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.xff; 023 024import org.biojava.bio.Annotation; 025import org.biojava.bio.MergeAnnotation; 026import org.biojava.bio.OverlayAnnotation; 027import org.biojava.bio.SmallAnnotation; 028import org.biojava.bio.seq.Feature; 029import org.biojava.bio.seq.io.ParseException; 030import org.biojava.bio.symbol.Location; 031import org.biojava.utils.AssertionFailure; 032import org.biojava.utils.ChangeVetoException; 033import org.biojava.utils.stax.DelegationManager; 034import org.biojava.utils.stax.StAXContentHandler; 035import org.biojava.utils.stax.StAXContentHandlerBase; 036import org.biojava.utils.stax.StringElementHandlerBase; 037import org.xml.sax.Attributes; 038import org.xml.sax.SAXException; 039 040/** 041 * StAX handler for the basic <code>feature</code> type of XFF. 042 * This class can also be subclassed to handle other XFF types. 043 * 044 * <p> 045 * In general, to handle a <code>feature</code> subclass, you will 046 * with to: 047 * </p> 048 * 049 * <ul> 050 * <li>If necessary, override createFeatureTemplate to build the appropriate BioJava 051 * Feature.Template</li> 052 * <li>Add your own <code>startElement</code> and <code>endElement</code> 053 * methods which handle extra extra elements in your feature type. These 054 * should normally pass on all the standard elements to 055 * <code>super.startElement</code> and <code>super.endElement</code>.</li> 056 * </ul> 057 * 058 * <p> 059 * Note that, since <code>FeatureHandler</code> does some basic housekeeping, 060 * if you `consume' a startElement notification (i.e. don't pass it on to the 061 * superclass) you must also consume the matching endElement. Since FeatureHandler 062 * silently ignores all unrecognized elements, it is usually safe to pass on 063 * all startElement and endElement notifications -- even those which are specific 064 * to your feature type. 065 * </p> 066 * 067 * @author Thomas Down 068 * @since 1.2 069 */ 070 071public class FeatureHandler extends StAXContentHandlerBase { 072 public static final XFFPartHandlerFactory FEATURE_HANDLER_FACTORY = new XFFPartHandlerFactory() { 073 public StAXContentHandler getPartHandler(XFFFeatureSetHandler xffenv) { 074 return new FeatureHandler(xffenv); 075 } 076 } ; 077 078 private XFFFeatureSetHandler xffenv; 079 private Feature.Template template = null; 080 private boolean startFired = false; 081 private boolean endFired = false; 082 private int level = 0; 083 084 /** 085 * Construct a new Feature handler, passing in an XFF-parsing environment. 086 */ 087 088 public FeatureHandler(XFFFeatureSetHandler xffenv) { 089 this.xffenv = xffenv; 090 } 091 092 /** 093 * Return the XFF processing environment passed in when this handler was 094 * created. 095 */ 096 097 public XFFFeatureSetHandler getXFFEnvironment() { 098 return xffenv; 099 } 100 101 /** 102 * Get the template for the feature being constructed. This should 103 * usually not be overridden. Delegates to <code>createFeatureTemplate</code> 104 * for template construction. 105 */ 106 107 protected Feature.Template getFeatureTemplate() { 108 if (template == null) { 109 template = createFeatureTemplate(); 110 } 111 return template; 112 } 113 114 /** 115 * Create a new template of the appropriate type. Override this method 116 * if you wish to use a template type other than Feature.Template. 117 */ 118 119 protected Feature.Template createFeatureTemplate() { 120 return new Feature.Template(); 121 } 122 123 /** 124 * Fire the startFeature event. You should wrap this method if you want 125 * to perform any validation on the Feature.Template before the 126 * startFeature is fired. 127 * 128 * @throws ParseException if the startFeature notification fails, or if 129 * it has already been made. 130 */ 131 132 protected void fireStartFeature() 133 throws ParseException 134 { 135 if (startFired) { 136 throw new ParseException("startFeature event has already been fired for this feature"); 137 } 138 139 Feature.Template templ = getFeatureTemplate(); 140 Annotation ann = getXFFEnvironment().getMergeAnnotation(); 141 Annotation orig = templ.annotation; 142 Annotation res = null; 143 144 if(ann != null && orig != null) { 145 try { 146 MergeAnnotation ma = new MergeAnnotation(); 147 ma.addAnnotation(templ.annotation); 148 ma.addAnnotation(ann); 149 res = ma; 150 } catch (ChangeVetoException cve) { 151 throw new AssertionFailure(cve); 152 } 153 } else if(ann != null) { 154 res = ann; 155 } else if(orig != null) { 156 res = orig; 157 } 158 159 if(res == null) { 160 res = new SmallAnnotation(); 161 } else { 162 res = new OverlayAnnotation(res); 163 } 164 165 templ.annotation = res; 166 167 getXFFEnvironment().getFeatureListener().startFeature(templ); 168 startFired = true; 169 } 170 171 /** 172 * Fire the endFeature event. 173 */ 174 175 protected void fireEndFeature() 176 throws ParseException 177 { 178 if (!startFired) { 179 throw new ParseException("startFeature has not yet been fired for this feature."); 180 } 181 182 if (endFired) { 183 throw new ParseException("endFeature event has already been fired for this feature"); 184 } 185 186 getXFFEnvironment().getFeatureListener().endFeature(); 187 endFired = true; 188 } 189 190 /** 191 * Set a property. If the startFeature notification has not yet been fired, 192 * the property is added directly to the annotation bundle in the feature 193 * template, otherwise an addFeatureProperty event is generated. 194 */ 195 196 protected void setFeatureProperty(Object key, Object value) 197 throws ChangeVetoException, ParseException 198 { 199 if (startFired) { 200 getXFFEnvironment().getFeatureListener().addFeatureProperty(key, value); 201 } else { 202 Feature.Template ft = getFeatureTemplate(); 203 if (ft.annotation == null) { 204 ft.annotation = new SmallAnnotation(); 205 } 206 ft.annotation.setProperty(key, value); 207 } 208 } 209 210 /** 211 * StAX callback for element starts. Wrap this method to handle extra 212 * elements within your own feature types. 213 */ 214 215 public void startElement(String nsURI, 216 String localName, 217 String qName, 218 Attributes attrs, 219 DelegationManager dm) 220 throws SAXException 221 { 222 level++; 223 if (level == 1) { 224 // This was our initial startElement. 225 String id = attrs.getValue("id"); 226 if (id != null) { 227 try { 228 setFeatureProperty(XFFFeatureSetHandler.PROPERTY_XFF_ID, id); 229 } catch (Exception ex) { 230 throw new SAXException("Couldn't set property", ex); 231 } 232 } 233 } else { 234 if (localName.equals("type")) { 235 dm.delegate(getTypeHandler()); 236 } else if (localName.equals("source")) { 237 dm.delegate(getSourceHandler()); 238 } else if (localName.equals("location")) { 239 dm.delegate(getLocationHandler()); 240 } else if (localName.equals("id")) { 241 dm.delegate(getOldIDHandler()); 242 } else if (localName.equals("details")) { 243 if (!startFired) { 244 try { 245 fireStartFeature(); 246 } catch (ParseException ex) { 247 throw new SAXException(ex); 248 } 249 } 250 251 dm.delegate(xffenv.getDetailsHandler()); 252 253 // Need to handle details properly 254 } else if (localName.equals("featureSet")) { 255 if (!startFired) { 256 try { 257 fireStartFeature(); 258 } catch (ParseException ex) { 259 throw new SAXException(ex); 260 } 261 } 262 263 dm.delegate(xffenv); 264 } else { 265 // throw new SAXException("Couldn't recognize element " + localName + " in namespace " + nsURI); 266 } 267 } 268 } 269 270 /** 271 * StAX callback for element ends. Wrap this method to handle extra 272 * elements within your own feature types. 273 */ 274 275 public void endElement(String nsURI, 276 String localName, 277 String qName, 278 StAXContentHandler handler) 279 throws SAXException 280 { 281 level--; 282 283 if (level == 0) { 284 // Our tree is done. 285 286 try { 287 if (!startFired) { 288 fireStartFeature(); 289 } 290 291 if (!endFired) { 292 fireEndFeature(); 293 } 294 } catch (ParseException ex) { 295 throw new SAXException(ex); 296 } 297 } 298 } 299 300 protected StAXContentHandler getTypeHandler() { 301 return new StringElementHandlerBase() { 302 protected void setStringValue(String s) { 303 getFeatureTemplate().type = s.trim(); 304 } 305 } ; 306 } 307 308 protected StAXContentHandler getSourceHandler() { 309 return new StringElementHandlerBase() { 310 protected void setStringValue(String s) { 311 getFeatureTemplate().source = s.trim(); 312 } 313 } ; 314 } 315 316 protected StAXContentHandler getOldIDHandler() { 317 return new StringElementHandlerBase() { 318 protected void setStringValue(String s) 319 throws SAXException 320 { 321 try { 322 setFeatureProperty(XFFFeatureSetHandler.PROPERTY_XFF_ID, s.trim()); 323 } catch (Exception ex) { 324 throw new SAXException("Couldn't set property", ex); 325 } 326 } 327 } ; 328 } 329 330 protected StAXContentHandler getLocationHandler() { 331 return new LocationHandlerBase() { 332 protected void setLocationValue(Location l) { 333 getFeatureTemplate().location = l; 334 } 335 } ; 336 } 337}