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.util.HashMap; 026import java.util.Iterator; 027import java.util.Map; 028import java.util.Set; 029 030import org.biojava.bio.AnnotationType; 031import org.biojava.bio.BioError; 032import org.biojava.bio.CardinalityConstraint; 033import org.biojava.bio.CollectionConstraint; 034import org.biojava.bio.PropertyConstraint; 035import org.biojava.bio.symbol.Location; 036import org.biojava.utils.xml.XMLWriter; 037 038/** 039 * Main class for writing AnnotationTypes as XML. Knows about all the builtin 040 * classes of AnnotationType. It's possible to plug new ones in by calling 041 * one of the addXMLPropertyConstraintWriter methods. 042 * 043 * @author Thomas Down 044 * @since 1.3 045 */ 046 047public class XMLAnnotationTypeWriter { 048 /** 049 * XML namespace string used to the AnnotationType representation 050 */ 051 052 public static final String XML_ANNOTATIONTYPE_NS = "http://www.biojava.org/AnnotationType"; 053 054 private Map constraintWritersByObject = new HashMap(); 055 private Map constraintWritersByClass = new HashMap(); 056 private Map colConstraintWritersByObject = new HashMap(); 057 private Map colConstraintWritersByClass = new HashMap(); 058 private boolean strict = false; 059 060 /** 061 * Writer for types of CollectionConstraint. Implement this to add support 062 * for a new type of CollectionConstraint. 063 * 064 * @author Thomas Down 065 * @since 1.3 066 */ 067 068 public interface XMLCollectionConstraintWriter { 069 public void writeCollectionConstraint(CollectionConstraint pc, 070 XMLWriter xw, 071 XMLAnnotationTypeWriter config) 072 throws ClassCastException, IOException, IllegalArgumentException; 073 } 074 075 /** 076 * Writer for types of PropertyConstraint. Implement this to add support 077 * for a new type of PropertyConstraint. 078 * 079 * @author Thomas Down 080 * @since 1.3 081 */ 082 083 public interface XMLPropertyConstraintWriter { 084 public void writePropertyConstraint(PropertyConstraint pc, 085 XMLWriter xw, 086 XMLAnnotationTypeWriter config) 087 throws ClassCastException, IOException, IllegalArgumentException; 088 } 089 090 /** 091 * Construct a new AnnotationTypeWriter which knows about the builtin types of PropertyConstraint 092 */ 093 094 public XMLAnnotationTypeWriter() { 095 try { 096 addXMLPropertyConstraintWriter( 097 PropertyConstraint.ANY, 098 new BlankConstraintWriter(XML_ANNOTATIONTYPE_NS, "any") 099 ); 100 addXMLPropertyConstraintWriter( 101 PropertyConstraint.NONE, 102 new BlankConstraintWriter(XML_ANNOTATIONTYPE_NS, "none") 103 ); 104 addXMLPropertyConstraintWriter( 105 PropertyConstraint.ExactValue.class, 106 new ExactValueConstraintWriter() 107 ); 108 addXMLPropertyConstraintWriter( 109 PropertyConstraint.ByClass.class, 110 new ByClassConstraintWriter() 111 ); 112 addXMLPropertyConstraintWriter( 113 PropertyConstraint.And.class, 114 new AndConstraintWriter() 115 ); 116 addXMLPropertyConstraintWriter( 117 PropertyConstraint.Or.class, 118 new OrConstraintWriter() 119 ); 120 addXMLPropertyConstraintWriter( 121 PropertyConstraint.Enumeration.class, 122 new EnumConstraintWriter() 123 ); 124 addXMLPropertyConstraintWriter( 125 PropertyConstraint.ByAnnotationType.class, 126 new AnnotationTypeConstraintWriter() 127 ); 128 129 addXMLCollectionConstraintWriter( 130 CollectionConstraint.ANY, 131 new BlankCollectionConstraintWriter(XML_ANNOTATIONTYPE_NS, "any") 132 ); 133 addXMLCollectionConstraintWriter( 134 CollectionConstraint.NONE, 135 new BlankCollectionConstraintWriter(XML_ANNOTATIONTYPE_NS, "none") 136 ); 137 addXMLCollectionConstraintWriter( 138 CollectionConstraint.EMPTY, 139 new BlankCollectionConstraintWriter(XML_ANNOTATIONTYPE_NS, "empty") 140 ); 141 addXMLCollectionConstraintWriter( 142 CollectionConstraint.And.class, 143 new AndCollectionConstraintWriter() 144 ); 145 addXMLCollectionConstraintWriter( 146 CollectionConstraint.Or.class, 147 new OrCollectionConstraintWriter() 148 ); 149 addXMLCollectionConstraintWriter( 150 CollectionConstraint.AllValuesIn.class, 151 new AllValuesInCollectionConstraintWriter() 152 ); 153 addXMLCollectionConstraintWriter( 154 CollectionConstraint.Contains.class, 155 new ContainsCollectionConstraintWriter() 156 ); 157 } catch (Exception ex) { 158 throw new BioError("Assertion failed: couldn't initialize XMLFilterWriters", ex); 159 } 160 } 161 162 /** 163 * Register a writer for the specified class of property constraint 164 */ 165 166 public void addXMLPropertyConstraintWriter(Class clazz, XMLPropertyConstraintWriter xfw) { 167 constraintWritersByClass.put(clazz, xfw); 168 } 169 170 /** 171 * Register a writer for a singleton property constraint. 172 */ 173 174 public void addXMLPropertyConstraintWriter(PropertyConstraint pc, XMLPropertyConstraintWriter xfw) { 175 constraintWritersByObject.put(pc, xfw); 176 } 177 178 /** 179 * Register a writer for the specified class of collection constraint 180 */ 181 182 public void addXMLCollectionConstraintWriter(Class clazz, XMLCollectionConstraintWriter xfw) { 183 colConstraintWritersByClass.put(clazz, xfw); 184 } 185 186 /** 187 * Register a writer for a singleton property constraint. 188 */ 189 190 public void addXMLCollectionConstraintWriter(CollectionConstraint pc, XMLCollectionConstraintWriter xfw) { 191 colConstraintWritersByObject.put(pc, xfw); 192 } 193 194 /** 195 * Determine if this writer is in strict mode. 196 */ 197 198 public boolean isStrict() { 199 return strict; 200 } 201 202 /** 203 * Selects strict mode. In strict mode, the writer will throw an <code>IllegalArgumentException</code> 204 * if it encounters a type of <code>PropertyConstraint</code> it doesn't recognize. When not 205 * in strict model, unrecognized constraints are silently replaced by <code>PropertyConstraint.ANY</code>. 206 * Default is <code>false</code>. 207 */ 208 209 public void setIsStrict(boolean b) { 210 this.strict = b; 211 } 212 213 private void writeCardinality(Location card, 214 XMLWriter xw) 215 throws IllegalArgumentException, IOException 216 { 217 if (card == CardinalityConstraint.ANY) { 218 xw.openTag(XML_ANNOTATIONTYPE_NS, "cardinalityAny"); 219 xw.closeTag(XML_ANNOTATIONTYPE_NS, "cardinalityAny"); 220 } else if (card == CardinalityConstraint.ZERO) { 221 xw.openTag(XML_ANNOTATIONTYPE_NS, "cardinalityZero"); 222 xw.closeTag(XML_ANNOTATIONTYPE_NS, "cardinalityZero"); 223 } else if (card == CardinalityConstraint.ONE) { 224 xw.openTag(XML_ANNOTATIONTYPE_NS, "cardinalityOne"); 225 xw.closeTag(XML_ANNOTATIONTYPE_NS, "cardinalityOne"); 226 } else if (card == CardinalityConstraint.NONE) { 227 xw.openTag(XML_ANNOTATIONTYPE_NS, "cardinalityNone"); 228 xw.closeTag(XML_ANNOTATIONTYPE_NS, "cardinalityNone"); 229 } else { 230 xw.openTag(XML_ANNOTATIONTYPE_NS, "cardinality"); 231 for (Iterator bi = card.blockIterator(); bi.hasNext(); ) { 232 Location bloc = (Location) bi.next(); 233 xw.openTag("span"); 234 xw.attribute("start", intToString(bloc.getMin())); 235 xw.attribute("stop", intToString(bloc.getMax())); 236 xw.closeTag("span"); 237 } 238 xw.closeTag(XML_ANNOTATIONTYPE_NS, "cardinality"); 239 } 240 } 241 242 private String intToString(int i) { 243 if (i == Integer.MAX_VALUE) { 244 return "infinity"; 245 } else { 246 return "" + i; 247 } 248 } 249 250 /** 251 * Writes a single property constraint. 252 */ 253 254 void writePropertyConstraint(PropertyConstraint pc, 255 XMLWriter xw) 256 throws IllegalArgumentException, IOException 257 { 258 XMLPropertyConstraintWriter xpcw = (XMLPropertyConstraintWriter) constraintWritersByObject.get(pc); 259 if (xpcw == null) { 260 xpcw = (XMLPropertyConstraintWriter) constraintWritersByClass.get(pc.getClass()); 261 } 262 if (xpcw == null) { 263 if (strict) { 264 throw new IllegalArgumentException("Couldn't find a writer for constraint of type " + pc.getClass().getName()); 265 } else { 266 xpcw = (XMLPropertyConstraintWriter) constraintWritersByObject.get(PropertyConstraint.ANY); 267 } 268 } 269 xpcw.writePropertyConstraint(pc, xw, this); 270 } 271 272 /** 273 * Writes a single collection constraint 274 */ 275 276 void writeCollectionConstraint(CollectionConstraint pc, 277 XMLWriter xw) 278 throws IllegalArgumentException, IOException 279 { 280 XMLCollectionConstraintWriter xpcw = (XMLCollectionConstraintWriter) colConstraintWritersByObject.get(pc); 281 if (xpcw == null) { 282 xpcw = (XMLCollectionConstraintWriter) colConstraintWritersByClass.get(pc.getClass()); 283 } 284 if (xpcw == null) { 285 if (strict) { 286 throw new IllegalArgumentException("Couldn't find a writer for constraint of type " + pc.getClass().getName()); 287 } else { 288 xpcw = (XMLCollectionConstraintWriter) constraintWritersByObject.get(CollectionConstraint.ANY); 289 } 290 } 291 xpcw.writeCollectionConstraint(pc, xw, this); 292 } 293 294 /** 295 * Write an <code>AnnotationType</code> to the specified XMLWriter. 296 * 297 * @throws IllegalArgumentException if the AnnotationType contains unrecognized 298 * constraints, and the writer is in strict mode. 299 * @throws IOException if an error occurs while outputting XML. 300 */ 301 302 public void writeAnnotationType(AnnotationType at, XMLWriter xw) 303 throws IllegalArgumentException, IOException 304 { 305 xw.openTag(XML_ANNOTATIONTYPE_NS, "annotationType"); 306 Set propKeys = at.getProperties(); 307 308 xw.openTag(XML_ANNOTATIONTYPE_NS, "propertyDefault"); 309 CollectionConstraint.AllValuesIn defaultcc = (CollectionConstraint.AllValuesIn) at.getDefaultConstraint(); 310 // writeCardinality(defaultcc.getCardinalityConstraint(), xw); 311 // writePropertyConstraint(defaultcc.getPropertyConstraint(), xw); 312 writeCollectionConstraint(defaultcc, xw); 313 xw.closeTag(XML_ANNOTATIONTYPE_NS, "propertyDefault"); 314 315 for (Iterator pi = propKeys.iterator(); pi.hasNext(); ) { 316 Object propKey = pi.next(); 317 CollectionConstraint cc = at.getConstraint(propKey); 318 319 xw.openTag(XML_ANNOTATIONTYPE_NS, "property"); 320 xw.attribute(XML_ANNOTATIONTYPE_NS, "name", propKey.toString()); 321 // CollectionConstraint.AllValuesIn cc = (CollectionConstraint.AllValuesIn) at.getConstraint(propKey); 322 // writeCardinality(cc.getCardinalityConstraint(), xw); 323 // writePropertyConstraint(cc.getPropertyConstraint(), xw); 324 writeCollectionConstraint(cc, xw); 325 xw.closeTag(XML_ANNOTATIONTYPE_NS, "property"); 326 } 327 xw.closeTag(XML_ANNOTATIONTYPE_NS, "annotationType"); 328 } 329 330 private static class BlankConstraintWriter implements XMLPropertyConstraintWriter { 331 private String nsURI; 332 private String localName; 333 334 BlankConstraintWriter(String nsURI, String localName) { 335 this.nsURI = nsURI; 336 this.localName = localName; 337 } 338 339 public void writePropertyConstraint(PropertyConstraint pc, 340 XMLWriter xw, 341 XMLAnnotationTypeWriter config) 342 throws ClassCastException, IllegalArgumentException, IOException 343 { 344 xw.openTag(nsURI, localName); 345 xw.closeTag(nsURI, localName); 346 } 347 } 348 349 private static class ExactValueConstraintWriter implements XMLPropertyConstraintWriter { 350 public void writePropertyConstraint(PropertyConstraint pc, 351 XMLWriter xw, 352 XMLAnnotationTypeWriter config) 353 throws ClassCastException, IllegalArgumentException, IOException 354 { 355 PropertyConstraint.ExactValue pcev = (PropertyConstraint.ExactValue) pc; 356 xw.openTag(XML_ANNOTATIONTYPE_NS, "value"); 357 xw.print(pcev.getValue().toString()); 358 xw.closeTag(XML_ANNOTATIONTYPE_NS, "value"); 359 } 360 } 361 362 private static class ByClassConstraintWriter implements XMLPropertyConstraintWriter { 363 public void writePropertyConstraint(PropertyConstraint pc, 364 XMLWriter xw, 365 XMLAnnotationTypeWriter config) 366 throws ClassCastException, IllegalArgumentException, IOException 367 { 368 PropertyConstraint.ByClass pcev = (PropertyConstraint.ByClass) pc; 369 xw.openTag(XML_ANNOTATIONTYPE_NS, "byClass"); 370 xw.print(pcev.getPropertyClass().getName()); 371 xw.closeTag(XML_ANNOTATIONTYPE_NS, "byClass"); 372 } 373 } 374 375 private static class AndConstraintWriter implements XMLPropertyConstraintWriter { 376 public void writePropertyConstraint(PropertyConstraint pc, 377 XMLWriter xw, 378 XMLAnnotationTypeWriter config) 379 throws ClassCastException, IllegalArgumentException, IOException 380 { 381 PropertyConstraint.And pca = (PropertyConstraint.And) pc; 382 xw.openTag(XML_ANNOTATIONTYPE_NS, "and"); 383 writeSubConstraint(pca, xw, config); 384 xw.closeTag(XML_ANNOTATIONTYPE_NS, "and"); 385 } 386 387 private void writeSubConstraint(PropertyConstraint pc, 388 XMLWriter xw, 389 XMLAnnotationTypeWriter config) 390 throws ClassCastException, IllegalArgumentException, IOException 391 { 392 if (pc instanceof PropertyConstraint.And) { 393 PropertyConstraint.And ffa = (PropertyConstraint.And) pc; 394 writeSubConstraint(ffa.getChild1(), xw, config); 395 writeSubConstraint(ffa.getChild2(), xw, config); 396 } else { 397 config.writePropertyConstraint(pc, xw); 398 } 399 } 400 } 401 402 private static class OrConstraintWriter implements XMLPropertyConstraintWriter { 403 public void writePropertyConstraint(PropertyConstraint pc, 404 XMLWriter xw, 405 XMLAnnotationTypeWriter config) 406 throws ClassCastException, IllegalArgumentException, IOException 407 { 408 PropertyConstraint.Or pca = (PropertyConstraint.Or) pc; 409 xw.openTag(XML_ANNOTATIONTYPE_NS, "or"); 410 writeSubConstraint(pca, xw, config); 411 xw.closeTag(XML_ANNOTATIONTYPE_NS, "or"); 412 } 413 414 private void writeSubConstraint(PropertyConstraint pc, 415 XMLWriter xw, 416 XMLAnnotationTypeWriter config) 417 throws ClassCastException, IllegalArgumentException, IOException 418 { 419 if (pc instanceof PropertyConstraint.Or) { 420 PropertyConstraint.Or ffa = (PropertyConstraint.Or) pc; 421 writeSubConstraint(ffa.getChild1(), xw, config); 422 writeSubConstraint(ffa.getChild2(), xw, config); 423 } else { 424 config.writePropertyConstraint(pc, xw); 425 } 426 } 427 } 428 429 private static class EnumConstraintWriter implements XMLPropertyConstraintWriter { 430 public void writePropertyConstraint(PropertyConstraint pc, 431 XMLWriter xw, 432 XMLAnnotationTypeWriter config) 433 throws ClassCastException, IllegalArgumentException, IOException 434 { 435 PropertyConstraint.Enumeration pcenum = (PropertyConstraint.Enumeration) pc; 436 xw.openTag(XML_ANNOTATIONTYPE_NS, "or"); 437 for (Iterator vi = pcenum.getValues().iterator(); vi.hasNext(); ) { 438 xw.openTag(XML_ANNOTATIONTYPE_NS, "value"); 439 xw.print(vi.next().toString()); 440 xw.closeTag(XML_ANNOTATIONTYPE_NS, "value"); 441 } 442 xw.closeTag(XML_ANNOTATIONTYPE_NS, "or"); 443 } 444 } 445 446 private static class AnnotationTypeConstraintWriter implements XMLPropertyConstraintWriter { 447 public void writePropertyConstraint(PropertyConstraint pc, 448 XMLWriter xw, 449 XMLAnnotationTypeWriter config) 450 throws ClassCastException, IllegalArgumentException, IOException 451 { 452 PropertyConstraint.ByAnnotationType pcbat = (PropertyConstraint.ByAnnotationType) pc; 453 config.writeAnnotationType(pcbat.getAnnotationType(), xw); 454 } 455 } 456 457 458 459 private static class BlankCollectionConstraintWriter implements XMLCollectionConstraintWriter { 460 private String nsURI; 461 private String localName; 462 463 BlankCollectionConstraintWriter(String nsURI, String localName) { 464 this.nsURI = nsURI; 465 this.localName = localName; 466 } 467 468 public void writeCollectionConstraint(CollectionConstraint pc, 469 XMLWriter xw, 470 XMLAnnotationTypeWriter config) 471 throws ClassCastException, IllegalArgumentException, IOException 472 { 473 xw.openTag(nsURI, localName); 474 xw.closeTag(nsURI, localName); 475 } 476 } 477 478 private static class AndCollectionConstraintWriter implements XMLCollectionConstraintWriter { 479 public void writeCollectionConstraint(CollectionConstraint pc, 480 XMLWriter xw, 481 XMLAnnotationTypeWriter config) 482 throws ClassCastException, IllegalArgumentException, IOException 483 { 484 CollectionConstraint.And pca = (CollectionConstraint.And) pc; 485 xw.openTag(XML_ANNOTATIONTYPE_NS, "and"); 486 writeSubConstraint(pca, xw, config); 487 xw.closeTag(XML_ANNOTATIONTYPE_NS, "and"); 488 } 489 490 private void writeSubConstraint(CollectionConstraint pc, 491 XMLWriter xw, 492 XMLAnnotationTypeWriter config) 493 throws ClassCastException, IllegalArgumentException, IOException 494 { 495 if (pc instanceof CollectionConstraint.And) { 496 CollectionConstraint.And ffa = (CollectionConstraint.And) pc; 497 writeSubConstraint(ffa.getChild1(), xw, config); 498 writeSubConstraint(ffa.getChild2(), xw, config); 499 } else { 500 config.writeCollectionConstraint(pc, xw); 501 } 502 } 503 } 504 505 private static class OrCollectionConstraintWriter implements XMLCollectionConstraintWriter { 506 public void writeCollectionConstraint(CollectionConstraint pc, 507 XMLWriter xw, 508 XMLAnnotationTypeWriter config) 509 throws ClassCastException, IllegalArgumentException, IOException 510 { 511 CollectionConstraint.Or pca = (CollectionConstraint.Or) pc; 512 xw.openTag(XML_ANNOTATIONTYPE_NS, "or"); 513 writeSubConstraint(pca, xw, config); 514 xw.closeTag(XML_ANNOTATIONTYPE_NS, "or"); 515 } 516 517 private void writeSubConstraint(CollectionConstraint pc, 518 XMLWriter xw, 519 XMLAnnotationTypeWriter config) 520 throws ClassCastException, IllegalArgumentException, IOException 521 { 522 if (pc instanceof CollectionConstraint.Or) { 523 CollectionConstraint.Or ffa = (CollectionConstraint.Or) pc; 524 writeSubConstraint(ffa.getChild1(), xw, config); 525 writeSubConstraint(ffa.getChild2(), xw, config); 526 } else { 527 config.writeCollectionConstraint(pc, xw); 528 } 529 } 530 } 531 532 private static class AllValuesInCollectionConstraintWriter implements XMLCollectionConstraintWriter { 533 public void writeCollectionConstraint(CollectionConstraint pc, 534 XMLWriter xw, 535 XMLAnnotationTypeWriter config) 536 throws ClassCastException, IllegalArgumentException, IOException 537 { 538 CollectionConstraint.AllValuesIn cc = (CollectionConstraint.AllValuesIn) pc; 539 xw.openTag(XML_ANNOTATIONTYPE_NS, "allValuesIn"); 540 config.writeCardinality(cc.getCardinalityConstraint(), xw); 541 config.writePropertyConstraint(cc.getPropertyConstraint(), xw); 542 xw.closeTag(XML_ANNOTATIONTYPE_NS, "allValuesIn"); 543 } 544 545 } 546 547 private static class ContainsCollectionConstraintWriter implements XMLCollectionConstraintWriter { 548 public void writeCollectionConstraint(CollectionConstraint pc, 549 XMLWriter xw, 550 XMLAnnotationTypeWriter config) 551 throws ClassCastException, IllegalArgumentException, IOException 552 { 553 CollectionConstraint.Contains cc = (CollectionConstraint.Contains) pc; 554 xw.openTag(XML_ANNOTATIONTYPE_NS, "contains"); 555 config.writeCardinality(cc.getCardinalityConstraint(), xw); 556 config.writePropertyConstraint(cc.getPropertyConstraint(), xw); 557 xw.closeTag(XML_ANNOTATIONTYPE_NS, "contains"); 558 } 559 560 } 561}