001package org.biopax.paxtools.model;
002
003
004import org.apache.commons.logging.Log;
005import org.apache.commons.logging.LogFactory;
006import org.biopax.paxtools.impl.BioPAXElementImpl;
007import org.biopax.paxtools.impl.ModelImpl;
008
009import java.lang.reflect.Constructor;
010import java.lang.reflect.Method;
011import java.lang.reflect.Modifier;
012
013/**
014 * Abstract factory class for instantiating BioPAX classes. Different implementations of BioPAX model should also
015 * implement their own factory
016 */
017
018public abstract class BioPAXFactory
019{
020    private static final Log log = LogFactory.getLog(BioPAXFactory.class);
021        
022    private final Method setUriMethod;
023    
024    /**
025     * Protected Constructor without parameters.
026     */
027    protected BioPAXFactory() {
028        try {
029                        setUriMethod = BioPAXElementImpl.class.getDeclaredMethod("setRDFId", String.class);
030                        setUriMethod.setAccessible(true);
031                } catch (Throwable e) {
032                        throw new RuntimeException("BioPAXFactory Constructor failed", e);
033                } 
034        }
035
036    /**
037     * Gets the level.
038     * 
039     * @return the biopax level
040     */
041    public abstract BioPAXLevel getLevel();
042
043    
044    public BioPAXElement create(String localName, String uri)
045    {
046        Class<? extends BioPAXElement> type = getLevel().getInterfaceForName(localName);
047        return create(type, uri);
048    }
049
050    
051    /**
052     * Universal method that creates a new BioPAX object.
053     * (works with non-public, other package, implementations)
054         *
055         * @param <T> type
056         * @param aClass the class that corresponds to the BioPAX type
057         * @param uri absolute URI of the new BioPAX object
058         * @return new BioPAX object
059     */
060        public <T extends BioPAXElement> T create(Class<T> aClass, String uri) {
061                T bpe = null;
062
063                // create a new instance of the BioPAX type
064                try {
065                        Class<T> t = getImplClass(aClass);
066                        if(t != null) {
067                                Constructor<T> c = t.getDeclaredConstructor();
068                                c.setAccessible(true);
069                                bpe = (T) c.newInstance();
070                                setUriMethod.invoke(bpe, uri);
071                        } else {
072                                log.error("Could not find a class implementing " + aClass);
073                                return null;
074                        }
075                } catch (Exception e) {
076                        log.error("Could not instantiate BioPAX Type: " + aClass 
077                                        + "; URI: " + uri, e);
078                } 
079
080                return bpe;
081        }
082
083    
084        /**
085         * Maps a BioPAX type (model interface) to the
086         * full-qualified class name of an implementing class.
087         * BioPAX factories have to implement this method.
088         * 
089         * @param aClass BioPAX type (model interface)
090         * @return full class name
091         */
092    public abstract String mapClassName(Class<? extends BioPAXElement> aClass);
093
094    
095    /**
096     * Checks whether objects of a BioPAX model interface
097     * can be created (some types are not official BioPAX 
098     * types, abstract classes).
099     * 
100     * @param aClass BioPAX interface class
101     * @return whether this factory can create an instance of the type
102     */
103    public boolean canInstantiate(Class<? extends BioPAXElement> aClass) 
104    {
105        try {
106            String cname = mapClassName(aClass);
107                return !Modifier.isAbstract(Class.forName(cname).getModifiers());
108        } catch (ClassNotFoundException e)
109        {
110            return false;
111        } catch (Exception ex) {
112                log.error("Error in canInstantiate(" + aClass + ")", ex);
113                        return false;
114                }
115    }
116
117
118        /**
119         * Creates a new BioPAX model.
120         * @return BioPAX object model implementation
121         */
122        public Model createModel() {
123        return new ModelImpl(this);
124    }
125
126    
127    /**
128     * Get a concrete or abstract BioPAX type (not interface), 
129     * from org.biopax.paxtools.impl..*, i.e., one that has 
130     * persistence/search annotations, etc. This may be required for
131     * some DAO and web service controllers; it also returns such 
132     * abstract BioPAX "adapters" as XReferrableImpl, ProcessImpl, etc.
133     * 
134     * @param <T> BioPAX type/interface
135     * @param aModelInterfaceClass interface class for the BioPAX type
136     * @return concrete class that implements the BioPAX interface and can be created with this factory
137     */
138        public <T extends BioPAXElement> Class<T> getImplClass(
139                        Class<T> aModelInterfaceClass) 
140        {
141                Class<T> implClass = null;
142
143                if (aModelInterfaceClass.isInterface()) {
144                        String name = mapClassName(aModelInterfaceClass);
145                        try {
146                                implClass = (Class<T>) Class.forName(name);
147                        } catch (ClassNotFoundException e) {
148                                log.error(String.format("getImplClass(%s), %s" , aModelInterfaceClass, e));
149                        }
150                }
151
152                return implClass;
153        }
154
155}