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.filterxml; 023 024import java.io.IOException; 025import java.lang.reflect.InvocationTargetException; 026import java.lang.reflect.Method; 027import java.util.HashMap; 028import java.util.Iterator; 029import java.util.Map; 030 031import org.biojava.bio.AnnotationType; 032import org.biojava.bio.BioError; 033import org.biojava.bio.seq.FeatureFilter; 034import org.biojava.bio.seq.FramedFeature; 035import org.biojava.bio.seq.StrandedFeature; 036import org.biojava.bio.symbol.Location; 037import org.biojava.utils.xml.XMLWriter; 038 039/** 040 * Write FeatureFilters in XML format. 041 * 042 * @author Thomas Down 043 * @since 1.3 044 */ 045 046public class XMLFilterWriter { 047 public static final String XML_FILTER_NS = "http://www.biojava.org/FeatureFilter"; 048 private static final Class[] NO_ARGS = new Class[0]; 049 private static final Object[] NO_ARGS_VAL = new Object[0]; 050 051 private Map filterWritersByClass = new HashMap(); 052 private boolean strict = false; 053 054 /** 055 * Interface for an object which can write a FeatureFilter as XML. Implement 056 * this to add support for new types of <code>FeatureFilter</code> 057 * 058 * @author Thomas Down 059 * @since 1.3 060 */ 061 062 public interface FilterWriter { 063 public void writeFilter(FeatureFilter ff, 064 XMLWriter xw, 065 XMLFilterWriter config) 066 throws ClassCastException, IllegalArgumentException, IOException; 067 } 068 069 070 /** 071 * Construct a new <code>XMLFilterWriter</code> which can serialize the buildin types of 072 * <code>FeatureFilter</code>. 073 */ 074 075 public XMLFilterWriter() { 076 try { 077 filterWritersByClass.put( 078 FeatureFilter.all, 079 new BlankFilterWriter(XML_FILTER_NS, "all") 080 ); 081 filterWritersByClass.put( 082 FeatureFilter.none, 083 new BlankFilterWriter(XML_FILTER_NS, "none") 084 ); 085 filterWritersByClass.put( 086 FeatureFilter.ByType.class, 087 new StringFilterWriter(XML_FILTER_NS, "byType", FeatureFilter.ByType.class.getMethod("getType", NO_ARGS)) 088 ); 089 filterWritersByClass.put( 090 FeatureFilter.BySource.class, 091 new StringFilterWriter(XML_FILTER_NS, "bySource", FeatureFilter.BySource.class.getMethod("getSource", NO_ARGS)) 092 ); 093 filterWritersByClass.put( 094 FeatureFilter.ByClass.class, 095 new ByClassFilterWriter() 096 ); 097 filterWritersByClass.put( 098 FeatureFilter.ContainedByLocation.class, 099 new LocationFilterWriter(XML_FILTER_NS, "containedByLocation", FeatureFilter.ContainedByLocation.class.getMethod("getLocation", NO_ARGS)) 100 ); 101 filterWritersByClass.put( 102 FeatureFilter.OverlapsLocation.class, 103 new LocationFilterWriter(XML_FILTER_NS, "overlapsLocation", FeatureFilter.OverlapsLocation.class.getMethod("getLocation", NO_ARGS)) 104 ); 105 filterWritersByClass.put( 106 FeatureFilter.ShadowContainedByLocation.class, 107 new LocationFilterWriter(XML_FILTER_NS, "shadowContainedByLocation", FeatureFilter.ContainedByLocation.class.getMethod("getLocation", NO_ARGS)) 108 ); 109 filterWritersByClass.put( 110 FeatureFilter.ShadowOverlapsLocation.class, 111 new LocationFilterWriter(XML_FILTER_NS, "shadowOverlapsLocation", FeatureFilter.OverlapsLocation.class.getMethod("getLocation", NO_ARGS)) 112 ); 113 filterWritersByClass.put( 114 FeatureFilter.Not.class, 115 new FilterFilterWriter(XML_FILTER_NS, "not", FeatureFilter.Not.class.getMethod("getChild", NO_ARGS)) 116 ); 117 filterWritersByClass.put( 118 FeatureFilter.And.class, 119 new AndFilterWriter() 120 ); 121 filterWritersByClass.put( 122 FeatureFilter.Or.class, 123 new OrFilterWriter() 124 ); 125 filterWritersByClass.put( 126 FeatureFilter.StrandFilter.class, 127 new StrandFilterWriter() 128 ); 129 filterWritersByClass.put( 130 FeatureFilter.FrameFilter.class, 131 new FrameFilterWriter() 132 ); 133 filterWritersByClass.put( 134 FeatureFilter.ByParent.class, 135 new FilterFilterWriter(XML_FILTER_NS, "byParent", FeatureFilter.ByParent.class.getMethod("getFilter", NO_ARGS)) 136 ); 137 filterWritersByClass.put( 138 FeatureFilter.ByAncestor.class, 139 new FilterFilterWriter(XML_FILTER_NS, "byAncestor", FeatureFilter.ByAncestor.class.getMethod("getFilter", NO_ARGS)) 140 ); 141 filterWritersByClass.put( 142 FeatureFilter.ByChild.class, 143 new FilterFilterWriter(XML_FILTER_NS, "byChild", FeatureFilter.ByChild.class.getMethod("getFilter", NO_ARGS)) 144 ); 145 filterWritersByClass.put( 146 FeatureFilter.ByDescendant.class, 147 new FilterFilterWriter(XML_FILTER_NS, "byDescendant", FeatureFilter.ByDescendant.class.getMethod("getFilter", NO_ARGS)) 148 ); 149 filterWritersByClass.put( 150 FeatureFilter.OnlyChildren.class, 151 new FilterFilterWriter(XML_FILTER_NS, "onlyChildren", FeatureFilter.OnlyChildren.class.getMethod("getFilter", NO_ARGS)) 152 ); 153 filterWritersByClass.put( 154 FeatureFilter.OnlyDescendants.class, 155 new FilterFilterWriter(XML_FILTER_NS, "onlyDescendants", FeatureFilter.OnlyDescendants.class.getMethod("getFilter", NO_ARGS)) 156 ); 157 filterWritersByClass.put( 158 FeatureFilter.ByComponentName.class, 159 new StringFilterWriter(XML_FILTER_NS, "byComponentName", FeatureFilter.ByComponentName.class.getMethod("getComponentName", NO_ARGS)) 160 ); 161 filterWritersByClass.put( 162 FeatureFilter.BySequenceName.class, 163 new StringFilterWriter(XML_FILTER_NS, "bySequenceName", FeatureFilter.BySequenceName.class.getMethod("getSequenceName", NO_ARGS)) 164 ); 165 filterWritersByClass.put( 166 FeatureFilter.top_level, 167 new BlankFilterWriter(XML_FILTER_NS, "isTopLevel") 168 ); 169 filterWritersByClass.put( 170 FeatureFilter.leaf, 171 new BlankFilterWriter(XML_FILTER_NS, "isLeaf") 172 ); 173 AnnotationTypeFilterWriter atfw = new AnnotationTypeFilterWriter(new XMLAnnotationTypeWriter()); 174 filterWritersByClass.put( 175 FeatureFilter.ByAnnotationType.class, 176 atfw 177 ); 178 filterWritersByClass.put( 179 FeatureFilter.ByAnnotation.class, 180 atfw 181 ); 182 filterWritersByClass.put( 183 FeatureFilter.HasAnnotation.class, 184 atfw 185 ); 186 filterWritersByClass.put( 187 FeatureFilter.AnnotationContains.class, 188 atfw 189 ); 190 filterWritersByClass.put( 191 FeatureFilter.ByPairwiseScore.class, 192 new ByPairwiseScoreFilterWriter() 193 ); 194 } catch (Exception ex) { 195 throw new BioError("Assertion failed: couldn't initialize XMLFilterWriters",ex); 196 } 197 } 198 199 /** 200 * Add a writer for the specified class of filters 201 */ 202 203 public void addXMLFilterWriter(Class clazz, FilterWriter xfw) { 204 filterWritersByClass.put(clazz, xfw); 205 } 206 207 /** 208 * Add a writer for a singleton filter. 209 */ 210 211 public void addXMLFilterWriter(FeatureFilter ff, FilterWriter xfw) { 212 filterWritersByClass.put(ff, xfw); 213 } 214 215 /** 216 * Determine if this writer is in strict mode. 217 */ 218 219 public boolean isStrict() { 220 return strict; 221 } 222 223 /** 224 * Selects strict mode. In strict mode, the writer will throw an <code>IllegalArgumentException</code> 225 * if it encounters a type of <code>FeatureFilter</code> it doesn't recognize. When not 226 * in strict model, unrecognized filters are silently replaced by <code>FeatureFilter.all</code>. 227 * Default is <code>false</code>. 228 */ 229 230 public void setIsStrict(boolean b) { 231 this.strict = b; 232 } 233 234 /** 235 * Write a FeatureFilter to the supplied XMLWriter 236 * 237 * @throws IllegalArgumentException if the FeatureFilter is unrecognized, and the writer is 238 * in strict mode. 239 * @throws IOException if an error occurs while outputting XML. 240 */ 241 242 public void writeFilter(FeatureFilter ff, XMLWriter xw) 243 throws IllegalArgumentException, IOException 244 { 245 FilterWriter xfw = (FilterWriter) filterWritersByClass.get(ff); 246 if (xfw == null) { 247 xfw = (FilterWriter) filterWritersByClass.get(ff.getClass()); 248 } 249 if (xfw != null) { 250 try { 251 xfw.writeFilter(ff, xw, this); 252 } catch (ClassCastException ex) { 253 throw new BioError("Filter type mismatch",ex); 254 } 255 } else { 256 if (strict) { 257 throw new IllegalArgumentException("Couldn't find a writer for filter of type " + ff.getClass().getName()); 258 } else { 259 writeFilter(FeatureFilter.all, xw); 260 } 261 } 262 } 263 264 private static class BlankFilterWriter implements FilterWriter { 265 private String nsURI; 266 private String localName; 267 268 BlankFilterWriter(String nsURI, String localName) { 269 this.nsURI = nsURI; 270 this.localName = localName; 271 } 272 273 public void writeFilter(FeatureFilter ff, 274 XMLWriter xw, 275 XMLFilterWriter config) 276 throws ClassCastException, IllegalArgumentException, IOException 277 { 278 xw.openTag(nsURI, localName); 279 xw.closeTag(nsURI, localName); 280 } 281 } 282 283 private static abstract class PropertyFilterWriter implements FilterWriter { 284 private final String nsURI; 285 private final String localName; 286 private final Method propMethod; 287 288 PropertyFilterWriter(String nsURI, 289 String localName, 290 Method propMethod) 291 { 292 this.nsURI = nsURI; 293 this.localName = localName; 294 this.propMethod = propMethod; 295 } 296 297 public void writeFilter(FeatureFilter ff, 298 XMLWriter xw, 299 XMLFilterWriter config) 300 throws ClassCastException, IllegalArgumentException, IOException 301 { 302 try { 303 Object obj = propMethod.invoke(ff, NO_ARGS_VAL); 304 xw.openTag(nsURI, localName); 305 writeContent(obj, xw, config); 306 xw.closeTag(nsURI, localName); 307 } catch (IllegalAccessException ex) { 308 throw new BioError("Can't access property method",ex); 309 } catch (InvocationTargetException ex) { 310 throw new BioError("Couldn't access property",ex); 311 } 312 } 313 314 protected abstract void writeContent(Object obj, 315 XMLWriter xw, 316 XMLFilterWriter config) 317 throws ClassCastException, IllegalArgumentException, IOException; 318 } 319 320 private static class StringFilterWriter extends PropertyFilterWriter { 321 StringFilterWriter(String nsURI, 322 String localName, 323 Method propMethod) 324 { 325 super(nsURI, localName, propMethod); 326 } 327 328 protected void writeContent(Object obj, 329 XMLWriter xw, 330 XMLFilterWriter config) 331 throws ClassCastException, IllegalArgumentException, IOException 332 { 333 xw.print(obj.toString()); 334 } 335 } 336 337 private static class LocationFilterWriter extends PropertyFilterWriter { 338 LocationFilterWriter(String nsURI, 339 String localName, 340 Method propMethod) 341 { 342 super(nsURI, localName, propMethod); 343 } 344 345 protected void writeContent(Object obj, 346 XMLWriter xw, 347 XMLFilterWriter config) 348 throws ClassCastException, IllegalArgumentException, IOException 349 { 350 Location l = (Location) obj; 351 for (Iterator i = l.blockIterator(); i.hasNext(); ) { 352 Location block = (Location) i.next(); 353 xw.openTag("span"); 354 xw.attribute("start", "" + block.getMin()); 355 xw.attribute("stop", "" + block.getMax()); 356 xw.closeTag("span"); 357 } 358 } 359 } 360 361 private static class FilterFilterWriter extends PropertyFilterWriter { 362 FilterFilterWriter(String nsURI, 363 String localName, 364 Method propMethod) 365 { 366 super(nsURI, localName, propMethod); 367 } 368 369 protected void writeContent(Object obj, 370 XMLWriter xw, 371 XMLFilterWriter config) 372 throws ClassCastException, IllegalArgumentException, IOException 373 { 374 FeatureFilter ff = (FeatureFilter) obj; 375 config.writeFilter(ff, xw); 376 } 377 } 378 379 private static class AndFilterWriter implements FilterWriter { 380 public void writeFilter(FeatureFilter ff, 381 XMLWriter xw, 382 XMLFilterWriter config) 383 throws ClassCastException, IllegalArgumentException, IOException 384 { 385 xw.openTag(XML_FILTER_NS, "and"); 386 writeSubFilter(ff, xw, config); 387 xw.closeTag(XML_FILTER_NS, "and"); 388 } 389 390 private void writeSubFilter(FeatureFilter ff, 391 XMLWriter xw, 392 XMLFilterWriter config) 393 throws ClassCastException, IllegalArgumentException, IOException 394 { 395 if (ff instanceof FeatureFilter.And) { 396 FeatureFilter.And ffa = (FeatureFilter.And) ff; 397 writeSubFilter(ffa.getChild1(), xw, config); 398 writeSubFilter(ffa.getChild2(), xw, config); 399 } else { 400 config.writeFilter(ff, xw); 401 } 402 } 403 } 404 405 private static class OrFilterWriter implements FilterWriter { 406 public void writeFilter(FeatureFilter ff, 407 XMLWriter xw, 408 XMLFilterWriter config) 409 throws ClassCastException, IllegalArgumentException, IOException 410 { 411 xw.openTag(XML_FILTER_NS, "or"); 412 writeSubFilter(ff, xw, config); 413 xw.closeTag(XML_FILTER_NS, "or"); 414 } 415 416 private void writeSubFilter(FeatureFilter ff, 417 XMLWriter xw, 418 XMLFilterWriter config) 419 throws ClassCastException, IllegalArgumentException, IOException 420 { 421 if (ff instanceof FeatureFilter.Or) { 422 FeatureFilter.Or ffa = (FeatureFilter.Or) ff; 423 writeSubFilter(ffa.getChild1(), xw, config); 424 writeSubFilter(ffa.getChild2(), xw, config); 425 } else { 426 config.writeFilter(ff, xw); 427 } 428 } 429 } 430 431 private static class ByPairwiseScoreFilterWriter implements FilterWriter { 432 public void writeFilter(FeatureFilter ff, 433 XMLWriter xw, 434 XMLFilterWriter config) 435 throws ClassCastException, IllegalArgumentException, IOException 436 { 437 FeatureFilter.ByPairwiseScore scoreFilter = (FeatureFilter.ByPairwiseScore) ff; 438 xw.openTag(XML_FILTER_NS, "byPairwiseScore"); 439 xw.openTag(XML_FILTER_NS, "minScore"); 440 xw.print("" + scoreFilter.getMinScore()); 441 xw.closeTag(XML_FILTER_NS, "minScore"); 442 xw.openTag(XML_FILTER_NS, "maxScore"); 443 xw.print("" + scoreFilter.getMaxScore()); 444 xw.closeTag(XML_FILTER_NS, "maxScore"); 445 xw.closeTag(XML_FILTER_NS, "byPairwiseScore"); 446 } 447 } 448 449 private static class ByClassFilterWriter extends PropertyFilterWriter { 450 ByClassFilterWriter() 451 throws NoSuchMethodException 452 { 453 super(XML_FILTER_NS, "byClass", FeatureFilter.ByClass.class.getMethod("getTestClass", NO_ARGS)); 454 } 455 456 protected void writeContent(Object obj, 457 XMLWriter xw, 458 XMLFilterWriter config) 459 throws ClassCastException, IllegalArgumentException, IOException 460 { 461 xw.print(((Class) obj).getName()); 462 } 463 } 464 465 private static class StrandFilterWriter extends PropertyFilterWriter { 466 StrandFilterWriter() 467 throws NoSuchMethodException 468 { 469 super(XML_FILTER_NS, "byStrand", FeatureFilter.StrandFilter.class.getMethod("getStrand", NO_ARGS)); 470 } 471 472 protected void writeContent(Object obj, 473 XMLWriter xw, 474 XMLFilterWriter config) 475 throws ClassCastException, IllegalArgumentException, IOException 476 { 477 xw.print(((StrandedFeature.Strand) obj).toString()); 478 } 479 } 480 481 private static class FrameFilterWriter extends PropertyFilterWriter { 482 FrameFilterWriter() 483 throws NoSuchMethodException 484 { 485 super(XML_FILTER_NS, "byFrame", FeatureFilter.FrameFilter.class.getMethod("getFrame", NO_ARGS)); 486 } 487 488 protected void writeContent(Object obj, 489 XMLWriter xw, 490 XMLFilterWriter config) 491 throws ClassCastException, IllegalArgumentException, IOException 492 { 493 xw.print("" + ((FramedFeature.ReadingFrame) obj).getFrame()); 494 } 495 } 496 497 private static class AnnotationTypeFilterWriter extends PropertyFilterWriter { 498 private XMLAnnotationTypeWriter xatw; 499 500 AnnotationTypeFilterWriter(XMLAnnotationTypeWriter xatw) 501 throws NoSuchMethodException 502 { 503 super(XML_FILTER_NS, "byAnnotationType", FeatureFilter.ByAnnotationType.class.getMethod("getType", NO_ARGS)); 504 this.xatw = xatw; 505 } 506 507 protected void writeContent(Object obj, 508 XMLWriter xw, 509 XMLFilterWriter config) 510 throws ClassCastException, IllegalArgumentException, IOException 511 { 512 xatw.writeAnnotationType((AnnotationType) obj, xw); 513 } 514 } 515}