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.util.ArrayList;
025import java.util.HashMap;
026import java.util.HashSet;
027import java.util.Iterator;
028import java.util.List;
029import java.util.Map;
030import java.util.Set;
031
032import org.biojava.bio.AnnotationType;
033import org.biojava.bio.BioError;
034import org.biojava.bio.CardinalityConstraint;
035import org.biojava.bio.CollectionConstraint;
036import org.biojava.bio.PropertyConstraint;
037import org.biojava.bio.symbol.Location;
038import org.biojava.bio.symbol.LocationTools;
039import org.biojava.bio.symbol.RangeLocation;
040import org.biojava.utils.ChangeVetoException;
041import org.biojava.utils.ClassTools;
042import org.biojava.utils.stax.DelegationManager;
043import org.biojava.utils.stax.StAXContentHandler;
044import org.biojava.utils.stax.StAXContentHandlerBase;
045import org.biojava.utils.stax.StringElementHandlerBase;
046import org.xml.sax.Attributes;
047import org.xml.sax.SAXException;
048
049/**
050 * StAX handler for parsing AnnotationTypes in FilterXML documents.  Mainly
051 * used internally by XMLFilterHandler.
052 *
053 * @author Thomas Down
054 * @since 1.3
055 */
056
057public class XMLAnnotationTypeHandler extends StAXContentHandlerBase {
058    private Map handlerFactories = new HashMap();
059    private Map collectionHandlerFactories = new HashMap();
060    private AnnotationType annotationType = new AnnotationType.Impl();
061    private int depth = 0;
062
063    /**
064     * Return the AnnotationType built by this handler
065     */
066
067    public AnnotationType getAnnotationType() {
068        return annotationType;
069    }
070
071    /**
072     * Handler for an individual <code>PropertyConstraint</code> in an AnnotationType.
073     * Implement this if you want to add support for additional types of
074     * PropertyConstraint.
075     */
076
077    public static interface PropertyConstraintHandler extends StAXContentHandler {
078        public PropertyConstraint getPropertyConstraint() throws SAXException;
079    }
080
081    /**
082     * Handler Factory for a <code>PropertyConstraint</code> in an AnnotationType.
083     * Implement this if you want to add support for additional types of
084     * PropertyConstraint.
085     */
086
087    public static interface PropertyConstraintHandlerFactory {
088        public PropertyConstraintHandler makeHandler(String nsURI,
089                                                     String localName)
090             throws SAXException;
091    }
092
093    /**
094     * Handler for an individual <code>CollectionConstraint</code> in an AnnotationType.
095     * Implement this if you want to add support for additional types of
096     * CollectionConstraint.
097     */
098
099    public static interface CollectionConstraintHandler extends StAXContentHandler {
100        public CollectionConstraint getCollectionConstraint() throws SAXException;
101    }
102
103    /**
104     * Handler Factory for a <code>CollectionConstraint</code> in an AnnotationType.
105     * Implement this if you want to add support for additional types of
106     * CollectionConstraint.
107     */
108
109    public static interface CollectionConstraintHandlerFactory {
110        public CollectionConstraintHandler makeHandler(String nsURI,
111                                                       String localName)
112             throws SAXException;
113    }
114
115    /**
116     * Register a factory used to create handlers for the specified tag in an
117     * XML AnnotationType
118     */
119
120    public void registerPropertyHandlerFactory(String nsURI, String localName, PropertyConstraintHandlerFactory factory) {
121        handlerFactories.put(new QName(nsURI, localName), factory);
122    }
123
124    /**
125     * Register a factory used to create handlers for the specified tag in an
126     * XML AnnotationType
127     */
128
129    public void registerCollectionHandlerFactory(String nsURI, String localName, CollectionConstraintHandlerFactory factory) {
130        collectionHandlerFactories.put(new QName(nsURI, localName), factory);
131    }
132
133
134
135    private PropertyConstraintHandler getHandler(String nsURI, String localName)
136        throws SAXException
137    {
138        PropertyConstraintHandlerFactory factory = (PropertyConstraintHandlerFactory) handlerFactories.get(new QName(nsURI, localName));
139        if (factory != null) {
140            return factory.makeHandler(nsURI, localName);
141        } else {
142            throw new SAXException("Unrecognized element (property context) " + nsURI + ":" + localName);
143        }
144    }
145
146    private CollectionConstraintHandler getCollectionHandler(String nsURI, String localName)
147        throws SAXException
148    {
149        CollectionConstraintHandlerFactory factory = (CollectionConstraintHandlerFactory) collectionHandlerFactories.get(new QName(nsURI, localName));
150        if (factory != null) {
151            return factory.makeHandler(nsURI, localName);
152        } else {
153            throw new SAXException("Unrecognized element (collection context) " + nsURI + ":" + localName);
154        }
155    }
156
157    /**
158     * Construct a new XMLAnnotationTypeHandler which can parse the builtin PropertyConstraints.
159     */
160
161    public XMLAnnotationTypeHandler() {
162        registerPropertyHandlerFactory(
163            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
164            "any",
165            new CDATAHandlerFactory() {
166                protected PropertyConstraint stringToConstraint(String s) {
167                    return PropertyConstraint.ANY;
168                }
169            }
170        );
171        registerPropertyHandlerFactory(
172            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
173            "none",
174            new CDATAHandlerFactory() {
175                protected PropertyConstraint stringToConstraint(String s) {
176                    return PropertyConstraint.NONE;
177                }
178            }
179        );
180        registerPropertyHandlerFactory(
181            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
182            "value",
183            new CDATAHandlerFactory() {
184                protected PropertyConstraint stringToConstraint(String s) {
185                    return new PropertyConstraint.ExactValue(s);
186                }
187            }
188        );
189        registerPropertyHandlerFactory(
190            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
191            "byClass",
192            new CDATAHandlerFactory() {
193                protected PropertyConstraint stringToConstraint(String s)
194                    throws SAXException
195                {
196                    try {
197                        return new PropertyConstraint.ByClass(ClassTools.getClassLoader(this).loadClass(s));
198                    } catch (Exception ex) {
199                        throw new SAXException("Couldn't find class " + s);
200                    }
201                }
202            }
203        );
204        registerPropertyHandlerFactory(
205            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
206            "and",
207            new ConstraintsHandlerFactory() {
208                public PropertyConstraint constraintsToConstraint(List l)
209                    throws SAXException
210                {
211                    PropertyConstraint pc = PropertyConstraint.ANY;
212                    Iterator i = l.iterator();
213                    pc = (PropertyConstraint) i.next();
214                    while (i.hasNext()) {
215                        pc = new PropertyConstraint.And(pc, (PropertyConstraint) i.next());
216                    }
217                    return pc;
218                }
219            }
220        );
221        registerPropertyHandlerFactory(
222            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
223            "or",
224            new ConstraintsHandlerFactory() {
225                public PropertyConstraint constraintsToConstraint(List l)
226                    throws SAXException
227                {
228                    boolean notValue = false;
229                    for (Iterator i = l.iterator(); i.hasNext(); ) {
230                        if (! (i.next() instanceof PropertyConstraint.ExactValue)) {
231                            notValue = true;
232                        }
233                    }
234                    if (notValue) {
235                        PropertyConstraint pc = PropertyConstraint.ANY;
236                        Iterator i = l.iterator();
237                        pc = (PropertyConstraint) i.next();
238                        while (i.hasNext()) {
239                            pc = new PropertyConstraint.Or(pc, (PropertyConstraint) i.next());
240                        }
241                        return pc;
242                    } else {
243                        Set values = new HashSet();
244                        for (Iterator i = l.iterator(); i.hasNext(); ) {
245                            values.add(((PropertyConstraint.ExactValue) i.next()).getValue());
246                        }
247                        return new PropertyConstraint.Enumeration(values);
248                    }
249                }
250            }
251        );
252        registerPropertyHandlerFactory(
253            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
254            "byAnnotationType",
255            new ByAnnotationTypeHandlerFactory()
256        );
257
258
259        registerCollectionHandlerFactory(
260            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
261            "any",
262            new CDATACollectionHandlerFactory() {
263                protected CollectionConstraint stringToConstraint(String s) {
264                    return CollectionConstraint.ANY;
265                }
266            }
267        );
268        registerCollectionHandlerFactory(
269            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
270            "none",
271            new CDATACollectionHandlerFactory() {
272                protected CollectionConstraint stringToConstraint(String s) {
273                    return CollectionConstraint.NONE;
274                }
275            }
276        );
277        registerCollectionHandlerFactory(
278            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
279            "empty",
280            new CDATACollectionHandlerFactory() {
281                protected CollectionConstraint stringToConstraint(String s) {
282                    return CollectionConstraint.EMPTY;
283                }
284            }
285        );
286        registerCollectionHandlerFactory(
287            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
288            "and",
289            new ConstraintsCollectionHandlerFactory() {
290                public CollectionConstraint constraintsToConstraint(List l)
291                    throws SAXException
292                {
293                    CollectionConstraint pc = CollectionConstraint.ANY;
294                    Iterator i = l.iterator();
295                    pc = (CollectionConstraint) i.next();
296                    while (i.hasNext()) {
297                        pc = new CollectionConstraint.And(pc, (CollectionConstraint) i.next());
298                    }
299                    return pc;
300                }
301            }
302        );
303        registerCollectionHandlerFactory(
304            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
305            "or",
306            new ConstraintsCollectionHandlerFactory() {
307                public CollectionConstraint constraintsToConstraint(List l)
308                    throws SAXException
309                {
310                    CollectionConstraint pc = CollectionConstraint.ANY;
311                    Iterator i = l.iterator();
312                    pc = (CollectionConstraint) i.next();
313                    while (i.hasNext()) {
314                        pc = new CollectionConstraint.Or(pc, (CollectionConstraint) i.next());
315                    }
316                    return pc;
317                }
318            }
319        );
320        registerCollectionHandlerFactory(
321            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
322            "allValuesIn",
323            new PropCardCollectionHandlerFactory() {
324                public CollectionConstraint constraintsToConstraint(Location card, PropertyConstraint pc)
325                    throws SAXException
326                {
327                    return new CollectionConstraint.AllValuesIn(pc, card);
328                }
329            }
330        );
331        registerCollectionHandlerFactory(
332            XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS,
333            "contains",
334            new PropCardCollectionHandlerFactory() {
335                public CollectionConstraint constraintsToConstraint(Location card, PropertyConstraint pc)
336                    throws SAXException
337                {
338                    return new CollectionConstraint.Contains(pc, card);
339                }
340            }
341        );
342    }
343
344    public void startElement(String nsURI,
345                                                 String localName,
346                                     String qName,
347                                     Attributes attrs,
348                                     DelegationManager dm)
349                throws SAXException
350    {
351        if (depth == 1) {
352            if (localName.equals("propertyDefault")) {
353                // System.err.println("Handling default constraints");
354                dm.delegate(new PropertyHandler() {
355                    protected void setConstraint(CollectionConstraint cc)
356                        throws ChangeVetoException
357                    {
358                        annotationType.setDefaultConstraint(cc);
359                    }
360                } );
361            } else if (localName.equals("property")) {
362                final Object propName = attrs.getValue(XMLAnnotationTypeWriter.XML_ANNOTATIONTYPE_NS, "name");
363                // System.err.println("Handling constraints on " + propName);
364                dm.delegate(new PropertyHandler() {
365                    protected void setConstraint(CollectionConstraint cc)
366                        throws ChangeVetoException
367                    {
368                        annotationType.setConstraint(propName, cc);
369                    }
370                } );
371            } else {
372                throw new SAXException("Unexpected element " + nsURI + ":" + localName);
373            }
374        }
375        ++depth;
376    }
377
378    public void endElement(String nsURI,
379                                               String localName,
380                                               String qName,
381                                               StAXContentHandler delegate)
382                 throws SAXException
383    {
384        --depth;
385    }
386
387    private abstract class PropertyHandler extends StAXContentHandlerBase {
388        private CollectionConstraint cons;
389        private int depth = 0;
390
391        public void startElement(String nsURI,
392                                                 String localName,
393                                     String qName,
394                                     Attributes attrs,
395                                     DelegationManager dm)
396                throws SAXException
397        {
398            if (depth == 1) {
399                dm.delegate(getCollectionHandler(nsURI, localName));
400            }
401
402            ++depth;
403        }
404
405        public void endElement(String nsURI,
406                                               String localName,
407                                               String qName,
408                                               StAXContentHandler delegate)
409                 throws SAXException
410        {
411            --depth;
412
413            /*
414
415            if (delegate instanceof CardinalityHandler) {
416                cardinality = ((CardinalityHandler) delegate).getCardinality();
417            } else if (delegate instanceof PropertyConstraintHandler) {
418                cons = ((PropertyConstraintHandler) delegate).getPropertyConstraint();
419            }
420
421            */
422            if (delegate instanceof CollectionConstraintHandler) {
423                cons = ((CollectionConstraintHandler) delegate).getCollectionConstraint();
424            }
425        }
426
427        public void endTree() {
428            try {
429                setConstraint(cons);
430            } catch (ChangeVetoException ex) {
431                throw new BioError("Assertion failed: couldn't modify AnnotationType", ex);
432            }
433        }
434
435        protected abstract void setConstraint(CollectionConstraint cc) throws ChangeVetoException;
436    }
437
438    private class CardinalityHandler extends StAXContentHandlerBase {
439        private List spans = new ArrayList();
440
441        public void startElement(String nsURI,
442                                                 String localName,
443                                     String qName,
444                                     Attributes attrs,
445                                     DelegationManager dm)
446                throws SAXException
447        {
448            // System.err.println("CardinalityHandler: " + localName);
449            if ("span".equals(localName)) {
450                int start = Integer.parseInt(attrs.getValue("start"));
451                String stops = attrs.getValue("stop");
452                int stop;
453                if (stops.equals("infinity")) {
454                    stop = Integer.MAX_VALUE;
455                } else {
456                    stop = Integer.parseInt(stops);
457                }
458                spans.add(new RangeLocation(start, stop));
459            }
460        }
461
462        public Location getCardinality() {
463            // System.err.println("GetCardinality");
464            return LocationTools.union(spans);
465        }
466    }
467
468    private abstract class CDATAHandlerFactory implements PropertyConstraintHandlerFactory {
469        private class CDATAHandler extends StringElementHandlerBase implements PropertyConstraintHandler {
470            private PropertyConstraint cons;
471
472            public PropertyConstraint getPropertyConstraint() {
473                return cons;
474            }
475
476            protected void setStringValue(String s)
477                throws SAXException
478            {
479                cons = stringToConstraint(s);
480                // System.err.println("stringToConstraint returned " + cons);
481            }
482        }
483
484        protected abstract PropertyConstraint stringToConstraint(String s) throws SAXException;
485
486        public PropertyConstraintHandler makeHandler(String nsURI, String localName) {
487            // System.err.println("Making CDATAHandler for " + localName);
488            return new CDATAHandler();
489        }
490    }
491
492    private abstract class ConstraintsHandlerFactory implements PropertyConstraintHandlerFactory {
493        private class ConstraintsHandler extends StAXContentHandlerBase implements PropertyConstraintHandler {
494            private List constraintChildren = new ArrayList();
495            private int depth = 0;
496
497            public void startElement(String nsURI,
498                                                 String localName,
499                                     String qName,
500                                     Attributes attrs,
501                                     DelegationManager dm)
502                throws SAXException
503            {
504                if (depth == 1) {
505                    PropertyConstraintHandler childHandler = getHandler(nsURI, localName);
506                    dm.delegate(childHandler);
507                }
508                ++depth;
509            }
510
511            public void endElement(String nsURI,
512                                               String localName,
513                                               String qName,
514                                               StAXContentHandler delegate)
515                 throws SAXException
516            {
517                if (delegate instanceof PropertyConstraintHandler) {
518                    constraintChildren.add(((PropertyConstraintHandler) delegate).getPropertyConstraint());
519                }
520                --depth;
521            }
522
523            public PropertyConstraint getPropertyConstraint()
524                throws SAXException
525            {
526                return constraintsToConstraint(constraintChildren);
527            }
528        }
529
530        protected abstract PropertyConstraint constraintsToConstraint(List filters) throws SAXException;
531
532        public PropertyConstraintHandler makeHandler(String nsURI, String localName) {
533            return new ConstraintsHandler();
534        }
535    }
536
537    private class ByAnnotationTypeHandlerFactory implements PropertyConstraintHandlerFactory {
538        private class ByAnnotationTypeHandler extends StAXContentHandlerBase implements PropertyConstraintHandler {
539            private int depth = 0;
540            private AnnotationType annoType;
541
542            public void startElement(String nsURI,
543                                                 String localName,
544                                     String qName,
545                                     Attributes attrs,
546                                     DelegationManager dm)
547                throws SAXException
548            {
549                if (depth == 1) {
550                    dm.delegate(new XMLAnnotationTypeHandler());
551                }
552                ++depth;
553            }
554
555            public void endElement(String nsURI,
556                                               String localName,
557                                               String qName,
558                                               StAXContentHandler delegate)
559                 throws SAXException
560            {
561                if (delegate instanceof XMLAnnotationTypeHandler) {
562                    annoType = ((XMLAnnotationTypeHandler) delegate).getAnnotationType();
563                }
564                --depth;
565            }
566
567            public PropertyConstraint getPropertyConstraint()
568                throws SAXException
569            {
570                return new PropertyConstraint.ByAnnotationType(annoType);
571            }
572        }
573
574        public PropertyConstraintHandler makeHandler(String nsURI, String localName) {
575            return new ByAnnotationTypeHandler();
576        }
577    }
578
579    private abstract class CDATACollectionHandlerFactory implements CollectionConstraintHandlerFactory {
580        private class CDATAHandler extends StringElementHandlerBase implements CollectionConstraintHandler {
581            private CollectionConstraint cons;
582
583            public CollectionConstraint getCollectionConstraint() {
584                return cons;
585            }
586
587            protected void setStringValue(String s)
588                throws SAXException
589            {
590                cons = stringToConstraint(s);
591                // System.err.println("stringToConstraint returned " + cons);
592            }
593        }
594
595        protected abstract CollectionConstraint stringToConstraint(String s) throws SAXException;
596
597        public CollectionConstraintHandler makeHandler(String nsURI, String localName) {
598            // System.err.println("Making CDATAHandler for " + localName);
599            return new CDATAHandler();
600        }
601    }
602
603    private abstract class ConstraintsCollectionHandlerFactory implements CollectionConstraintHandlerFactory {
604        private class ConstraintsHandler extends StAXContentHandlerBase implements CollectionConstraintHandler {
605            private List constraintChildren = new ArrayList();
606            private int depth = 0;
607
608            public void startElement(String nsURI,
609                                                 String localName,
610                                     String qName,
611                                     Attributes attrs,
612                                     DelegationManager dm)
613                throws SAXException
614            {
615                if (depth == 1) {
616                    CollectionConstraintHandler childHandler = getCollectionHandler(nsURI, localName);
617                    dm.delegate(childHandler);
618                }
619                ++depth;
620            }
621
622            public void endElement(String nsURI,
623                                               String localName,
624                                               String qName,
625                                               StAXContentHandler delegate)
626                 throws SAXException
627            {
628                if (delegate instanceof CollectionConstraintHandler) {
629                    constraintChildren.add(((CollectionConstraintHandler) delegate).getCollectionConstraint());
630                }
631                --depth;
632            }
633
634            public CollectionConstraint getCollectionConstraint()
635                throws SAXException
636            {
637                return constraintsToConstraint(constraintChildren);
638            }
639        }
640
641        protected abstract CollectionConstraint constraintsToConstraint(List filters) throws SAXException;
642
643        public CollectionConstraintHandler makeHandler(String nsURI, String localName) {
644            return new ConstraintsHandler();
645        }
646    }
647
648    private abstract class PropCardCollectionHandlerFactory implements CollectionConstraintHandlerFactory {
649        private class PropCardHandler extends StAXContentHandlerBase implements CollectionConstraintHandler {
650            private Location cardinality;
651            private PropertyConstraint pc;
652            private int depth = 0;
653
654            public void startElement(String nsURI,
655                                                 String localName,
656                                     String qName,
657                                     Attributes attrs,
658                                     DelegationManager dm)
659                throws SAXException
660            {
661                if (depth == 1) {
662                    if  ("cardinalityAny".equals(localName)) {
663                        cardinality = CardinalityConstraint.ANY;
664                    } else if ("cardinalityZero".equals(localName)) {
665                        cardinality = CardinalityConstraint.ZERO;
666                    } else if ("cardinalityOne".equals(localName)) {
667                        cardinality = CardinalityConstraint.ONE;
668                    } else if ("cardinalityNone".equals(localName)) {
669                        cardinality = CardinalityConstraint.NONE;
670                    } else if ("cardinality".equals(localName)) {
671                        dm.delegate(new CardinalityHandler());
672                    } else {
673                        dm.delegate(getHandler(nsURI, localName));
674                    }
675                }
676                ++depth;
677            }
678
679            public void endElement(String nsURI,
680                                               String localName,
681                                               String qName,
682                                               StAXContentHandler delegate)
683                 throws SAXException
684            {
685                if (delegate instanceof CardinalityHandler) {
686                    cardinality = ((CardinalityHandler) delegate).getCardinality();
687                } else if (delegate instanceof PropertyConstraintHandler) {
688                    pc = ((PropertyConstraintHandler) delegate).getPropertyConstraint();
689                }
690                --depth;
691            }
692
693            public CollectionConstraint getCollectionConstraint()
694                throws SAXException
695            {
696                return constraintsToConstraint(cardinality, pc);
697            }
698        }
699
700        protected abstract CollectionConstraint constraintsToConstraint(Location card, PropertyConstraint pc) throws SAXException;
701
702        public CollectionConstraintHandler makeHandler(String nsURI, String localName) {
703            return new PropCardHandler();
704        }
705    }
706}