/*
* BioJava development code
*
* This code may be freely distributed and modified under the
* terms of the GNU Lesser General Public Licence. This should
* be distributed with the code. If you do not have a copy,
* see:
*
* http://www.gnu.org/copyleft/lesser.html
*
* Copyright for this code is held jointly by the individual
* authors. These should be listed in @author doc comments.
*
* For more information on the BioJava project and its aims,
* or to join the biojava-l mailing list, visit the home page
* at:
*
* http://www.biojava.org/
*
*/
package org.biojava.bio.program.xff;
import org.biojava.bio.Annotation;
import org.biojava.bio.MergeAnnotation;
import org.biojava.bio.OverlayAnnotation;
import org.biojava.bio.SmallAnnotation;
import org.biojava.bio.seq.Feature;
import org.biojava.bio.seq.io.ParseException;
import org.biojava.bio.symbol.Location;
import org.biojava.utils.AssertionFailure;
import org.biojava.utils.ChangeVetoException;
import org.biojava.utils.stax.DelegationManager;
import org.biojava.utils.stax.StAXContentHandler;
import org.biojava.utils.stax.StAXContentHandlerBase;
import org.biojava.utils.stax.StringElementHandlerBase;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
/**
* StAX handler for the basic feature
type of XFF.
* This class can also be subclassed to handle other XFF types.
*
*
* In general, to handle a feature
subclass, you will
* with to:
*
startElement
and endElement
* methods which handle extra extra elements in your feature type. These
* should normally pass on all the standard elements to
* super.startElement
and super.endElement
.
* Note that, since FeatureHandler
does some basic housekeeping,
* if you `consume' a startElement notification (i.e. don't pass it on to the
* superclass) you must also consume the matching endElement. Since FeatureHandler
* silently ignores all unrecognized elements, it is usually safe to pass on
* all startElement and endElement notifications -- even those which are specific
* to your feature type.
*
createFeatureTemplate
* for template construction.
*/
protected Feature.Template getFeatureTemplate() {
if (template == null) {
template = createFeatureTemplate();
}
return template;
}
/**
* Create a new template of the appropriate type. Override this method
* if you wish to use a template type other than Feature.Template.
*/
protected Feature.Template createFeatureTemplate() {
return new Feature.Template();
}
/**
* Fire the startFeature event. You should wrap this method if you want
* to perform any validation on the Feature.Template before the
* startFeature is fired.
*
* @throws ParseException if the startFeature notification fails, or if
* it has already been made.
*/
protected void fireStartFeature()
throws ParseException
{
if (startFired) {
throw new ParseException("startFeature event has already been fired for this feature");
}
Feature.Template templ = getFeatureTemplate();
Annotation ann = getXFFEnvironment().getMergeAnnotation();
Annotation orig = templ.annotation;
Annotation res = null;
if(ann != null && orig != null) {
try {
MergeAnnotation ma = new MergeAnnotation();
ma.addAnnotation(templ.annotation);
ma.addAnnotation(ann);
res = ma;
} catch (ChangeVetoException cve) {
throw new AssertionFailure(cve);
}
} else if(ann != null) {
res = ann;
} else if(orig != null) {
res = orig;
}
if(res == null) {
res = new SmallAnnotation();
} else {
res = new OverlayAnnotation(res);
}
templ.annotation = res;
getXFFEnvironment().getFeatureListener().startFeature(templ);
startFired = true;
}
/**
* Fire the endFeature event.
*/
protected void fireEndFeature()
throws ParseException
{
if (!startFired) {
throw new ParseException("startFeature has not yet been fired for this feature.");
}
if (endFired) {
throw new ParseException("endFeature event has already been fired for this feature");
}
getXFFEnvironment().getFeatureListener().endFeature();
endFired = true;
}
/**
* Set a property. If the startFeature notification has not yet been fired,
* the property is added directly to the annotation bundle in the feature
* template, otherwise an addFeatureProperty event is generated.
*/
protected void setFeatureProperty(Object key, Object value)
throws ChangeVetoException, ParseException
{
if (startFired) {
getXFFEnvironment().getFeatureListener().addFeatureProperty(key, value);
} else {
Feature.Template ft = getFeatureTemplate();
if (ft.annotation == null) {
ft.annotation = new SmallAnnotation();
}
ft.annotation.setProperty(key, value);
}
}
/**
* StAX callback for element starts. Wrap this method to handle extra
* elements within your own feature types.
*/
public void startElement(String nsURI,
String localName,
String qName,
Attributes attrs,
DelegationManager dm)
throws SAXException
{
level++;
if (level == 1) {
// This was our initial startElement.
String id = attrs.getValue("id");
if (id != null) {
try {
setFeatureProperty(XFFFeatureSetHandler.PROPERTY_XFF_ID, id);
} catch (Exception ex) {
throw new SAXException("Couldn't set property", ex);
}
}
} else {
if (localName.equals("type")) {
dm.delegate(getTypeHandler());
} else if (localName.equals("source")) {
dm.delegate(getSourceHandler());
} else if (localName.equals("location")) {
dm.delegate(getLocationHandler());
} else if (localName.equals("id")) {
dm.delegate(getOldIDHandler());
} else if (localName.equals("details")) {
if (!startFired) {
try {
fireStartFeature();
} catch (ParseException ex) {
throw new SAXException(ex);
}
}
dm.delegate(xffenv.getDetailsHandler());
// Need to handle details properly
} else if (localName.equals("featureSet")) {
if (!startFired) {
try {
fireStartFeature();
} catch (ParseException ex) {
throw new SAXException(ex);
}
}
dm.delegate(xffenv);
} else {
// throw new SAXException("Couldn't recognize element " + localName + " in namespace " + nsURI);
}
}
}
/**
* StAX callback for element ends. Wrap this method to handle extra
* elements within your own feature types.
*/
public void endElement(String nsURI,
String localName,
String qName,
StAXContentHandler handler)
throws SAXException
{
level--;
if (level == 0) {
// Our tree is done.
try {
if (!startFired) {
fireStartFeature();
}
if (!endFired) {
fireEndFeature();
}
} catch (ParseException ex) {
throw new SAXException(ex);
}
}
}
protected StAXContentHandler getTypeHandler() {
return new StringElementHandlerBase() {
protected void setStringValue(String s) {
getFeatureTemplate().type = s.trim();
}
} ;
}
protected StAXContentHandler getSourceHandler() {
return new StringElementHandlerBase() {
protected void setStringValue(String s) {
getFeatureTemplate().source = s.trim();
}
} ;
}
protected StAXContentHandler getOldIDHandler() {
return new StringElementHandlerBase() {
protected void setStringValue(String s)
throws SAXException
{
try {
setFeatureProperty(XFFFeatureSetHandler.PROPERTY_XFF_ID, s.trim());
} catch (Exception ex) {
throw new SAXException("Couldn't set property", ex);
}
}
} ;
}
protected StAXContentHandler getLocationHandler() {
return new LocationHandlerBase() {
protected void setLocationValue(Location l) {
getFeatureTemplate().location = l;
}
} ;
}
}