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.seq.io.ParseException;
029import org.biojava.bio.seq.io.SeqIOListener;
030import org.biojava.utils.stax.DelegationManager;
031import org.biojava.utils.stax.StAXContentHandler;
032import org.biojava.utils.stax.StAXContentHandlerBase;
033import org.xml.sax.Attributes;
034import org.xml.sax.SAXException;
035
036/**
037 * StAX handler shamelessly ripped off from Thomas Down's
038 * XFFFeatureSetHandler.
039 *
040 * <strong>NOTE</strong> This class is not thread-safe -- it
041 * must only be used for one parse at any time.
042 *
043 * This class is the basis for classes that do not create
044 * a new feature but modify an existing feature.
045 *
046 * It is not compulsory for property handlers to subclass
047 * this class but those that don't but wish to use the
048 * handler stack facility need to use the StaxFeatureHandler's
049 * push and pop methods.
050 *
051 * @author Thomas Down
052 * @author David Huen
053 *
054 * @since 1.8
055 */
056public class StAXPropertyHandler extends StAXContentHandlerBase
057{
058  private String myLocalName;
059  private boolean hasCallback = false;
060  private boolean inElement = false;
061  private boolean setOnceFired = false;
062
063  // class variables
064  protected SeqIOListener featureListener;
065  private List handlers;
066  protected StAXFeatureHandler staxenv;
067  private int baseLevel = 0;
068
069  {
070    handlers = new ArrayList();
071  }
072
073  // there should be a factory method here to make this class
074
075  // constructor
076  // because every StAXPropertyHandler was ultimately
077  // invoked from a StAXFeatureHandler via delegation
078  // the staxenv will point at the StAXFeatureHandler
079  // at the base of the current chain of StAXPropertyHandler,
080  // which is not necessarily the first root element
081  // StAXFeatureHandler.
082  StAXPropertyHandler(StAXFeatureHandler staxenv) {
083    // cache environmnet
084    this.staxenv = staxenv;
085  }
086
087/**
088 * Sets the element name that the class responds to.
089 */
090  public void setHandlerCharacteristics(String localName, boolean hasCallback) 
091  {    
092  if (!setOnceFired) {
093      myLocalName = localName;
094      this.hasCallback = hasCallback;
095      setOnceFired = true;
096    }
097    else
098      System.err.println("setHandlerChracteristics called twice on same handler");  
099  }
100
101
102/**
103 * get iterator for current stack starting at the position
104 * below mine.
105 */
106  protected ListIterator getHandlerStackIterator() 
107      throws ParseException {
108    if (baseLevel >= 1)
109      return staxenv.getHandlerStackIterator(baseLevel-1);
110    else
111      throw new ParseException("getHandlerStackIterator while at bottom of stack.");
112  }
113
114  // Class to implement bindings
115  class Binding {
116    final ElementRecognizer recognizer;
117    final StAXHandlerFactory handlerFactory;
118    Binding(ElementRecognizer er, StAXHandlerFactory hf)
119    {
120      recognizer = er;
121      handlerFactory = hf;
122    }
123  }
124
125  // method to add a handler
126  // we do not distinguish whither it is a feature or property
127  // handler.  The factory method creates the right type subclassed
128  // from the correct type of handler
129  protected void addHandler(
130                   ElementRecognizer rec, 
131                   StAXHandlerFactory handler)
132  {
133//    System.out.println("StAXPropertyHandler.addHandler called.");
134    handlers.add(new Binding(rec, handler));
135//    System.out.println("StAXPropertyHandler.addHandler left.");
136//    System.out.println(" ");
137  }
138
139/**
140 * Element-specific handler.
141 * Subclass this to do something useful!
142 */
143  public void startElementHandler(
144                String nsURI,
145                String localName,
146                String qName,
147                Attributes attrs)
148         throws SAXException
149  {
150  }
151
152/**
153 * Override this to do any processing required but call this
154 * prior to returning.  Delegation occurs here!
155 *
156 */
157  public void startElement(
158                String nsURI,
159                String localName,
160                String qName,
161                Attributes attrs,
162                DelegationManager dm)
163         throws SAXException
164  {
165//    System.out.println("StAXPropertyHandler.startElement localName: " + localName);
166    // perform delegation
167    for (int i = handlers.size() - 1; i >= 0; --i) {
168      Binding b = (Binding) handlers.get(i);
169      if (b.recognizer.filterStartElement(nsURI, localName, qName, attrs)) {
170      dm.delegate(b.handlerFactory.getHandler(staxenv));
171      return;
172      }
173    }
174
175    // is this for me?
176    if (!(myLocalName.equals(localName)) ) return;
177
178    if (!inElement) {
179      // save current stack position just in case I want to search downwards.
180      baseLevel = staxenv.getLevel();
181
182      if (hasCallback) staxenv.push(this);
183      
184      inElement = true;
185    }
186
187    if (inElement) startElementHandler(nsURI, localName, qName, attrs);
188  }
189
190/**
191 * Element specific exit handler
192 * Subclass to do anything useful.
193 */
194  public void endElementHandler(
195                String nsURI,
196                String localName,
197                String qName,
198                StAXContentHandler handler)
199              throws SAXException
200  {
201  }
202
203  public void endElement(
204                String nsURI,
205                String localName,
206                String qName,
207                StAXContentHandler handler)
208              throws SAXException
209  {
210    // is this mine?
211    if (!(myLocalName.equals(localName)) ) return;
212
213//    System.out.println("StAXPropertyHandler.endElement localName: " + localName);
214    // do the necessary before exit
215    if (inElement) {
216      // element specific handling
217      endElementHandler(nsURI, localName, qName, handler);
218
219      if (hasCallback)
220        if (setOnceFired) {
221          staxenv.pop();
222          setOnceFired = false;
223        }
224   
225      inElement = false;
226    }
227  }
228}