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.program.tagvalue;
023
024import java.util.ArrayList;
025import java.util.List;
026
027import org.biojava.bio.Annotation;
028import org.biojava.bio.AnnotationType;
029import org.biojava.bio.CollectionConstraint;
030import org.biojava.bio.PropertyConstraint;
031import org.biojava.bio.SmallAnnotation;
032import org.biojava.utils.AssertionFailure;
033import org.biojava.utils.ChangeVetoException;
034
035/**
036 * <p>
037 * Builds an Annotation tree from TagValue events using an AnnotationType to
038 * work out which fields are of what type.
039 * </p>
040 *
041 * @since 1.2
042 * @author Matthew Pocock
043 */
044public class AnnotationBuilder
045  implements TagValueListener
046{
047  private List annotationStack;
048  private AnnotationType type;
049  private Annotation last;
050  
051  /**
052   * <p>
053   * Make a new AnnotationBuilder that will build Annotation instances of a
054   * given type.
055   * </p>
056   *
057   * <p>
058   * The type is used to provide appropriate accessors for properties. As tag
059   * -value events stream through this TagValueListener, they will be matched
060   * against the properties of the annotation type. As sub-trees of events are
061   * pushed, child annotation bundles will be pushed into the appropriate
062   * properties. If any of the tag-value events are of a type that are not
063   * accepted by the annotation type, a ClassCastException will be thrown.
064   * </p>
065   *
066   * @param type  the AnnotationType stating what will be built and how
067   * @throws ClassCastException if any of the tag-value events are of
068   *         inappropriate type
069   */
070  public AnnotationBuilder(AnnotationType type) {
071    this.type = type;
072    this.annotationStack = new ArrayList();
073  }
074  
075  /**
076   * <p>
077   * Get the last complete annotation built.
078   * </p>
079   *
080   * <p>
081   * The value of this is undefined before the first annotation has been built
082   * and during the parsing of an event stream.
083   * </p>
084   *
085   * @return the Annotation that was last built
086   */
087  public Annotation getLast() {
088    return last;
089  }
090  
091  public void startRecord() {
092    Frame top = new Frame();
093    top.annotation = new SmallAnnotation();
094    if(annotationStack.isEmpty()) {
095      top.type = this.type;
096    } else {
097      Frame old = peek(annotationStack);
098      CollectionConstraint cc = old.type.getConstraint(old.tag);
099      PropertyConstraint pc = null;
100      if (cc instanceof CollectionConstraint.AllValuesIn) {
101          pc = ((CollectionConstraint.AllValuesIn) cc).getPropertyConstraint();
102      }
103      if(pc instanceof PropertyConstraint.ByAnnotationType) {
104        PropertyConstraint.ByAnnotationType pcat = (PropertyConstraint.ByAnnotationType) pc;
105        top.type = pcat.getAnnotationType();
106      } else {
107        top.type = AnnotationType.ANY;
108      }
109    }
110    push(annotationStack, top);
111  }
112  
113  public void endRecord() {
114    Frame top = pop(annotationStack);
115    last = top.annotation;
116    if(!annotationStack.isEmpty()) {
117      try {
118        Frame old = peek(annotationStack);
119        old.type.setProperty(old.annotation, old.tag, last);
120      } catch (ChangeVetoException cve) {
121        throw new AssertionFailure(cve);
122      }
123    }
124  }
125  
126  public void startTag(Object tag) {
127    peek(annotationStack).tag = tag;
128  }
129  
130  public void value(TagValueContext ctxt, Object value) {
131    try {
132      Frame top = peek(annotationStack);
133      top.type.setProperty(top.annotation, top.tag, value);
134    } catch (ChangeVetoException cve) {
135      throw new AssertionFailure(cve);
136    }
137  }
138  
139  public void endTag() {}
140  
141  private void push(List list, Frame frame) {
142    list.add(frame);
143  }
144  
145  private Frame peek(List list) {
146    return (Frame) list.get(list.size() - 1);
147  }
148  
149  private Frame pop(List list) {
150    return (Frame) list.remove(list.size() - 1);
151  }
152  
153  private static class Frame {
154    public AnnotationType type;
155    public Annotation annotation;
156    public Object tag;
157  }
158}