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.Iterator;
027import java.util.List;
028import java.util.Map;
029
030import org.biojava.bio.seq.FeatureFilter;
031import org.biojava.bio.seq.FramedFeature;
032import org.biojava.bio.seq.StrandedFeature;
033import org.biojava.bio.symbol.Location;
034import org.biojava.bio.symbol.LocationTools;
035import org.biojava.bio.symbol.RangeLocation;
036import org.biojava.utils.ClassTools;
037import org.biojava.utils.stax.DelegationManager;
038import org.biojava.utils.stax.StAXContentHandler;
039import org.biojava.utils.stax.StAXContentHandlerBase;
040import org.biojava.utils.stax.StringElementHandlerBase;
041import org.xml.sax.Attributes;
042import org.xml.sax.SAXException;
043
044/**
045 * Factory producing content handlers for parsing FilterXML elements.
046 *
047 * <p>
048 * An XMLFilterHandler object is a collection of individual StAX handlers for parsing FilterXML
049 * documents.  It uses XMLAnnotationTypeHandler to parse <code>byAnnotationType</code> elements.
050 * To handle an individual XML filter, you should call the getStAXContentHandler method
051 * <p>
052 *
053 * <p><strong>Example:</strong></p>
054 * <pre>
055 *       // Setup
056 *       XMLFilterHandler filterHandler = new XMLFilterHandler();
057 *       Reader xmlFile = new FileReader("featurefilter.xml");
058 *
059 *       // Create an XML parser
060 *       SAXParserFactory spf = SAXParserFactory.newInstance();
061 *       spf.setNamespaceAware(true);
062 *       XMLReader parser = spf.newSAXParser().getXMLReader();
063 *
064 *       // Create a new handler for this document
065 *       XMLFilterHandler.FilterHandler handler = filterHandler.getStAXContentHandler();
066 *       parser.setContentHandler(new SAX2StAXAdaptor(handler));
067 *
068 *       // Parse the file and retrieve the FeatureFilter
069 *       parser.parse(new InputSource(xmlFile));
070 *       FeatureFilter filter = handler.getFeatureFilter();
071 * </pre>
072 *
073 *
074 * @author Thomas Down
075 * @since 1.3
076 */
077 
078public class XMLFilterHandler {
079    Map handlerFactories = new HashMap();
080    
081    /**
082     * StAXContentHandler for a particular type of FeatureFilter.  Implement
083     * this interface to add parsing support for new types of FeatureFilter.
084     *
085     * @author Thomas Down
086     */
087    
088    public static interface FilterHandler extends StAXContentHandler {
089        public FeatureFilter getFeatureFilter() throws SAXException;
090    }            
091    
092    /**
093     * Factory of StAXContentHandlers for a particular type of FeatureFilter.  Implement
094     * this interface to add parsing support for new types of FeatureFilter.
095     *
096     * @author Thomas Down
097     */
098    
099    public static interface FilterHandlerFactory {
100        public FilterHandler makeHandler(String nsURI, 
101                                   String localName,
102                                   XMLFilterHandler config)
103             throws SAXException;
104    }
105    
106    /**
107     * Register a factory used to create handlers for the specified tag name
108     */
109    
110    public void registerHandlerFactory(String nsURI, String localName, FilterHandlerFactory factory) {
111        handlerFactories.put(new QName(nsURI, localName), factory);
112    }
113    
114    /**
115     * Retrieve a <code>FilterHandler</code> for the specified tag name.
116     */
117    
118    public FilterHandler getHandler(String nsURI, String localName) 
119        throws SAXException
120    {
121        FilterHandlerFactory factory = (FilterHandlerFactory) handlerFactories.get(new QName(nsURI, localName));
122        if (factory != null) {
123            return factory.makeHandler(nsURI, localName, this);
124        } else {
125            throw new SAXException("No handler for " + nsURI + ":" + localName);
126        }
127    }
128    
129    /**
130     * Construct a new XMLFilterHandler which can parse the builtin types of <code>FeatureFilter</code>.
131     */
132    
133    public XMLFilterHandler() {
134        registerHandlerFactory(
135            XMLFilterWriter.XML_FILTER_NS,
136            "byType",
137            new CDATAHandlerFactory() {
138                protected FeatureFilter stringToFilter(String s) {
139                    return new FeatureFilter.ByType(s);
140                }
141            }
142        );
143        registerHandlerFactory(
144            XMLFilterWriter.XML_FILTER_NS,
145            "bySource",
146            new CDATAHandlerFactory() {
147                protected FeatureFilter stringToFilter(String s) {
148                    return new FeatureFilter.BySource(s);
149                }
150            }
151        );
152        registerHandlerFactory(
153            XMLFilterWriter.XML_FILTER_NS,
154            "byClass",
155            new CDATAHandlerFactory() {
156                protected FeatureFilter stringToFilter(String s) 
157                    throws SAXException
158                {
159                    try {
160                        return new FeatureFilter.ByClass(ClassTools.getClassLoader(this).loadClass(s));
161                    } catch (Exception ex) {
162                        throw new SAXException("Couldn't load class " + s);
163                    }
164                }
165            }
166        );
167        registerHandlerFactory(
168            XMLFilterWriter.XML_FILTER_NS,
169            "byStrand",
170            new CDATAHandlerFactory() {
171                protected FeatureFilter stringToFilter(String s) 
172                    throws SAXException
173                {
174                    s = s.trim();
175                    StrandedFeature.Strand strand = StrandedFeature.UNKNOWN;
176                    if ("POSITIVE".equalsIgnoreCase(s)) {
177                        strand = StrandedFeature.POSITIVE;
178                    } else if ("NEGATIVE".equalsIgnoreCase(s)) {
179                        strand = StrandedFeature.NEGATIVE;
180                    } 
181                    return new FeatureFilter.StrandFilter(strand);
182                }
183            }
184        );
185        registerHandlerFactory(
186            XMLFilterWriter.XML_FILTER_NS,
187            "byFrame",
188            new CDATAHandlerFactory() {
189                protected FeatureFilter stringToFilter(String s) 
190                    throws SAXException
191                {
192                    int frameNum = Integer.parseInt(s.trim());
193                    switch (frameNum) {
194                        case 0:
195                            return new FeatureFilter.FrameFilter(FramedFeature.FRAME_0);
196                        case 1:
197                            return new FeatureFilter.FrameFilter(FramedFeature.FRAME_1);
198                        case 2:
199                            return new FeatureFilter.FrameFilter(FramedFeature.FRAME_2);
200                    }
201                    throw new SAXException("Invalid frame: " + frameNum);
202                }
203            }
204        );
205        registerHandlerFactory(
206            XMLFilterWriter.XML_FILTER_NS,
207            "all",
208            new CDATAHandlerFactory() {
209                protected FeatureFilter stringToFilter(String s) {
210                    return FeatureFilter.all;
211                }
212            }
213        );
214        registerHandlerFactory(
215            XMLFilterWriter.XML_FILTER_NS,
216            "none",
217            new CDATAHandlerFactory() {
218                protected FeatureFilter stringToFilter(String s) {
219                    return FeatureFilter.none;
220                }
221            }
222        );
223        registerHandlerFactory(
224            XMLFilterWriter.XML_FILTER_NS,
225            "isTopLevel",
226            new CDATAHandlerFactory() {
227                protected FeatureFilter stringToFilter(String s) {
228                    return FeatureFilter.top_level;
229                }
230            }
231        );
232        registerHandlerFactory(
233            XMLFilterWriter.XML_FILTER_NS,
234            "isLeaf",
235            new CDATAHandlerFactory() {
236                protected FeatureFilter stringToFilter(String s) {
237                    return FeatureFilter.leaf;
238                }
239            }
240        );
241        registerHandlerFactory(
242            XMLFilterWriter.XML_FILTER_NS,
243            "bySequenceName",
244            new CDATAHandlerFactory() {
245                protected FeatureFilter stringToFilter(String s) {
246                    return new FeatureFilter.BySequenceName(s);
247                }
248            }
249        );
250        registerHandlerFactory(
251            XMLFilterWriter.XML_FILTER_NS,
252            "byComponentName",
253            new CDATAHandlerFactory() {
254                protected FeatureFilter stringToFilter(String s) {
255                    return new FeatureFilter.ByComponentName(s);
256                }
257            }
258        );
259        registerHandlerFactory(
260            XMLFilterWriter.XML_FILTER_NS,
261            "not",
262            new SingleFilterHandlerFactory() {
263                protected FeatureFilter filterToFilter(FeatureFilter ff) {
264                    return new FeatureFilter.Not(ff);
265                }
266            }
267        );
268        registerHandlerFactory(
269            XMLFilterWriter.XML_FILTER_NS,
270            "byParent",
271            new SingleFilterHandlerFactory() {
272                protected FeatureFilter filterToFilter(FeatureFilter ff) {
273                    return new FeatureFilter.ByParent(ff);
274                }
275            }
276        );
277        registerHandlerFactory(
278            XMLFilterWriter.XML_FILTER_NS,
279            "byAncestor",
280            new SingleFilterHandlerFactory() {
281                protected FeatureFilter filterToFilter(FeatureFilter ff) {
282                    return new FeatureFilter.ByAncestor(ff);
283                }
284            }
285        );
286        registerHandlerFactory(
287            XMLFilterWriter.XML_FILTER_NS,
288            "byChild",
289            new SingleFilterHandlerFactory() {
290                protected FeatureFilter filterToFilter(FeatureFilter ff) {
291                    return new FeatureFilter.ByChild(ff);
292                }
293            }
294        );
295        registerHandlerFactory(
296            XMLFilterWriter.XML_FILTER_NS,
297            "byDescendant",
298            new SingleFilterHandlerFactory() {
299                protected FeatureFilter filterToFilter(FeatureFilter ff) {
300                    return new FeatureFilter.ByDescendant(ff);
301                }
302            }
303        );
304        registerHandlerFactory(
305            XMLFilterWriter.XML_FILTER_NS,
306            "onlyChildren",
307            new SingleFilterHandlerFactory() {
308                protected FeatureFilter filterToFilter(FeatureFilter ff) {
309                    return new FeatureFilter.OnlyChildren(ff);
310                }
311            }
312        );
313        registerHandlerFactory(
314            XMLFilterWriter.XML_FILTER_NS,
315            "onlyDescendants",
316            new SingleFilterHandlerFactory() {
317                protected FeatureFilter filterToFilter(FeatureFilter ff) {
318                    return new FeatureFilter.OnlyDescendants(ff);
319                }
320            }
321        );
322        registerHandlerFactory(
323            XMLFilterWriter.XML_FILTER_NS,
324            "and",
325            new FiltersHandlerFactory() {
326                protected FeatureFilter filtersToFilter(List l) {
327                    FeatureFilter ff;
328                    Iterator i = l.iterator();
329                    ff = (FeatureFilter) i.next();
330                    while (i.hasNext()) {
331                        ff = new FeatureFilter.And(ff, (FeatureFilter) i.next());
332                    }
333                    return ff;
334                }
335            }
336        );
337        registerHandlerFactory(
338            XMLFilterWriter.XML_FILTER_NS,
339            "or",
340            new FiltersHandlerFactory() {
341                protected FeatureFilter filtersToFilter(List l) {
342                    FeatureFilter ff;
343                    Iterator i = l.iterator();
344                    ff = (FeatureFilter) i.next();
345                    while (i.hasNext()) {
346                        ff = new FeatureFilter.Or(ff, (FeatureFilter) i.next());
347                    }
348                    return ff;
349                }
350            }
351        );
352        registerHandlerFactory(
353            XMLFilterWriter.XML_FILTER_NS,
354            "overlapsLocation",
355            new LocationHandlerFactory() {
356                protected FeatureFilter locationToFilter(Location loc) {
357                    return new FeatureFilter.OverlapsLocation(loc);
358                }
359            }
360        );
361        registerHandlerFactory(
362            XMLFilterWriter.XML_FILTER_NS,
363            "containedByLocation",
364            new LocationHandlerFactory() {
365                protected FeatureFilter locationToFilter(Location loc) {
366                    return new FeatureFilter.ContainedByLocation(loc);
367                }
368            }
369        );
370        registerHandlerFactory(
371            XMLFilterWriter.XML_FILTER_NS,
372            "shadowOverlapsLocation",
373            new LocationHandlerFactory() {
374                protected FeatureFilter locationToFilter(Location loc) {
375                    return new FeatureFilter.ShadowOverlapsLocation(loc);
376                }
377            }
378        );
379        registerHandlerFactory(
380            XMLFilterWriter.XML_FILTER_NS,
381            "shadowContainedByLocation",
382            new LocationHandlerFactory() {
383                protected FeatureFilter locationToFilter(Location loc) {
384                    return new FeatureFilter.ShadowContainedByLocation(loc);
385                }
386            }
387        );
388        registerHandlerFactory(
389            XMLFilterWriter.XML_FILTER_NS,
390            "byAnnotationType",
391            new AnnotationTypeHandlerFactory()
392        );
393    }
394    
395    private abstract static class SingleFilterHandlerFactory extends FiltersHandlerFactory {
396        protected abstract FeatureFilter filterToFilter(FeatureFilter ff) throws SAXException;
397        protected final FeatureFilter filtersToFilter(List filters)
398            throws SAXException
399        {
400            if (filters.size() != 1) {
401                throw new SAXException("Expected 1 child filter but got " + filters.size());
402            }
403            return filterToFilter((FeatureFilter) filters.get(0));
404        }
405    }
406    
407    private abstract static class FiltersHandlerFactory implements FilterHandlerFactory {
408        private class FiltersHandler extends StAXContentHandlerBase implements FilterHandler {
409            private XMLFilterHandler config;
410            private List filterChildren = new ArrayList();
411            private int depth = 0;
412            
413            public FiltersHandler(XMLFilterHandler config) {
414                super();
415                this.config = config;
416            }
417            
418            public void startElement(String nsURI,
419                                                 String localName,
420                                     String qName,
421                                     Attributes attrs,
422                                     DelegationManager dm)
423                throws SAXException
424            {
425                if (depth == 1) {
426                    FilterHandler childHandler = config.getHandler(nsURI, localName);
427                    dm.delegate(childHandler);
428                }
429                ++depth;
430            }
431
432            public void endElement(String nsURI,
433                                               String localName,
434                                               String qName,
435                                               StAXContentHandler delegate)
436                 throws SAXException
437            {
438                if (delegate instanceof FilterHandler) {
439                    filterChildren.add(((FilterHandler) delegate).getFeatureFilter());
440                }
441                --depth;
442            }
443            
444            public FeatureFilter getFeatureFilter() 
445                throws SAXException
446            {
447                return filtersToFilter(filterChildren);
448            }
449        }
450        
451        protected abstract FeatureFilter filtersToFilter(List filters) throws SAXException;
452        
453        public FilterHandler makeHandler(String nsURI, String localName, XMLFilterHandler config) {
454            return new FiltersHandler(config);
455        }
456    }
457    
458    private abstract static class LocationHandlerFactory implements FilterHandlerFactory {
459        private class LocationHandler extends StAXContentHandlerBase implements FilterHandler {
460            private List spans = new ArrayList();
461            
462            public void startElement(String nsURI,
463                                                 String localName,
464                                     String qName,
465                                     Attributes attrs,
466                                     DelegationManager dm)
467                throws SAXException
468            {
469                if ("span".equals(localName)) {
470                    int start = Integer.parseInt(attrs.getValue("start"));
471                    int stop = Integer.parseInt(attrs.getValue("stop"));
472                    spans.add(new RangeLocation(start, stop));
473                }
474            }
475            
476            public FeatureFilter getFeatureFilter() 
477                throws SAXException
478            {
479                return locationToFilter(LocationTools.union(spans));
480            }
481        }
482        
483        protected abstract FeatureFilter locationToFilter(Location loc) throws SAXException;
484        
485        public FilterHandler makeHandler(String nsURI, String localName, XMLFilterHandler config) {
486            return new LocationHandler();
487        }
488    }
489    
490    private abstract static class CDATAHandlerFactory implements FilterHandlerFactory {
491        private class CDATAHandler extends StringElementHandlerBase implements FilterHandler {
492            private FeatureFilter ourFilter;
493            
494            public FeatureFilter getFeatureFilter() {
495                return ourFilter;
496            }
497            
498            protected void setStringValue(String s) 
499                throws SAXException
500            {
501                ourFilter = stringToFilter(s);
502            }
503        }
504        
505        protected abstract FeatureFilter stringToFilter(String s) throws SAXException;
506        
507        public FilterHandler makeHandler(String nsURI, String localName, XMLFilterHandler config) {
508            return new CDATAHandler();
509        }
510    }
511    
512    private static class AnnotationTypeHandlerFactory implements FilterHandlerFactory {
513        private class AnnotationTypeHandler extends StAXContentHandlerBase implements FilterHandler {
514            private int depth = 0;
515            private FeatureFilter filter;
516            
517            public void startElement(String nsURI,
518                                                 String localName,
519                                     String qName,
520                                     Attributes attrs,
521                                     DelegationManager dm)
522                throws SAXException
523            {
524                if (depth == 1) {
525                    dm.delegate(new XMLAnnotationTypeHandler());
526                }
527                ++depth;
528            }
529
530            public void endElement(String nsURI,
531                                               String localName,
532                                               String qName,
533                                               StAXContentHandler delegate)
534                 throws SAXException
535            {
536                if (delegate instanceof XMLAnnotationTypeHandler) {
537                    filter = new FeatureFilter.ByAnnotationType(((XMLAnnotationTypeHandler) delegate).getAnnotationType());
538                }
539                --depth;
540            }
541            
542            public FeatureFilter getFeatureFilter() {
543                return filter;
544            }
545        }
546        
547        public FilterHandler makeHandler(String nsURI, String localName, XMLFilterHandler config) {
548            return new AnnotationTypeHandler();
549        }
550    }
551    
552    private class AnyFilterHandler extends StAXContentHandlerBase implements FilterHandler {
553        private FeatureFilter filter;
554        
555        public FeatureFilter getFeatureFilter() {
556            return filter;
557        }
558        
559        public void startElement(String nsURI,
560                             String localName,
561                             String qName,
562                             Attributes attrs,
563                             DelegationManager dm)
564            throws SAXException
565        {
566            FilterHandler h = getHandler(nsURI, localName);
567            dm.delegate(h);
568        }
569
570        public void endElement(String nsURI,
571                           String localName,
572                           String qName,
573                           StAXContentHandler delegate)
574            throws SAXException
575        {
576            filter = ((FilterHandler) delegate).getFeatureFilter();
577        }
578    }
579    
580    /**
581     * Return a StAXContentHandler which can deal with any FilterXML construct known to this class.
582     */
583    
584    public FilterHandler getStAXContentHandler() {
585        return new AnyFilterHandler();
586    }
587}