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.biojava.bio.seq;
023
024import java.io.Serializable;
025import java.lang.reflect.Constructor;
026import java.lang.reflect.InvocationTargetException;
027import java.util.ArrayList;
028import java.util.Iterator;
029import java.util.List;
030
031import org.biojava.bio.BioException;
032
033/**
034 * FeatureRealizer which uses a lookup table to map template classes
035 * to implementations.  Optionally, this implementation can fall back
036 * on another FeatureRealizer if it fails.
037 *
038 * <p>
039 * When searching for a Feature implementation to match a specific
040 * Feature.Template class, the following search order is used:
041 * <ol><li>Mappings added to this SimpleFeatureRealizer, in reverse
042 * order of addition.</li>
043 * <li>Any mappings known to the fallback realizer, if one is installed.</li>
044 * <li>If no mapping can be found, a BioException is thrown.</li></ol>
045 * </p>
046 *
047 * @author Thomas Down
048 */
049
050public class SimpleFeatureRealizer implements FeatureRealizer, Serializable {
051    private List templateToImpl;
052    private FeatureRealizer fallBack;
053
054    {
055        templateToImpl = new ArrayList();
056    }
057
058    public SimpleFeatureRealizer() {
059        fallBack = null;
060    }
061
062    public SimpleFeatureRealizer(FeatureRealizer fallBack) {
063        this.fallBack = fallBack;
064    }
065
066    /**
067     * Install a new mapping from a class of Feature.Template to
068     * a class of Feature implementations.  The implementation
069     * class MUST have a public constructor of the form
070     * (Sequence, FeatureHolder, Feature.Template).
071     *
072     * <p>
073     * A newly added implementation takes precendence over
074     * any existing implementations if a template can be realized
075     * by more than one implementation.
076     * </p>
077     *
078     * @param template The class of templates to implement.
079     * @param impl A class of Feature which can be used to implement these templates.
080     */
081
082    public void addImplementation(Class template, Class impl)
083        throws BioException
084    {
085        TemplateImpl ti = new TemplateImpl(template, impl);
086        templateToImpl.add(0, ti);
087    }
088
089    public Feature realizeFeature(Sequence seq,
090                                  FeatureHolder parent,
091                                  Feature.Template temp)
092        throws BioException
093    {
094        for (Iterator i = templateToImpl.iterator(); i.hasNext(); ) {
095            TemplateImpl ti = (TemplateImpl) i.next();
096            if (ti.accept(temp))
097                return ti.realize(seq, parent, temp);
098        }
099
100        if (fallBack != null)
101            return fallBack.realizeFeature(seq, parent, temp);
102
103        throw new BioException("Couldn't find realized implementation for template of class " +
104                               temp.getClass().getName());
105    }
106
107    private static class TemplateImpl {
108        private Class template;
109        private Constructor cons;
110
111        private TemplateImpl(Class template, Class impl)
112            throws BioException
113        {
114            Class[] signature = new Class[3];
115            signature[0] = Sequence.class;
116            signature[1] = FeatureHolder.class;
117            signature[2] = template;
118            this.template = template;
119
120            try {
121                this.cons = impl.getConstructor(signature);
122            } catch (NoSuchMethodException ex) {
123                throw new BioException("Class " + impl.getName() + " does not have suitable constructor", ex);
124            }
125        }
126
127        public boolean accept(Feature.Template temp) {
128            return template.isInstance(temp);
129        }
130
131        public Feature realize(Sequence seq,
132                               FeatureHolder parent,
133                               Feature.Template temp)
134            throws BioException
135        {
136            Object[] consArgs = new Object[3];
137            consArgs[0] = seq;
138            consArgs[1] = parent;
139            consArgs[2] = temp;
140            try {
141                return (Feature) cons.newInstance(consArgs);
142            } catch (Exception ex) {
143                Throwable t = ex;
144                if(ex instanceof InvocationTargetException) {
145                    t = ((InvocationTargetException) ex).getTargetException();
146                }
147                throw new BioException("Couldn't realize feature", t);
148            }
149        }
150    }
151}