001/** 002 003 * BioJava development code 004 005 * 006 007 * This code may be freely distributed and modified under the 008 009 * terms of the GNU Lesser General Public Licence. This should 010 011 * be distributed with the code. If you do not have a copy, 012 013 * see: 014 015 * 016 017 * http://www.gnu.org/copyleft/lesser.html 018 019 * 020 021 * Copyright for this code is held jointly by the individual 022 023 * authors. These should be listed in @author doc comments. 024 025 * 026 027 * For more information on the BioJava project and its aims, 028 029 * or to join the biojava-l mailing list, visit the home page 030 031 * at: 032 033 * 034 035 * http://www.biojava.org/ 036 037 * 038 039 */ 040 041package org.biojava.bio.seq.io.agave; 042 043import java.util.ArrayList; 044import java.util.Iterator; 045import java.util.List; 046import java.util.ListIterator; 047 048import org.biojava.bio.Annotation; 049import org.biojava.bio.BioException; 050import org.biojava.bio.SimpleAnnotation; 051import org.biojava.bio.seq.Feature; 052import org.biojava.bio.seq.Sequence; 053import org.biojava.bio.seq.StrandedFeature; 054import org.biojava.bio.seq.io.ParseException; 055import org.biojava.bio.seq.io.SeqIOListener; 056import org.biojava.bio.symbol.Location; 057import org.biojava.utils.ChangeVetoException; 058import org.xml.sax.Attributes; 059import org.xml.sax.SAXException; 060 061 062 063/** 064 065 * StAX handler shamelessly ripped off from Thomas Down's 066 067 * XFFFeatureSetHandler. It was modified for greater 068 069 * generality. 070 071 * 072 073 * <strong>NOTE</strong> This class is not thread-safe -- it 074 075 * must only be used for one parse at any time. 076 077 * <p> 078 079 * Because AGAVE has nested feature and each feature must attached to 080 081 * some parent feature-holder in biojava, which means we need to generate parents first, 082 083 * so we have to keep the tree structure of features in memory, which is really bad. 084 085 * anyway, we still saved a lot of memory compared with DOM tree . Hanning Ni) 086 087 088 089 * @author copied from Thomas Down 090 091 * @author copied from David Huen 092 093 * @author Hanning Ni Doubletwist Inc 094 095 * 096 097 */ 098 099public class StAXFeatureHandler extends StAXContentHandlerBase 100 101{ 102 103 // class variables 104 105 private boolean setOnceFired = false; 106 107 protected String myLocalName; 108 109 private boolean hasCallback = false; 110 111 private List handlers; 112 113 private boolean startFired=false; 114 115 private boolean endFired=false; 116 117 private boolean inFeature=false; // have we been here before? 118 119 120 121 // class variables for use by nested elements 122 123 protected Feature.Template featureTemplate; 124 125 protected StAXFeatureHandler staxenv ; 126 127 protected SeqIOListener featureListener; 128 129 protected int startLoc; 130 131 protected int endLoc; // needed because some XML don't give start and stops as attributes. 132 133/** 134 135 * this is the stack of handler objects for the current feature. 136 137 * The base value is the FeatureHandler itself. 138 139 * your feature and property handlers place and remove themselves 140 141 * from this stack. 142 143 * 144 145 * the purpose of all this is to implement context sensitivty for 146 147 * property handlers translucently. Property handlers can pop the 148 149 * stack for other handlers that implement interfaces that process 150 151 * that element. This way the context code is within the object that 152 153 * defines that context rather than in a child property handler. 154 155 */ 156 157 protected List callbackStack; 158 159 protected int stackLevel; 160 161 162 163 //store all the sub features, should be a tree structure with the root is 164 165 //top level seqeuence 166 167 protected List subFeatures ; 168 169 170 171 protected SimpleAnnotation annot = new SimpleAnnotation(); 172 173 174 175 StAXFeatureHandler(StAXFeatureHandler staxenv) { 176 177 // cache environmnet 178 179 this.staxenv = staxenv; 180 181 handlers = new ArrayList(); 182 183 callbackStack = new ArrayList(); 184 185 subFeatures = new ArrayList(1) ; 186 187 } 188 189 190 191 StAXFeatureHandler() { 192 193 handlers = new ArrayList(); 194 195 callbackStack = new ArrayList(); 196 197 subFeatures = new ArrayList(1) ; 198 199 } 200 201 202 203 // there should be a factory method here to make this class 204 205 206 207/** 208 209 * Sets the element name that the class responds to. 210 211 */ 212 213 public void setHandlerCharacteristics(String localName, boolean hasCallback) { 214 215 if (!setOnceFired) { 216 217 myLocalName = localName; 218 219 this.hasCallback = hasCallback; 220 221 setOnceFired = true; 222 223 } 224 225 } 226 227 228 229 public void setFeatureListener(SeqIOListener siol) { 230 231 featureListener = siol; 232 233 } 234 235 236 237 // Class to implement bindings 238 239 class Binding { 240 241 final ElementRecognizer recognizer; 242 243 final StAXHandlerFactory handlerFactory; 244 245 Binding(ElementRecognizer er, StAXHandlerFactory hf) 246 247 { 248 249 recognizer = er; 250 251 handlerFactory = hf; 252 253 } 254 255 } 256 257 258 259 // method to add a handler 260 261 // we do not distinguish whither it is a feature or property 262 263 // handler. The factory method creates the right type subclassed 264 265 // from the correct type of handler 266 267 protected void addHandler( 268 269 ElementRecognizer rec, 270 271 StAXHandlerFactory handler) 272 273 { 274 275 handlers.add(new Binding(rec, handler)); 276 277 } 278 279 280 281/** 282 283 * generates a very basic Template for the feature with 284 285 * SmallAnnotation in the annotation field. 286 287 * <p> 288 289 * Override if you wish a more specialised Template. 290 291 */ 292 293 protected Feature.Template createTemplate() { 294 295 // create Gene Template for this 296 297 StrandedFeature.Template st = new StrandedFeature.Template(); 298 299 300 301 // assume feature set to describe a transcript 302 303 st.source = myLocalName; 304 305 st.type = myLocalName; 306 307 st.strand = StrandedFeature.UNKNOWN; 308 309 // set up annotation bundle 310 311 st.annotation = annot ; 312 313 st.location = Location.empty; 314 315 if( staxenv != null ) 316 317 staxenv. subFeatures .add( this ) ; 318 319 320 321 return st; 322 323 } 324 325 326 327 /** 328 329 * recursively attach children features to parent 330 331 */ 332 333 protected void realizeSubFeatures(Feature feature) 334 335 { 336 337 try{ 338 339 Iterator it = subFeatures.iterator() ; 340 341 while( it.hasNext() ) 342 343 { 344 345 StAXFeatureHandler handler = (StAXFeatureHandler)it.next() ; 346 347 if( handler instanceof SequenceHandler ) //already handled 348 349 continue ; 350 351 Feature f = feature.createFeature ( handler.featureTemplate ) ; 352 353 handler.realizeSubFeatures( f ) ; 354 355 } 356 357 }catch(BioException e){ e.printStackTrace();} 358 359 catch(ChangeVetoException e){ e.printStackTrace();} 360 361 } 362 363 364 365 protected void addFeatureToSequence(Sequence seq) throws Exception 366 367 { 368 369 Iterator it = subFeatures.iterator() ; 370 371 while( it.hasNext() ) 372 373 { 374 375 StAXFeatureHandler handler = (StAXFeatureHandler)it.next() ; 376 377 Feature f = seq.createFeature ( handler.featureTemplate ) ; 378 379 handler.realizeSubFeatures( f ) ; 380 381 } 382 383 } 384 385/** 386 387 * return current stack level. Remember that the 388 389 * stack level is incremented/decremented AFTER 390 391 * the push()/pop() calls and superclass 392 393 * startElement()/StopElement calls. 394 395 */ 396 397 protected int getLevel() { 398 399 return stackLevel; 400 401 } 402 403 404 405/** 406 407 * return iterator to callbackStack 408 409 */ 410 411 protected ListIterator getHandlerStackIterator(int level) { 412 413 return callbackStack.listIterator(level); 414 415 } 416 417 418 419/** 420 421 * Push StAXContentHandler object onto stack 422 423 */ 424 425 protected void push(StAXContentHandler handler) { 426 427 // push handler 428 429 callbackStack.add(handler); 430 431 432 433 // increment pointer 434 435 stackLevel++; 436 437 } 438 439 440 441/** 442 443 * pop a StAXContentHandler off the stack. 444 445 */ 446 447 protected void pop() { 448 449 // decrement pointer 450 451 stackLevel--; 452 453 454 455 // pop handler 456 457 callbackStack.remove(stackLevel); 458 459 } 460 461 462 463/** 464 465 * Return current feature listener 466 467 */ 468 469 public SeqIOListener getFeatureListener() { 470 471 return featureListener; 472 473 } 474 475 476 477/** 478 479 * Fire the startFeature event. 480 481 */ 482 483 private void fireStartFeature() 484 485 throws ParseException 486 487 { 488 489 if (startFired) 490 491 throw new ParseException("startFeature event has already been fired"); 492 493 494 495 if (featureTemplate == null) 496 497 featureTemplate = createTemplate(); 498 499 500 501 if (featureTemplate.annotation == null) 502 503 featureTemplate.annotation = Annotation.EMPTY_ANNOTATION; 504 505 506 507 featureListener.startFeature(featureTemplate); 508 509 startFired = true; 510 511 } 512 513 514 515/** 516 517 * Fire the endFeature event. 518 519 */ 520 521 private void fireEndFeature() 522 523 throws ParseException 524 525 { 526 527 if (!startFired) 528 529 throw new ParseException("startFeature has not yet been fired!"); 530 531 532 533 if (endFired) 534 535 throw new ParseException("endFeature event has already been fired!"); 536 537 featureListener.endFeature(); 538 539 endFired = true; 540 541 } 542 543 544 545 protected void setProperty(String name, String value, boolean forFeature) 546 547 { 548 549 if ( value != null) { 550 551 try { 552 553 annot.setProperty( name, value); 554 555 if( forFeature ) 556 557 featureListener.addFeatureProperty(name, value); 558 559 else 560 561 featureListener.addSequenceProperty(name, value); 562 563 } 564 565 catch (ChangeVetoException cae) { 566 567 System.err.println(" veto exception caught."); 568 569 } 570 571 catch (ParseException cae) { 572 573 System.err.println("parse exception in addProperty() ."); 574 575 } 576 577 } 578 579 } 580 581/** 582 583 * Element-specific handler. 584 585 * Subclass this to do something useful! 586 587 */ 588 589 public void startElementHandler( 590 591 String nsURI, 592 593 String localName, 594 595 String qName, 596 597 Attributes attrs) 598 599 throws SAXException 600 601 { 602 603 } 604 605 606 607 608 609/** 610 611 * Handles basic entry processing for all feature handlers. 612 613 */ 614 615 public void startElement( 616 617 String nsURI, 618 619 String localName, 620 621 String qName, 622 623 Attributes attrs, 624 625 DelegationManager dm) 626 627 throws SAXException 628 629 { 630 631 // sanity check 632 633 if (!setOnceFired) 634 635 throw new SAXException("StAXFeaturehandler not initialised before use!"); 636 637 638 639 // find out if this element is really for me. 640 641 // perform delegation 642 643 // if (!(myLocalName.equals(localName)) ) { 644 645 if( dm.getRecursive() ) 646 647 for (int i = handlers.size() - 1; i >= 0; --i) { 648 649 Binding b = (Binding) handlers.get(i); 650 651 if (b.recognizer.filterStartElement(nsURI, localName, qName, attrs)) { 652 653 dm.delegate(b.handlerFactory.getHandler(this)); 654 655 return; 656 657 } 658 659 } 660 661 // } 662 663 // I don't have a delegate for it but it might be a stray... 664 665 if (!(myLocalName.equals(localName)) ) { 666 667 //this one's not for me! 668 669 return; 670 671 } 672 673 674 675 // this one's mine. 676 677 // initialise if this is the first time thru' 678 679 if (!inFeature) { 680 681 inFeature = true; 682 683 684 685 if (hasCallback) { 686 687 if (stackLevel == 0) push(this); 688 689 } 690 691 692 693 // indicate start of feature to listener 694 695 try { 696 697 if (!startFired) fireStartFeature(); 698 699 } 700 701 catch (ParseException pe) { 702 703 throw new SAXException("ParseException thrown in user code"); 704 705 } 706 707 } 708 709 710 711 // call the element specific handler now. 712 713 startElementHandler(nsURI, localName, qName, attrs); 714 715 716 717 } 718 719 720 721/** 722 723 * Element specific exit handler 724 725 * Subclass to do anything useful. 726 727 */ 728 729 public void endElementHandler( 730 731 String nsURI, 732 733 String localName, 734 735 String qName, 736 737 StAXContentHandler handler) 738 739 throws SAXException 740 741 { 742 743 } 744 745 746 747/** 748 749 * Handles basic exit processing. 750 751 */ 752 753 public void endElement( 754 755 String nsURI, 756 757 String localName, 758 759 String qName, 760 761 StAXContentHandler handler) 762 763 throws SAXException 764 765 { 766 767 // is this a return after delegation or really mine? 768 769 if (!(myLocalName.equals(localName)) ) 770 771 return; 772 773 774 775 // last chance to clear up before exiting 776 777 //endElementHandler(nsURI, localName, qName, handler); 778 779 780 781 // it's mine, get prepared to leave. 782 783 if (hasCallback) pop(); 784 785 786 787 // is it time to go beddy-byes? 788 789 if (stackLevel == 0) { 790 791 // we need to cope with zero-sized elements 792 793 try { 794 795 if (!startFired) fireStartFeature(); 796 797 if (!endFired) fireEndFeature(); 798 799 } 800 801 catch (ParseException pe) { 802 803 throw new SAXException("ParseException thrown in user code"); 804 805 } 806 807 } 808 809 endElementHandler(nsURI, localName, qName, handler); 810 811 } 812 813} 814