001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021
022package org.biojavax;
023import java.util.Arrays;
024import java.util.HashMap;
025import java.util.Iterator;
026import java.util.LinkedHashMap;
027import java.util.List;
028import java.util.Map;
029
030
031import org.biojavax.bio.seq.DummyRichSequenceHandler;
032import org.biojavax.bio.seq.PositionResolver;
033import org.biojavax.bio.seq.RichSequenceHandler;
034import org.biojavax.bio.seq.PositionResolver.AverageResolver;
035import org.biojavax.ontology.ComparableOntology;
036import org.biojavax.ontology.SimpleComparableOntology;
037
038
039/**
040 * Runs a service that builds rich objects, and provides some default values
041 * for things like default ontology, default namespace, etc.
042 * @author Richard Holland
043 * @since 1.5
044 */
045public class RichObjectFactory {
046    
047    private static RichObjectBuilder builder = new SimpleRichObjectBuilder();
048    
049    private static String defaultOntologyName = "biojavax";
050    private static String defaultNamespaceName = "lcl";
051    private static PositionResolver defaultPositionResolver = new AverageResolver();
052    private static CrossReferenceResolver defaultCrossRefResolver = new DummyCrossReferenceResolver();
053    private static RichSequenceHandler defaultRichSequenceHandler = new DummyRichSequenceHandler();
054    
055    // the LRU cache - keys are classes, entries are maps of param sets to objects
056    private static int defaultLRUcacheSize = 20;
057    private static Map LRUcacheSizes = new HashMap();
058    private static Map cache = new HashMap();
059    private final static Map applicationClassMap = new HashMap();
060    
061    // Constructor is private as this is all static.
062    private RichObjectFactory() {}
063    
064    /**
065     * Sets the builder to use when instantiating new Rich objects. The basic,
066     * default, one is a SimpleRichObjectBuilder, which just calls the constructor.
067     * Another useful one is BioSQLRichObjectBuilder, which attempts to load
068     * objects from the database. The latter is required if you are working with
069     * Hibernate as it will not work without it.
070     * @param b the builder to use.
071     * @see SimpleRichObjectBuilder
072     * @see org.biojavax.bio.db.biosql.BioSQLRichObjectBuilder
073     */
074    public static synchronized void setRichObjectBuilder(RichObjectBuilder b) {
075        builder = b;
076    }
077    
078    /**
079     * Delegates to a RichObjectBuilder to construct/retrieve the object, and returns it.
080     * To increase efficiency, it keeps a list of recently requested objects. If it
081     * receives further requests for the same object, it returns them from the cache. The
082     * size of the cache can be altered using setLRUCacheSize(). The default cache size is 20
083     * objects for each type of class requested.
084     * @param clazz the class to build
085     * @param params the parameters to pass to the class' constructor
086     * @return the instantiated object
087     */
088    public static synchronized Object getObject(final Class clazz, Object[] params) {
089        List paramsList = Arrays.asList(params);
090        final Class applicationClass = getApplicationClass(clazz);
091        if (!cache.containsKey(applicationClass)) {
092            int LRUcacheSize = defaultLRUcacheSize;
093            if (LRUcacheSizes.containsKey(applicationClass)) LRUcacheSize = ((Integer)LRUcacheSizes.get(applicationClass)).intValue();
094            else LRUcacheSizes.put(applicationClass,new Integer(LRUcacheSize));
095            cache.put(applicationClass, new LinkedHashMap(LRUcacheSize, 0.75f, true) {
096                protected boolean removeEldestEntry(Map.Entry eldest) {
097                    return this.size() > ((Integer)LRUcacheSizes.get(applicationClass)).intValue();
098                }
099            });
100        }
101        Map m = (Map)cache.get(applicationClass);
102       if (!m.containsKey(paramsList)) {
103           m.put(paramsList,builder.buildObject(applicationClass, paramsList));
104       }
105        return m.get(paramsList);
106    }
107    
108    private final static Map getApplicationClassMap() {
109        return applicationClassMap;
110    }
111    
112        /**
113         * Allow application to override the default biojava class created in getObject - subclass restriction is checked in the builder.
114         * @param theBiojavaClass one of the well-known builder classes: SimpleNamespace, SimpleComparableOntology, SimpleNCBITaxon, SimpleCrossRef, or SimpleDocRef
115         * @param theApplicationClass - a subclass of theBiojavaClass
116         */
117    public final static void setApplicationClass(final Class theBiojavaClass, final Class theApplicationClass) {
118        if (theApplicationClass==null || theBiojavaClass.isAssignableFrom(theApplicationClass) == false) throw new IllegalArgumentException("RichObjectFactory.setApplicationClass-theApplicationClass: <"+theApplicationClass+"> must be assignable to the biojava class: <"+theBiojavaClass+">");
119        getApplicationClassMap().put(theBiojavaClass, theApplicationClass);
120    }
121    
122    private final static Class getApplicationClass(final Class theBiojavaClass) {
123        final Class applicationClass = (Class) getApplicationClassMap().get(theBiojavaClass);
124        return applicationClass!=null?applicationClass:theBiojavaClass;
125    }
126    
127    /**
128     * Removes all objects from the LRU cache.
129     */
130    public static synchronized void clearLRUCache(){
131        cache.clear();
132    }
133    
134    /**
135     * Removes all objects of the specified class from the LRU cache.
136     * @param clazz The class of the objects to remove.
137     */
138    public static synchronized void clearLRUCache(Class clazz){
139        cache.remove(clazz);
140    }
141    
142    /**
143     * Sets the size of the LRU cache. This is the size per class of object requested, so
144     * if you set it to 20 and request 3 different types of object, you will get 20*3=60
145     * entries in the cache. The default cache size is 20. Setting this value will undo 
146     * any previous changes made using the setLRUCacheSize(Class,int) method below, but will not
147     * override future ones.
148     * @param size the size of the cache.
149     */
150    public static void setLRUCacheSize(int size) {
151        defaultLRUcacheSize = size;
152        for (Iterator i = LRUcacheSizes.keySet().iterator(); i.hasNext(); ) LRUcacheSizes.put(i.next(), new Integer(size));
153    }
154    
155    /**
156     * Sets the size of the LRU cache. This is the size for the specific class of object 
157     * requested, so does not affect the size of caches of other objects.
158     * If this method is not called, then the cache size defaults to 20, or whatever value
159     * was passed to setLRUCacheSize(int) above.
160     * @param size the size of the cache.
161     */
162    public static void setLRUCacheSize(Class clazz, int size) {
163        LRUcacheSizes.put(clazz,new Integer(size));
164    }
165    
166    /**
167     * Sets the default namespace name to use when loading sequences. Defaults to "lcl".
168     * @param name the namespace name to use.
169     */
170    public static void setDefaultNamespaceName(String name) { defaultNamespaceName = name; }
171    
172    /**
173     * Sets the default ontology name to use when loading sequences. Defaults to "biojavax".
174     * @param name the ontology name to use.
175     */
176    public static void setDefaultOntologyName(String name) { defaultOntologyName = name; }
177    
178    /**
179     * Sets the default position resolver to use when creating new rich feature locations.
180     * Defaults to the AverageResolver
181     * @param pr the position resolver to use.
182     * @see org.biojavax.bio.seq.PositionResolver
183     * @see org.biojavax.bio.seq.PositionResolver.AverageResolver
184     * @see org.biojavax.bio.seq.RichLocation
185     */
186    public static void setDefaultPositionResolver(PositionResolver pr) { defaultPositionResolver = pr; }
187    
188    /**
189     * Sets the default crossref resolver to use when resolving remote entries.
190     * Defaults to the DummyCrossReferenceResolver.
191     * @param crr the resolver to use.
192     * @see org.biojavax.CrossReferenceResolver
193     * @see org.biojavax.DummyCrossReferenceResolver
194     */
195    public static void setDefaultCrossReferenceResolver(CrossReferenceResolver crr) { defaultCrossRefResolver = crr; }
196    
197    /**
198     * Sets the default sequence handler to use when performing sequence manipulation.
199     * Defaults to the DummyRichSequenceHandler.
200     * @param rsh the resolver to use.
201     * @see org.biojavax.bio.seq.RichSequenceHandler
202     * @see org.biojavax.bio.seq.DummyRichSequenceHandler
203     */
204    public static void setDefaultRichSequenceHandler(RichSequenceHandler rsh) { defaultRichSequenceHandler = rsh; }
205    
206    /**
207     * Returns the default namespace object. Defaults to "lcl".
208     * @return the default namespace.
209     */
210    public static Namespace getDefaultNamespace() {
211        return (Namespace)getObject(SimpleNamespace.class, new Object[]{defaultNamespaceName});
212    }
213    
214    /**
215     * Returns the default ontology object. Defaults to "biojavax".
216     * @return the default ontology.
217     */
218    public static ComparableOntology getDefaultOntology() {
219        return (ComparableOntology)getObject(SimpleComparableOntology.class, new Object[]{defaultOntologyName});
220    }
221    
222    /**
223     * Returns the default position resolver object. Defaults to PositionResolver.AverageResolver
224     * @return the default position resolver.
225     * @see org.biojavax.bio.seq.PositionResolver.AverageResolver
226     */
227    public static PositionResolver getDefaultPositionResolver() { return defaultPositionResolver; }
228    
229    /**
230     * Returns the default cross ref resolver object. Defaults to DummyCrossReferenceResolver
231     * @return the default resolver.
232     * @see org.biojavax.DummyCrossReferenceResolver
233     */
234    public static CrossReferenceResolver getDefaultCrossReferenceResolver() { return defaultCrossRefResolver; }
235    
236    /**
237     * Returns the default sequence resolver object. Defaults to DummyRichSequenceHandler.
238     * @return the default resolver.
239     * @see org.biojavax.bio.seq.DummyRichSequenceHandler
240     */
241    public static RichSequenceHandler getDefaultRichSequenceHandler() { return defaultRichSequenceHandler; }
242        
243// commenting out for the moment, since it prevents core from compiling.
244// TODO: move to BioSql module    
245//    /** 
246//     * A utility method that configures the RichObjectFactory for use with a Hibernate session.
247//     * @param session an object containing a Hibernate session.
248//     */
249//    public static void connectToBioSQL(Object session) {
250//      clearLRUCache();
251//        RichObjectFactory.setRichObjectBuilder(new BioSQLRichObjectBuilder(session));
252//        RichObjectFactory.setDefaultCrossReferenceResolver(new BioSQLCrossReferenceResolver(session));      
253//        RichObjectFactory.setDefaultRichSequenceHandler(new BioSQLRichSequenceHandler(session));        
254//    }
255}