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.util.Iterator;
025
026import org.biojava.bio.BioException;
027import org.biojava.utils.AbstractChangeable;
028import org.biojava.utils.ChangeEvent;
029import org.biojava.utils.ChangeListener;
030import org.biojava.utils.ChangeSupport;
031import org.biojava.utils.ChangeType;
032import org.biojava.utils.ChangeVetoException;
033
034/**
035 * Wrapper implementation of FeatureHolder which calls a method
036 * to create a contained FeatureHolder on demand.  This is an
037 * abstract class and is normally used like:
038 *
039 * FeatureHolder fh = new LazyFeatureHolder() {
040 *         protected FeatureHolder createFeatureHolder() {
041 *             SimpleFeatureHolder features = new SimpleFeatureHolder();
042 *             // Create some features in here...
043 *             return features;
044 *         }
045 *     } ;
046 * </pre>
047 *
048 * @author Thomas Down
049 * @author Matthew Pocock
050 * @since 1.2
051 * @see org.biojavax.bio.seq.RichFeatureRelationshipHolder 
052 */
053
054public abstract class LazyFeatureHolder
055  extends
056    AbstractChangeable
057  implements
058    FeatureHolder
059{
060    private FeatureHolder featureHolder;
061    private Forwarder changeForwarder;
062    private FeatureFilter schema;
063    
064    /**
065     * Construct a LazyFeatureHolder with the schema of its contained featureholder
066     */
067    
068    protected LazyFeatureHolder() {
069    }
070    
071    /**
072     * Construct a LazyFeatureHolder with the specified schema
073     */
074     
075    protected LazyFeatureHolder(FeatureFilter schema) {
076        this.schema = schema;
077    }
078    
079    public FeatureFilter getSchema() {
080        if (schema == null) {
081            return getFeatureHolder().getSchema();
082        } else {
083            return schema;
084        }
085    }
086
087    protected abstract FeatureHolder createFeatureHolder();
088
089    protected void flushFeatures() {
090        featureHolder = null;
091    }
092
093    private FeatureHolder getFeatureHolder() {
094        if (featureHolder == null) {
095            featureHolder = createFeatureHolder();
096
097            if (!hasListeners()) {
098                changeForwarder = new Forwarder();
099                featureHolder.addChangeListener(changeForwarder, ChangeType.UNKNOWN);
100            }
101        }
102        return featureHolder;
103    }
104
105    public Iterator features() {
106        return getFeatureHolder().features();
107    }
108
109    public int countFeatures() {
110        return getFeatureHolder().countFeatures();
111    }
112
113    public FeatureHolder filter(FeatureFilter ff) {
114        // FIXME: we need to optimize here
115        return getFeatureHolder().filter(ff);
116    }
117    
118    public FeatureHolder filter(FeatureFilter ff, boolean recurse) {
119        if(!recurse) {
120          if(FilterUtils.areDisjoint(ff, getSchema())) {
121            return FeatureHolder.EMPTY_FEATURE_HOLDER;
122          } else if (FilterUtils.areProperSubset(ff, getSchema())) {
123            return this;
124          }
125        }
126
127        // FIXME: There are other optimisations we should be making
128        return getFeatureHolder().filter(ff, recurse);
129    }
130
131    public Feature createFeature(Feature.Template template) 
132        throws BioException, ChangeVetoException
133    {
134        return getFeatureHolder().createFeature(template);
135    }
136
137    public void removeFeature(Feature f) 
138        throws ChangeVetoException, BioException
139    {
140        getFeatureHolder().removeFeature(f);
141    }
142
143    public boolean containsFeature(Feature f) {
144        return getFeatureHolder().containsFeature(f);
145    }
146
147    protected ChangeSupport getChangeSupport(ChangeType ct) {
148      ChangeSupport changeSupport = super.getChangeSupport(ct);
149
150      if (featureHolder != null) {
151        changeForwarder = new Forwarder();
152        featureHolder.addChangeListener(changeForwarder, ChangeType.UNKNOWN);
153      }
154      
155      return changeSupport;
156    }
157        
158    private class Forwarder implements ChangeListener {
159        public void preChange(ChangeEvent cev)
160            throws ChangeVetoException
161        {
162            getChangeSupport(cev.getType()).firePreChangeEvent(cev);
163        }
164
165        public void postChange(ChangeEvent cev) {
166            getChangeSupport(cev.getType()).firePostChangeEvent(cev);
167        }
168    }
169}