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}