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.util.ArrayList; 026import java.util.Collections; 027import java.util.Iterator; 028import java.util.List; 029import java.util.NoSuchElementException; 030 031import org.biojava.utils.ChangeEvent; 032import org.biojava.utils.ChangeSupport; 033import org.biojava.utils.ChangeVetoException; 034 035/** 036 * FeatureHolder which exposes all the features in a set 037 * of sub-FeatureHolders. This is provided primarily as 038 * a support class for ViewSequence. It may also be useful 039 * for other applications, such as simple distributed 040 * annotation systems. 041 * 042 * @author Thomas Down 043 * @author Matthew Pocock 044 * @see org.biojavax.bio.seq.RichFeatureRelationshipHolder 045 */ 046 047public class MergeFeatureHolder extends AbstractFeatureHolder 048 implements Serializable{ 049 050 private List featureHolders; 051 052 /** 053 * Create a new, empty, MergeFeatureHolder. 054 */ 055 056 public MergeFeatureHolder() { 057 featureHolders = new ArrayList(); 058 } 059 060 /** 061 * Create a populated MFH 062 */ 063 064 private MergeFeatureHolder(List m) { 065 featureHolders = m; 066 } 067 068 /** 069 * Add an extra FeatureHolder to the set of FeatureHolders which 070 * are merged. This method is provided for backward compatibility, 071 * and is equivalent to: 072 * 073 * <pre> 074 * mfh.addFeatureHolder(fh, FeatureFilter.all); 075 * </pre> 076 * 077 * <p> 078 * You should always use the two-arg version in preference if you 079 * are able to define the membership of a FeatureHolder. 080 * </p> 081 */ 082 083 public void addFeatureHolder(FeatureHolder fh) 084 throws ChangeVetoException 085 { 086 if(!hasListeners()) { 087 featureHolders.add(fh); 088 } else { 089 ChangeSupport changeSupport = getChangeSupport(FeatureHolder.FEATURES); 090 synchronized(changeSupport) { 091 ChangeEvent ce = new ChangeEvent(this, FeatureHolder.FEATURES); 092 changeSupport.firePreChangeEvent(ce); 093 featureHolders.add(fh); 094 changeSupport.firePostChangeEvent(ce); 095 } 096 } 097 } 098 099 /** 100 * Remove a FeatureHolder from the set of FeatureHolders which 101 * are merged. 102 */ 103 104 public void removeFeatureHolder(FeatureHolder fh) 105 throws ChangeVetoException 106 { 107 if(!hasListeners()) { 108 featureHolders.remove(fh); 109 } else { 110 ChangeSupport changeSupport = getChangeSupport(FeatureHolder.FEATURES); 111 synchronized(changeSupport) { 112 ChangeEvent ce = new ChangeEvent(this, FeatureHolder.FEATURES); 113 changeSupport.firePreChangeEvent(ce); 114 featureHolders.remove(fh); 115 changeSupport.firePostChangeEvent(ce); 116 } 117 } 118 } 119 120 public int countFeatures() { 121 int fc = 0; 122 for (Iterator i = featureHolders.iterator(); i.hasNext(); ) { 123 fc += ((FeatureHolder) i.next()).countFeatures(); 124 } 125 return fc; 126 } 127 128 public boolean containsFeature(Feature f) { 129 for (Iterator i = featureHolders.iterator(); i.hasNext(); ) { 130 FeatureHolder subFH = (FeatureHolder) i.next(); 131 FeatureFilter membership = subFH.getSchema(); 132 133 if (membership.accept(f)) { 134 if(subFH.containsFeature(f)) { 135 return true; 136 } 137 } 138 } 139 140 return false; 141 } 142 143 /** 144 * Iterate over all the features in all child FeatureHolders. 145 * The Iterator may throw ConcurrantModificationException if 146 * there is a change in the underlying collections during 147 * iteration. 148 */ 149 150 public Iterator features() { 151 return new MFHIterator(); 152 } 153 154 /** 155 * When applied to a MergeFeatureHolder, this filters each child 156 * FeatureHolder independently. 157 */ 158 159 public FeatureHolder filter(FeatureFilter ff, boolean recurse) { 160 List results = new ArrayList(); 161 for (Iterator fhi = featureHolders.iterator(); fhi.hasNext(); ) { 162 FeatureHolder fh = (FeatureHolder) fhi.next(); 163 FeatureFilter mf = fh.getSchema(); 164 if (recurse && !FilterUtils.areProperSubset(mf, FeatureFilter.leaf)) { 165 if (FilterUtils.areDisjoint(new FeatureFilter.Or(mf, new FeatureFilter.ByAncestor(mf)), 166 ff)) 167 { 168 continue; 169 } 170 } else { 171 if (FilterUtils.areDisjoint(mf, ff)) { 172 continue; 173 } 174 } 175 176 FeatureHolder filterResult = fh.filter(ff, recurse); 177 results.add(filterResult); 178 } 179 180 if (results.size() == 0) { 181 return FeatureHolder.EMPTY_FEATURE_HOLDER; 182 } else if (results.size() == 1) { 183 return (FeatureHolder) results.get(0); 184 } else { 185 return new MergeFeatureHolder(results); 186 } 187 } 188 189 public FeatureFilter getSchema() { 190 FeatureFilter[] filters = new FeatureFilter[featureHolders.size()]; 191 for (int i = 0; i < filters.length; ++i) { 192 filters[i] = ((FeatureHolder) featureHolders.get(i)).getSchema(); 193 } 194 return FilterUtils.or(filters); 195 } 196 197 private class MFHIterator implements Iterator { 198 private Iterator fhIterator; 199 private Iterator fIterator; 200 201 public MFHIterator() { 202 fhIterator = featureHolders.iterator(); 203 if (fhIterator.hasNext()) 204 fIterator = ((FeatureHolder) fhIterator.next()).features(); 205 else 206 fIterator = Collections.EMPTY_SET.iterator(); 207 } 208 209 public boolean hasNext() { 210 if (fIterator.hasNext()) 211 return true; 212 if (fhIterator.hasNext()) { 213 fIterator = ((FeatureHolder) fhIterator.next()).features(); 214 return hasNext(); 215 } 216 return false; 217 } 218 219 public Object next() { 220 if (fIterator.hasNext()) 221 return fIterator.next(); 222 if (fhIterator.hasNext()) { 223 fIterator = ((FeatureHolder) fhIterator.next()).features(); 224 return next(); 225 } 226 throw new NoSuchElementException(); 227 } 228 229 public void remove() { 230 throw new UnsupportedOperationException(); 231 } 232 } 233}