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.io;
023
024import java.util.ArrayList;
025import java.util.Collection;
026import java.util.HashSet;
027import java.util.Iterator;
028import java.util.List;
029import java.util.Set;
030
031import org.biojava.bio.Annotation;
032import org.biojava.bio.BioError;
033import org.biojava.bio.BioException;
034import org.biojava.bio.SimpleAnnotation;
035import org.biojava.bio.SmallAnnotation;
036import org.biojava.bio.seq.Feature;
037import org.biojava.bio.seq.Sequence;
038import org.biojava.bio.symbol.Alphabet;
039import org.biojava.bio.symbol.IllegalAlphabetException;
040import org.biojava.bio.symbol.Symbol;
041import org.biojava.utils.ChangeVetoException;
042
043/**
044 * Basic SequenceBuilder implementation which accumulates all
045 * notified information.  Subclass this to implement specific
046 * Sequence implementations.
047 *
048 * More functionality is offered by the 
049 * {@link org.biojavax.bio.seq.io.SimpleRichSequenceBuilder SimpleRichSequenceBuilder}.
050 *
051 * @author Thomas Down
052 * @author David Huen (modified SimpleSequence to make this)
053 * @version 1.2 [newio proposal]
054 * 
055 */
056
057public abstract class SequenceBuilderBase implements SequenceBuilder {
058    public static Object ERROR_FEATURES_PROPERTY
059      = SequenceBuilderBase.class + "ERROR_FEATURES_PROPERTY";
060
061    //
062    // State
063    //
064
065    protected String name;
066    protected String uri;
067
068    // annotation on the sequence itself
069    protected Annotation annotation;
070
071    // features directly attached to sequence
072    private Set rootFeatures;
073
074    private List featureStack;
075
076    protected Sequence seq;
077
078    {
079        annotation = new SimpleAnnotation();
080        rootFeatures = new HashSet();
081        featureStack = new ArrayList();
082//      slBuilder = new ChunkedSymbolListBuilder();
083    }
084
085    //
086    // SeqIOListener
087    //
088
089    public void startSequence() {
090    }
091
092    public void endSequence() {
093    }
094
095    public void setName(String name) {
096        this.name = name;
097    }
098
099    public void setURI(String uri) {
100        this.uri = uri;
101    }
102
103    public abstract void addSymbols(Alphabet alpha, Symbol[] syms, int pos, int len)
104        throws IllegalAlphabetException;
105
106    /**
107     * Add an annotation-bundle entry to the sequence.  If the annotation key
108     * isn't currently defined, the value is added directly.  Otherwise:
109     *
110     * <ul>
111     * <li> If the current value implements the Collection interface,
112     *      the new value is added to that collection. </li>
113     * <li> Otherwise, the current value is replaced by a List object
114     *      containing the old value then the new value in that order. </li>
115     * </ul>
116     */
117    public void addSequenceProperty(Object key, Object value) {
118      addProperty(annotation, key, value);
119    }
120
121    public void startFeature(Feature.Template templ) {
122        TemplateWithChildren t2 = new TemplateWithChildren();
123        t2.template = templ;
124    if(templ.annotation == Annotation.EMPTY_ANNOTATION) {
125        templ.annotation = new SmallAnnotation();
126    }
127        int stackSize = featureStack.size();
128        if (stackSize == 0) {
129            rootFeatures.add(t2);
130        } else {
131            TemplateWithChildren parent = (TemplateWithChildren) featureStack.get(stackSize - 1);
132            if (parent.children == null)
133                parent.children = new HashSet();
134            parent.children.add(t2);
135        }
136        featureStack.add(t2);
137    }
138
139    /**
140     * Add an annotation-bundle entry to the feature. If the annotation key
141     * isn't currently defined, the value is added directly. Otherwise:
142     *
143     * <ul>
144     * <li> If the current value implements the Collection interface,
145     *      the new value is added to that collection. </li>
146     * <li> Otherwise, the current value is replaced by a List object
147     *      containing the old value then the new value in that order. </li>
148     * </ul>
149     */
150    public void addFeatureProperty(Object key, Object value)
151    throws ParseException {
152      try {
153        int stackSize = featureStack.size();
154
155        TemplateWithChildren top =
156        (TemplateWithChildren) featureStack.get(stackSize - 1);
157
158        addProperty(top.template.annotation, key, value);
159      } catch (IndexOutOfBoundsException ioobe) {
160        throw new ParseException(
161          ioobe,
162          "Attempted to add annotation to a feature when no startFeature " +
163          "had been invoked"
164        );
165      }
166    }
167
168    public void endFeature() {
169        if (featureStack.size() == 0)
170            throw new BioError("Assertion failed: Not within a feature");
171        featureStack.remove(featureStack.size() - 1);
172    }
173
174    public Sequence makeSequence()
175            throws BioException
176    {
177      //        SymbolList symbols = slBuilder.makeSymbolList();
178      //        Sequence seq = new SimpleSequence(symbols, uri, name, annotation);
179      try {
180        for (Iterator i = rootFeatures.iterator(); i.hasNext(); ) {
181          TemplateWithChildren twc = (TemplateWithChildren) i.next();
182          try {
183            Feature f = seq.createFeature(twc.template);
184            if (twc.children != null) {
185              makeChildFeatures(f, twc.children);
186            }
187          } catch (Exception e) {
188            // fixme: we should do something more sensible with this error
189            e.printStackTrace();
190            Set errFeatures;
191            Annotation ann = seq.getAnnotation();
192            if(ann.containsProperty(ERROR_FEATURES_PROPERTY)) {
193              errFeatures = (Set) ann.getProperty(ERROR_FEATURES_PROPERTY);
194            } else {
195              ann.setProperty(
196                ERROR_FEATURES_PROPERTY,
197                errFeatures = new HashSet()
198              );
199            }
200            errFeatures.add(twc);
201          }
202        }
203      } catch (Exception ex) {
204        throw new BioError("Couldn't create feature",ex);
205      }
206      return seq;
207    }
208
209    private void makeChildFeatures(Feature parent, Set children)
210        throws Exception
211    {
212        for (Iterator i = children.iterator(); i.hasNext(); ) {
213            TemplateWithChildren twc = (TemplateWithChildren) i.next();
214            Feature f = parent.createFeature(twc.template);
215            if (twc.children != null) {
216                makeChildFeatures(f, twc.children);
217            }
218        }
219    }
220
221    protected void addProperty(Annotation ann, Object key, Object value) {
222        if (value == null)
223            return;
224
225        Object oldValue = null;
226        Object newValue = value;
227
228        if(ann.containsProperty(key)) {
229            oldValue = ann.getProperty(key);
230        }
231
232        if (oldValue != null) {
233            if (oldValue instanceof Collection) {
234                ((Collection) oldValue).add(newValue);
235                newValue = oldValue;
236            } else {
237                List nvList = new ArrayList();
238                nvList.add(oldValue);
239                nvList.add(newValue);
240                newValue = nvList;
241            }
242        }
243
244        try {
245            ann.setProperty(key, newValue);
246        } catch (ChangeVetoException ex) {
247            throw new BioError("Annotation should be modifiable",ex);
248        }
249    }
250
251    private static class TemplateWithChildren {
252        Feature.Template template;
253        Set children;
254    }
255}