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.impl;
023
024import java.util.Iterator;
025
026import org.biojava.bio.BioException;
027import org.biojava.bio.seq.Feature;
028import org.biojava.bio.seq.FeatureFilter;
029import org.biojava.bio.seq.FeatureHolder;
030import org.biojava.bio.seq.FilterUtils;
031import org.biojava.utils.ChangeEvent;
032import org.biojava.utils.ChangeListener;
033import org.biojava.utils.ChangeSupport;
034import org.biojava.utils.ChangeType;
035import org.biojava.utils.ChangeVetoException;
036
037/**
038 * FeatureHolder which lazily applies a specified filter to another FeatureHolder.
039 * This means that when you use a second filter to query the LazyFilterFeatureHolder,
040 * the underlying holder receives a single <code>filter</code> request with the
041 * two queries ANDed together.
042 *
043 * @author Thomas Down
044 * @since 1.3
045 */
046
047public class LazyFilterFeatureHolder implements FeatureHolder {
048        private FeatureHolder featureHolder;
049        private FeatureFilter featureFilter;
050        private transient ChangeSupport changeSupport;
051
052        public LazyFilterFeatureHolder(FeatureHolder fh,
053                        FeatureFilter ff)
054        {
055                this.featureHolder = fh;
056                this.featureFilter = ff;
057        }
058
059        public Iterator features() {
060                return featureHolder.filter(featureFilter, false).features();
061        }
062
063        public int countFeatures() {
064                return featureHolder.filter(featureFilter, false).countFeatures();
065        }
066
067        public boolean containsFeature(Feature f) {
068                if (featureFilter.accept(f)) {
069                        return featureHolder.containsFeature(f);
070                } else {
071                        return false;
072                }
073        }
074
075        public FeatureHolder filter(FeatureFilter ff) {
076                if (FilterUtils.areDisjoint(ff, featureFilter)) {
077                        return FeatureHolder.EMPTY_FEATURE_HOLDER;
078                }
079                return featureHolder.filter(new FeatureFilter.And(ff, featureFilter));
080        }
081
082        public FeatureHolder filter(FeatureFilter ff, boolean recurse) {
083                if (FilterUtils.areDisjoint(ff, featureFilter)) {
084                        return FeatureHolder.EMPTY_FEATURE_HOLDER;
085                }
086                return featureHolder.filter(new FeatureFilter.And(ff, featureFilter), recurse);
087        }
088
089
090        public Feature createFeature(Feature.Template temp)
091        throws ChangeVetoException, BioException
092        {
093
094                Feature f= null;
095
096                synchronized (featureHolder) {
097                        f = featureHolder.createFeature(temp);  
098                }
099
100                return f;
101        }
102
103        public void removeFeature(Feature f)
104        throws ChangeVetoException, BioException
105        {
106                synchronized (featureHolder) {
107                        featureHolder.removeFeature(f);
108                }
109
110        }
111
112
113        protected boolean hasListeners() {
114                return changeSupport != null;
115        }
116
117        protected ChangeSupport getChangeSupport() {
118                if(changeSupport != null) {
119                        return changeSupport;
120                }
121
122                synchronized(this) {
123                        if(changeSupport == null) {
124                                changeSupport = new ChangeSupport();
125                        }
126                        synchronized (featureHolder) {
127                                featureHolder.addChangeListener(new LFFHChangeForwarder(), ChangeType.UNKNOWN);         
128                        }
129
130                }
131
132                return changeSupport;
133        }
134
135        private class LFFHChangeForwarder implements ChangeListener {
136                public void preChange(ChangeEvent cev)
137                throws ChangeVetoException
138                {
139                        ChangeEvent fcev = getForwardedEvent(cev);
140                        if (fcev != null) {
141                                ChangeSupport cs = getChangeSupport();
142                                synchronized(cs){
143                                        cs.firePreChangeEvent(fcev);
144                                }
145                        }
146                }
147
148                public void postChange(ChangeEvent cev)
149                {
150                        ChangeEvent fcev = getForwardedEvent(cev);
151                        if (fcev != null) {
152                                ChangeSupport cs = getChangeSupport();
153                                synchronized(cs){
154                                        cs.firePostChangeEvent(fcev);
155                                }
156                        }
157                }
158        }
159
160        /**
161         * Only forward events concerning features which are either accepted by
162         * our filter, or are parents of a feature which is.
163         *
164         * In future this maybe ought to look further down the event chain
165         * to reject forwarded events from uninterested features.
166         */
167
168        private ChangeEvent getForwardedEvent(ChangeEvent cev) {
169                Object change = cev.getChange();
170                if (! (change instanceof Feature)) {
171                        change = null;
172                }
173                Object previous = cev.getPrevious();
174                if (! (previous instanceof Feature)) {
175                        previous = null;
176                }
177                boolean forward = false;
178                if (change == null && previous == null) {
179                        forward = true;
180                } else {
181                        forward = isInterestingFeature((Feature) previous) || 
182                        isInterestingFeature((Feature) change);
183                }
184                if (forward) {
185                        return new ChangeEvent(this,
186                                        cev.getType(),
187                                        cev.getChange(),
188                                        cev.getPrevious(),
189                                        cev);
190                } else {
191                        return null;
192                }
193        }
194
195        private boolean isInterestingFeature(Feature f) {
196                if (f == null) {
197                        return false;
198                } else if (featureFilter.accept(f)) {
199                        return true;
200                } else {
201                        return f.filter(featureFilter).countFeatures() > 0;
202                }
203        }
204
205        public final void addChangeListener(ChangeListener cl) {
206                addChangeListener(cl, ChangeType.UNKNOWN);
207        }
208
209        public final void addChangeListener(ChangeListener cl, ChangeType ct) {
210                if (!isUnchanging(ct)) {
211                        ChangeSupport cs = getChangeSupport();
212                        cs.addChangeListener(cl, ct);
213                }
214        }
215
216        public final void removeChangeListener(ChangeListener cl) {
217                removeChangeListener(cl, ChangeType.UNKNOWN);
218        }
219
220        public final void removeChangeListener(ChangeListener cl, ChangeType ct) {
221                if(hasListeners()) {
222                        ChangeSupport cs = getChangeSupport();
223                        cs.removeChangeListener(cl, ct);
224                }
225        }
226
227        public boolean isUnchanging(ChangeType ct) {
228                return featureHolder.isUnchanging(ct);
229        }
230
231        public FeatureFilter getSchema() {
232                return new FeatureFilter.And(featureFilter,
233                                new FeatureFilter.Or(featureHolder.getSchema(), 
234                                                new FeatureFilter.ByAncestor(featureHolder.getSchema())));
235        }
236}