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.game; 023 024import java.util.ArrayList; 025import java.util.List; 026import java.util.ListIterator; 027 028import org.biojava.bio.Annotation; 029import org.biojava.bio.SmallAnnotation; 030import org.biojava.bio.seq.Feature; 031import org.biojava.bio.seq.io.ParseException; 032import org.biojava.bio.seq.io.SeqIOListener; 033import org.biojava.utils.stax.DelegationManager; 034import org.biojava.utils.stax.StAXContentHandler; 035import org.biojava.utils.stax.StAXContentHandlerBase; 036import org.xml.sax.Attributes; 037import org.xml.sax.SAXException; 038 039/** 040 * StAX handler shamelessly ripped off from Thomas Down's 041 * XFFFeatureSetHandler. It was modified for greater 042 * generality. 043 * 044 * <strong>NOTE</strong> This class is not thread-safe -- it 045 * must only be used for one parse at any time. 046 * 047 * @author Thomas Down 048 * @author David Huen 049 * 050 * @since 1.8 051 */ 052 053public class StAXFeatureHandler extends StAXContentHandlerBase 054{ 055 // class variables 056 private boolean setOnceFired = false; 057 private String myLocalName; 058 private boolean hasCallback = false; 059 private List handlers; 060 private boolean startFired=false; 061 private boolean endFired=false; 062 private boolean inFeature=false; // have we been here before? 063 064 // class variables for use by nested elements 065 protected Feature.Template featureTemplate; 066 protected SeqIOListener featureListener; 067 protected int startLoc; 068 protected int endLoc; // needed because some XML don't give start and stops as attributes. 069/** 070 * this is the stack of handler objects for the current feature. 071 * The base value is the FeatureHandler itself. 072 * your feature and property handlers place and remove themselves 073 * from this stack. 074 * 075 * the purpose of all this is to implement context sensitivty for 076 * property handlers translucently. Property handlers can pop the 077 * stack for other handlers that implement interfaces that process 078 * that element. This way the context code is within the object that 079 * defines that context rather than in a child property handler. 080 */ 081 protected List callbackStack; 082 protected int stackLevel; 083 084 { 085 handlers = new ArrayList(); 086 callbackStack = new ArrayList(); 087 } 088 089 // there should be a factory method here to make this class 090 091/** 092 * Sets the element name that the class responds to. 093 */ 094 public void setHandlerCharacteristics(String localName, boolean hasCallback) { 095 if (!setOnceFired) { 096 myLocalName = localName; 097 this.hasCallback = hasCallback; 098 setOnceFired = true; 099 } 100 else 101 System.err.println("setHandlerChracteristics called twice on same handler."); 102 } 103 104 public void setFeatureListener(SeqIOListener siol) { 105 featureListener = siol; 106 } 107 108 // Class to implement bindings 109 class Binding { 110 final ElementRecognizer recognizer; 111 final StAXHandlerFactory handlerFactory; 112 Binding(ElementRecognizer er, StAXHandlerFactory hf) 113 { 114 recognizer = er; 115 handlerFactory = hf; 116 } 117 } 118 119 // method to add a handler 120 // we do not distinguish whither it is a feature or property 121 // handler. The factory method creates the right type subclassed 122 // from the correct type of handler 123 protected void addHandler( 124 ElementRecognizer rec, 125 StAXHandlerFactory handler) 126 { 127 handlers.add(new Binding(rec, handler)); 128 } 129 130/** 131 * generates a very basic Template for the feature with 132 * SmallAnnotation in the annotation field. 133 * <p> 134 * Override if you wish a more specialised Template. 135 * 136 */ 137 protected Feature.Template createTemplate() { 138 Feature.Template ft = new Feature.Template(); 139 ft.annotation = new SmallAnnotation(); 140 return ft; 141 } 142 143/** 144 * return current stack level. Remember that the 145 * stack level is incremented/decremented AFTER 146 * the push()/pop() calls and superclass 147 * startElement()/StopElement calls. 148 */ 149 protected int getLevel() { 150 return stackLevel; 151 } 152 153/** 154 * return iterator to callbackStack 155 */ 156 protected ListIterator getHandlerStackIterator(int level) { 157 return callbackStack.listIterator(level); 158 } 159 160/** 161 * Push StAXContentHandler object onto stack 162 */ 163 protected void push(StAXContentHandler handler) { 164 // push handler 165 callbackStack.add(handler); 166 167 // increment pointer 168 stackLevel++; 169 } 170 171/** 172 * pop a StAXContentHandler off the stack. 173 */ 174 protected void pop() { 175 // decrement pointer 176 stackLevel--; 177 178 // pop handler 179 callbackStack.remove(stackLevel); 180 } 181 182/** 183 * Return current feature listener 184 */ 185 public SeqIOListener getFeatureListener() { 186 return featureListener; 187 } 188 189/** 190 * Fire the startFeature event. 191 */ 192 private void fireStartFeature() 193 throws ParseException 194 { 195 if (startFired) 196 throw new ParseException("startFeature event has already been fired"); 197 198 if (featureTemplate == null) 199 featureTemplate = createTemplate(); 200 201 if (featureTemplate.annotation == null) 202 featureTemplate.annotation = Annotation.EMPTY_ANNOTATION; 203 204 featureListener.startFeature(featureTemplate); 205 startFired = true; 206 } 207 208/** 209 * Fire the endFeature event. 210 */ 211 private void fireEndFeature() 212 throws ParseException 213 { 214 if (!startFired) 215 throw new ParseException("startFeature has not yet been fired!"); 216 217 if (endFired) 218 throw new ParseException("endFeature event has already been fired!"); 219 220 featureListener.endFeature(); 221 endFired = true; 222 } 223 224/** 225 * Element-specific handler. 226 * Subclass this to do something useful! 227 */ 228 public void startElementHandler( 229 String nsURI, 230 String localName, 231 String qName, 232 Attributes attrs) 233 throws SAXException 234 { 235 } 236 237 238/** 239 * Handles basic entry processing for all feature handlers. 240 */ 241 public void startElement( 242 String nsURI, 243 String localName, 244 String qName, 245 Attributes attrs, 246 DelegationManager dm) 247 throws SAXException 248 { 249// System.out.println("StaxFeaturehandler.startElement starting. localName: " + localName); 250 // sanity check 251 if (!setOnceFired) 252 throw new SAXException("StAXFeaturehandler not initialised before use!"); 253 254 // find out if this element is really for me. 255 // perform delegation 256 for (int i = handlers.size() - 1; i >= 0; --i) { 257 Binding b = (Binding) handlers.get(i); 258 if (b.recognizer.filterStartElement(nsURI, localName, qName, attrs)) { 259// System.out.println("StaxFeaturehandler.startElement delegating."); 260 dm.delegate(b.handlerFactory.getHandler(this)); 261 return; 262 } 263 } 264 265 // I don't have a delegate for it but it might be a stray... 266 if (!(myLocalName.equals(localName)) ) { 267 //this one's not for me! 268 return; 269 } 270 271 // this one's mine. 272 // initialise if this is the first time thru' 273 if (!inFeature) { 274 inFeature = true; 275 276 if (hasCallback) { 277 // push ourselves onto the stack at bedrock. 278 // Except in the event of great stupidity, you will 279 // never call this method twice in an instance since 280 // each feature handler starts a new context. 281// System.out.println("StAXFeatureHandler startElement called, localName, level: " + localName + " " + stackLevel); 282 if (stackLevel == 0) push(this); 283 } 284 285 // indicate start of feature to listener 286 try { 287 if (!startFired) fireStartFeature(); 288 } 289 catch (ParseException pe) { 290 throw new SAXException("ParseException thrown in user code"); 291 } 292 } 293 294 // call the element specific handler now. 295 startElementHandler(nsURI, localName, qName, attrs); 296 297 } 298 299/** 300 * Element specific exit handler 301 * Subclass to do anything useful. 302 */ 303 public void endElementHandler( 304 String nsURI, 305 String localName, 306 String qName, 307 StAXContentHandler handler) 308 throws SAXException 309 { 310 } 311 312/** 313 * Handles basic exit processing. 314 */ 315 public void endElement( 316 String nsURI, 317 String localName, 318 String qName, 319 StAXContentHandler handler) 320 throws SAXException 321 { 322 // is this a return after delegation or really mine? 323 if (!(myLocalName.equals(localName)) ) 324 return; 325 326 // last chance to clear up before exiting 327// System.out.println("StAXFeatureHandler endElement called, localName, level: " + localName + " " + stackLevel); 328 endElementHandler(nsURI, localName, qName, handler); 329 330 // it's mine, get prepared to leave. 331 if (hasCallback) pop(); 332 333 // is it time to go beddy-byes? 334 if (stackLevel == 0) { 335 // we need to cope with zero-sized elements 336 try { 337 if (!startFired) fireStartFeature(); 338 if (!endFired) fireEndFeature(); 339 } 340 catch (ParseException pe) { 341 throw new SAXException("ParseException thrown in user code"); 342 } 343 } 344// System.out.println("StAXFeatureHandler endElement-leaving, localName, level: " + localName + " " + stackLevel); 345 } 346}