Source for gnu.xml.pipeline.TeeConsumer

   1: /* TeeConsumer.java --
   2:    Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package gnu.xml.pipeline;
  39: 
  40: import org.xml.sax.Attributes;
  41: import org.xml.sax.ContentHandler;
  42: import org.xml.sax.DTDHandler;
  43: import org.xml.sax.ErrorHandler;
  44: import org.xml.sax.Locator;
  45: import org.xml.sax.SAXException;
  46: import org.xml.sax.SAXNotRecognizedException;
  47: import org.xml.sax.ext.DeclHandler;
  48: import org.xml.sax.ext.LexicalHandler;
  49: 
  50: /**
  51:  * Fans its events out to two other consumers, a "tee" filter stage in an
  52:  * event pipeline.  Networks can be assembled with multiple output points.
  53:  *
  54:  * <p> Error handling should be simple if you remember that exceptions
  55:  * you throw will cancel later stages in that callback's pipeline, and
  56:  * generally the producer will stop if it sees such an exception.  You
  57:  * may want to protect your pipeline against such backflows, making a
  58:  * kind of reverse filter (or valve?) so that certain exceptions thrown by
  59:  * your pipeline will caught and handled before the producer sees them.
  60:  * Just use a "try/catch" block, rememebering that really important
  61:  * cleanup tasks should be in "finally" clauses.
  62:  *
  63:  * <p> That issue isn't unique to "tee" consumers, but tee consumers have
  64:  * the additional twist that exceptions thrown by the first consumer
  65:  * will cause the second consumer not to see the callback (except for
  66:  * the endDocument callback, which signals state cleanup).
  67:  *
  68:  * @author David Brownell
  69:  */
  70: final public class TeeConsumer
  71:         implements EventConsumer,
  72:                 ContentHandler, DTDHandler,
  73:                 LexicalHandler,DeclHandler
  74: {
  75:     private EventConsumer       first, rest;
  76: 
  77:     // cached to minimize time overhead
  78:     private ContentHandler      docFirst, docRest;
  79:     private DeclHandler         declFirst, declRest;
  80:     private LexicalHandler      lexFirst, lexRest;
  81: 
  82: 
  83:     /**
  84:      * Constructs a consumer which sends all its events to the first
  85:      * consumer, and then the second one.  If the first consumer throws
  86:      * an exception, the second one will not see the event which
  87:      * caused that exception to be reported.
  88:      *
  89:      * @param car The first consumer to get the events
  90:      * @param cdr The second consumer to get the events
  91:      */
  92:     public TeeConsumer (EventConsumer car, EventConsumer cdr)
  93:     {
  94:         if (car == null || cdr == null)
  95:             throw new NullPointerException ();
  96:         first = car;
  97:         rest = cdr;
  98: 
  99:         //
 100:         // Cache the handlers.
 101:         //
 102:         docFirst = first.getContentHandler ();
 103:         docRest = rest.getContentHandler ();
 104:         // DTD handler isn't cached (rarely needed)
 105: 
 106:         try {
 107:             declFirst = null;
 108:             declFirst = (DeclHandler) first.getProperty (
 109:                         EventFilter.DECL_HANDLER);
 110:         } catch (SAXException e) {}
 111:         try {
 112:             declRest = null;
 113:             declRest = (DeclHandler) rest.getProperty (
 114:                         EventFilter.DECL_HANDLER);
 115:         } catch (SAXException e) {}
 116: 
 117:         try {
 118:             lexFirst = null;
 119:             lexFirst = (LexicalHandler) first.getProperty (
 120:                         EventFilter.LEXICAL_HANDLER);
 121:         } catch (SAXException e) {}
 122:         try {
 123:             lexRest = null;
 124:             lexRest = (LexicalHandler) rest.getProperty (
 125:                         EventFilter.LEXICAL_HANDLER);
 126:         } catch (SAXException e) {}
 127:     }
 128: 
 129: /* FIXME
 130:     /**
 131:      * Constructs a pipeline, and is otherwise a shorthand for the
 132:      * two-consumer constructor for this class.
 133:      *
 134:      * @param first Description of the first pipeline to get events,
 135:      *  which will be passed to {@link PipelineFactory#createPipeline}
 136:      * @param rest The second pipeline to get the events
 137:      * /
 138:         // constructor used by PipelineFactory
 139:     public TeeConsumer (String first, EventConsumer rest)
 140:     throws IOException
 141:     {
 142:         this (PipelineFactory.createPipeline (first), rest);
 143:     }
 144: */
 145: 
 146:     /** Returns the first pipeline to get event calls. */
 147:     public EventConsumer getFirst ()
 148:         { return first; }
 149: 
 150:     /** Returns the second pipeline to get event calls. */
 151:     public EventConsumer getRest ()
 152:         { return rest; }
 153: 
 154:     /** Returns the content handler being used. */
 155:     final public ContentHandler getContentHandler ()
 156:     {
 157:         if (docRest == null)
 158:             return docFirst;
 159:         if (docFirst == null)
 160:             return docRest;
 161:         return this;
 162:     }
 163: 
 164:     /** Returns the dtd handler being used. */
 165:     final public DTDHandler getDTDHandler ()
 166:     {
 167:         // not cached (hardly used)
 168:         if (rest.getDTDHandler () == null)
 169:             return first.getDTDHandler ();
 170:         if (first.getDTDHandler () == null)
 171:             return rest.getDTDHandler ();
 172:         return this;
 173:     }
 174: 
 175:     /** Returns the declaration or lexical handler being used. */
 176:     final public Object getProperty (String id)
 177:     throws SAXNotRecognizedException
 178:     {
 179:         //
 180:         // in degenerate cases, we have no work to do.
 181:         //
 182:         Object  firstProp = null, restProp = null;
 183: 
 184:         try { firstProp = first.getProperty (id); }
 185:         catch (SAXNotRecognizedException e) { /* ignore */ }
 186:         try { restProp = rest.getProperty (id); }
 187:         catch (SAXNotRecognizedException e) { /* ignore */ }
 188: 
 189:         if (restProp == null)
 190:             return firstProp;
 191:         if (firstProp == null)
 192:             return restProp;
 193: 
 194:         //
 195:         // we've got work to do; handle two builtin cases.
 196:         //
 197:         if (EventFilter.DECL_HANDLER.equals (id))
 198:             return this;
 199:         if (EventFilter.LEXICAL_HANDLER.equals (id))
 200:             return this;
 201: 
 202:         //
 203:         // non-degenerate, handled by both consumers, but we don't know
 204:         // how to handle this.
 205:         //
 206:         throw new SAXNotRecognizedException ("can't tee: " + id);
 207:     }
 208: 
 209:     /**
 210:      * Provides the error handler to both subsequent nodes of
 211:      * this filter stage.
 212:      */
 213:     public void setErrorHandler (ErrorHandler handler)
 214:     {
 215:         first.setErrorHandler (handler);
 216:         rest.setErrorHandler (handler);
 217:     }
 218: 
 219: 
 220:     //
 221:     // ContentHandler
 222:     //
 223:     public void setDocumentLocator (Locator locator)
 224:     {
 225:         // this call is not made by all parsers
 226:         docFirst.setDocumentLocator (locator);
 227:         docRest.setDocumentLocator (locator);
 228:     }
 229: 
 230:     public void startDocument ()
 231:     throws SAXException
 232:     {
 233:         docFirst.startDocument ();
 234:         docRest.startDocument ();
 235:     }
 236: 
 237:     public void endDocument ()
 238:     throws SAXException
 239:     {
 240:         try {
 241:             docFirst.endDocument ();
 242:         } finally {
 243:             docRest.endDocument ();
 244:         }
 245:     }
 246: 
 247:     public void startPrefixMapping (String prefix, String uri)
 248:     throws SAXException
 249:     {
 250:         docFirst.startPrefixMapping (prefix, uri);
 251:         docRest.startPrefixMapping (prefix, uri);
 252:     }
 253: 
 254:     public void endPrefixMapping (String prefix)
 255:     throws SAXException
 256:     {
 257:         docFirst.endPrefixMapping (prefix);
 258:         docRest.endPrefixMapping (prefix);
 259:     }
 260: 
 261:     public void skippedEntity (String name)
 262:     throws SAXException
 263:     {
 264:         docFirst.skippedEntity (name);
 265:         docRest.skippedEntity (name);
 266:     }
 267: 
 268:     public void startElement (String uri, String localName,
 269:             String qName, Attributes atts)
 270:     throws SAXException
 271:     {
 272:         docFirst.startElement (uri, localName, qName, atts);
 273:         docRest.startElement (uri, localName, qName, atts);
 274:     }
 275: 
 276:     public void endElement (String uri, String localName, String qName)
 277:     throws SAXException
 278:     {
 279:         docFirst.endElement (uri, localName, qName);
 280:         docRest.endElement (uri, localName, qName);
 281:     }
 282: 
 283:     public void processingInstruction (String target, String data)
 284:     throws SAXException
 285:     {
 286:         docFirst.processingInstruction (target, data);
 287:         docRest.processingInstruction (target, data);
 288:     }
 289: 
 290:     public void characters (char ch [], int start, int length)
 291:     throws SAXException
 292:     {
 293:         docFirst.characters (ch, start, length);
 294:         docRest.characters (ch, start, length);
 295:     }
 296: 
 297:     public void ignorableWhitespace (char ch [], int start, int length)
 298:     throws SAXException
 299:     {
 300:         docFirst.ignorableWhitespace (ch, start, length);
 301:         docRest.ignorableWhitespace (ch, start, length);
 302:     }
 303: 
 304: 
 305:     //
 306:     // DTDHandler
 307:     //
 308:     public void notationDecl (String name, String publicId, String systemId)
 309:     throws SAXException
 310:     {
 311:         DTDHandler      l1 = first.getDTDHandler ();
 312:         DTDHandler      l2 = rest.getDTDHandler ();
 313: 
 314:         l1.notationDecl (name, publicId, systemId);
 315:         l2.notationDecl (name, publicId, systemId);
 316:     }
 317: 
 318:     public void unparsedEntityDecl (String name,
 319:             String publicId, String systemId,
 320:             String notationName
 321:     ) throws SAXException
 322:     {
 323:         DTDHandler      l1 = first.getDTDHandler ();
 324:         DTDHandler      l2 = rest.getDTDHandler ();
 325: 
 326:         l1.unparsedEntityDecl (name, publicId, systemId, notationName);
 327:         l2.unparsedEntityDecl (name, publicId, systemId, notationName);
 328:     }
 329: 
 330: 
 331:     //
 332:     // DeclHandler
 333:     //
 334:     public void attributeDecl (String eName, String aName,
 335:         String type,
 336:         String mode, String value)
 337:     throws SAXException
 338:     {
 339:         declFirst.attributeDecl (eName, aName, type, mode, value);
 340:         declRest.attributeDecl (eName, aName, type, mode, value);
 341:     }
 342: 
 343:     public void elementDecl (String name, String model)
 344:     throws SAXException
 345:     {
 346:         declFirst.elementDecl (name, model);
 347:         declRest.elementDecl (name, model);
 348:     }
 349: 
 350:     public void externalEntityDecl (String name,
 351:         String publicId, String systemId)
 352:     throws SAXException
 353:     {
 354:         declFirst.externalEntityDecl (name, publicId, systemId);
 355:         declRest.externalEntityDecl (name, publicId, systemId);
 356:     }
 357: 
 358:     public void internalEntityDecl (String name, String value)
 359:     throws SAXException
 360:     {
 361:         declFirst.internalEntityDecl (name, value);
 362:         declRest.internalEntityDecl (name, value);
 363:     }
 364: 
 365: 
 366:     //
 367:     // LexicalHandler
 368:     //
 369:     public void comment (char ch [], int start, int length)
 370:     throws SAXException
 371:     {
 372:         lexFirst.comment (ch, start, length);
 373:         lexRest.comment (ch, start, length);
 374:     }
 375: 
 376:     public void startCDATA ()
 377:     throws SAXException
 378:     {
 379:         lexFirst.startCDATA ();
 380:         lexRest.startCDATA ();
 381:     }
 382: 
 383:     public void endCDATA ()
 384:     throws SAXException
 385:     {
 386:         lexFirst.endCDATA ();
 387:         lexRest.endCDATA ();
 388:     }
 389: 
 390:     public void startEntity (String name)
 391:     throws SAXException
 392:     {
 393:         lexFirst.startEntity (name);
 394:         lexRest.startEntity (name);
 395:     }
 396: 
 397:     public void endEntity (String name)
 398:     throws SAXException
 399:     {
 400:         lexFirst.endEntity (name);
 401:         lexRest.endEntity (name);
 402:     }
 403: 
 404:     public void startDTD (String name, String publicId, String systemId)
 405:     throws SAXException
 406:     {
 407:         lexFirst.startDTD (name, publicId, systemId);
 408:         lexRest.startDTD (name, publicId, systemId);
 409:     }
 410: 
 411:     public void endDTD ()
 412:     throws SAXException
 413:     {
 414:         lexFirst.endDTD ();
 415:         lexRest.endDTD ();
 416:     }
 417: }