Source for gnu.xml.dom.ls.DomLSParser

   1: /* DomLSParser.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.dom.ls;
  39: 
  40: import java.io.File;
  41: import java.io.InputStream;
  42: import java.io.IOException;
  43: import java.net.MalformedURLException;
  44: import java.net.URL;
  45: import java.util.Arrays;
  46: import java.util.List;
  47: import javax.xml.parsers.ParserConfigurationException;
  48: import javax.xml.parsers.SAXParser;
  49: import javax.xml.parsers.SAXParserFactory;
  50: import org.w3c.dom.Document;
  51: import org.w3c.dom.DOMConfiguration;
  52: import org.w3c.dom.DOMException;
  53: import org.w3c.dom.DOMStringList;
  54: import org.w3c.dom.Node;
  55: import org.w3c.dom.ls.DOMImplementationLS;
  56: import org.w3c.dom.ls.LSException;
  57: import org.w3c.dom.ls.LSInput;
  58: import org.w3c.dom.ls.LSParser;
  59: import org.w3c.dom.ls.LSParserFilter;
  60: import org.xml.sax.EntityResolver;
  61: import org.xml.sax.ErrorHandler;
  62: import org.xml.sax.InputSource;
  63: import org.xml.sax.SAXException;
  64: import org.xml.sax.SAXNotRecognizedException;
  65: import org.xml.sax.SAXParseException;
  66: import org.xml.sax.XMLReader;
  67: import gnu.xml.dom.DomDocument;
  68: import gnu.xml.dom.DomDOMException;
  69: 
  70: /**
  71:  * Parser implementation for GNU DOM.
  72:  *
  73:  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
  74:  */
  75: public class DomLSParser
  76:   implements LSParser, DOMConfiguration, DOMStringList, ErrorHandler
  77: {
  78: 
  79:   private static final List SUPPORTED_PARAMETERS
  80:     = Arrays.asList(new String[] { "cdata-sections",
  81:                     "comments",
  82:                     "element-content-whitespace",
  83:                     "namespaces",
  84:                     "expand-entity-references",
  85:                     "coalescing",
  86:                     "validating",
  87:                     "xinclude-aware",
  88:                     "entity-resolver",
  89:                     "error-handler" });
  90: 
  91:   private LSParserFilter filter;
  92:   private final boolean async;
  93:   private String schemaType;
  94:   private SAXEventSink eventSink;
  95:   private SAXParserFactory factory;
  96:   private XMLReader reader;
  97: 
  98:   private boolean namespaceAware = true;
  99:   private boolean ignoreWhitespace;
 100:   private boolean expandEntityReferences;
 101:   private boolean ignoreComments;
 102:   private boolean coalescing;
 103:   private boolean validating;
 104:   private boolean xIncludeAware;
 105:   private EntityResolver entityResolver;
 106:   private ErrorHandler errorHandler;
 107: 
 108:   public DomLSParser(short mode, String schemaType)
 109:     throws DOMException
 110:   {
 111:     switch (mode)
 112:       {
 113:       case DOMImplementationLS.MODE_ASYNCHRONOUS:
 114:         async = true;
 115:         break;
 116:       case DOMImplementationLS.MODE_SYNCHRONOUS:
 117:         async = false;
 118:         break;
 119:       default:
 120:         throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR);
 121:       }
 122:     // TODO schemaType
 123:     this.schemaType = schemaType;
 124:     factory = SAXParserFactory.newInstance();
 125:   }
 126: 
 127:   // -- LSParser --
 128:   
 129:   public DOMConfiguration getDomConfig()
 130:   {
 131:     return this;
 132:   }
 133:   
 134:   public LSParserFilter getFilter()
 135:   {
 136:     return filter;
 137:   }
 138: 
 139:   public void setFilter(LSParserFilter filter)
 140:   {
 141:     this.filter = filter;
 142:   }
 143: 
 144:   public boolean getAsync()
 145:   {
 146:     return async;
 147:   }
 148: 
 149:   public boolean getBusy()
 150:   {
 151:     return eventSink != null;
 152:   }
 153: 
 154:   public Document parse(LSInput input)
 155:     throws DOMException, LSException
 156:   {
 157:     if (async)
 158:       {
 159:         return doParse(input);
 160:       }
 161:     else
 162:       {
 163:         synchronized (this)
 164:           {
 165:             return doParse(input);
 166:           }
 167:       }
 168:   }
 169: 
 170:   public Document parseURI(String uri)
 171:     throws DOMException, LSException
 172:   {
 173:     LSInput input = new DomLSInput();
 174:     input.setSystemId(uri);
 175:     return parse(input);
 176:   }
 177: 
 178:   public Node parseWithContext(LSInput input, Node context, short action)
 179:     throws DOMException, LSException
 180:   {
 181:     Document doc = (context.getNodeType() == Node.DOCUMENT_NODE) ?
 182:       (Document) context : context.getOwnerDocument();
 183:     input.setBaseURI(doc.getDocumentURI());
 184:     // TODO use namespaces defined on context node
 185:     Document ret = parse(input);
 186:     Node root = ret.getDocumentElement();
 187:     root = doc.adoptNode(root);
 188:     switch (action)
 189:       {
 190:       case ACTION_APPEND_AS_CHILDREN:
 191:         context.appendChild(root);
 192:         break;
 193:       case ACTION_REPLACE_CHILDREN:
 194:         Node c1 = context.getFirstChild();
 195:         while (c1 != null)
 196:           {
 197:             Node next = c1.getNextSibling();
 198:             context.removeChild(c1);
 199:             c1 = next;
 200:           }
 201:         context.appendChild(root);
 202:         break;
 203:       case ACTION_INSERT_BEFORE:
 204:         Node p1 = context.getParentNode();
 205:         p1.insertBefore(root, context);
 206:         break;
 207:       case ACTION_INSERT_AFTER:
 208:         Node p2 = context.getParentNode();
 209:         Node r1 = context.getNextSibling();
 210:         if (r1 == null)
 211:           {
 212:             p2.appendChild(root);
 213:           }
 214:         else
 215:           {
 216:             p2.insertBefore(root, r1);
 217:           }
 218:         break;
 219:       case ACTION_REPLACE:
 220:         Node p3 = context.getParentNode();
 221:         Node r2 = context.getNextSibling();
 222:         p3.removeChild(context);
 223:         if (r2 == null)
 224:           {
 225:             p3.appendChild(root);
 226:           }
 227:         else
 228:           {
 229:             p3.insertBefore(root, r2);
 230:           }
 231:         break;
 232:       }
 233:     return root;
 234:   }
 235: 
 236:   public void abort()
 237:   {
 238:     if (eventSink != null)
 239:       {
 240:         eventSink.interrupt();
 241:       }
 242:   }
 243: 
 244:   private Document doParse(LSInput input)
 245:     throws DOMException, LSException
 246:   {
 247:     // create event sink
 248:     if (eventSink != null)
 249:       {
 250:         throw new LSException(LSException.PARSE_ERR, "parse in progress");
 251:       }
 252:     InputSource source = getInputSource(input);
 253:     eventSink = (filter == null) ? new SAXEventSink() :
 254:       new FilteredSAXEventSink(filter);
 255:     // configure sink
 256:     eventSink.namespaceAware = namespaceAware;
 257:     eventSink.ignoreWhitespace = ignoreWhitespace;
 258:     eventSink.expandEntityReferences = expandEntityReferences;
 259:     eventSink.ignoreComments = ignoreComments;
 260:     eventSink.coalescing = coalescing;
 261:     // get and configure reader
 262:     XMLReader reader = getXMLReader();
 263:     eventSink.reader = reader;
 264:     try
 265:       {
 266:         reader.setContentHandler(eventSink);
 267:         reader.setDTDHandler(eventSink);
 268:         reader.setProperty("http://xml.org/sax/properties/lexical-handler",
 269:                            eventSink);
 270:         reader.setProperty("http://xml.org/sax/properties/declaration-handler",
 271:                            eventSink);
 272:         reader.setFeature("http://xml.org/sax/features/namespaces",
 273:                           namespaceAware);
 274:         reader.setFeature("http://xml.org/sax/features/namespace-prefixes",
 275:                           true);
 276:         reader.setFeature("http://xml.org/sax/features/validation",
 277:                           validating);
 278:         try
 279:           {
 280:             reader.setFeature("http://xml.org/sax/features/use-attributes2",
 281:                               true);
 282:           }
 283:         catch (SAXNotRecognizedException e)
 284:           {
 285:             // ignore
 286:           }
 287:         try
 288:           {
 289:             reader.setFeature("http://xml.org/sax/features/external-general-entities",
 290:                               true);
 291:           }
 292:         catch (SAXNotRecognizedException e)
 293:           {
 294:             // ignore
 295:           }
 296:         reader.setEntityResolver(entityResolver);
 297:         reader.setErrorHandler(errorHandler);
 298:         // parse
 299:         reader.parse(source);
 300:       }
 301:     catch (DOMException e)
 302:       {
 303:         reader = null;
 304:         eventSink = null;
 305:         throw e;
 306:       }
 307:     catch (SAXException e)
 308:       {
 309:         reader = null;
 310:         eventSink = null;
 311:         throw new DomLSException(LSException.PARSE_ERR, e);
 312:       }
 313:     catch (IOException e)
 314:       {
 315:         reader = null;
 316:         eventSink = null;
 317:         throw new DomLSException(LSException.PARSE_ERR, e);
 318:       }
 319:     // return document
 320:     Document ret = eventSink.doc;
 321:     String systemId = input.getSystemId();
 322:     if (systemId != null && ret instanceof DomDocument)
 323:       {
 324:         ((DomDocument) ret).setDocumentURI(systemId);
 325:       }
 326:     eventSink = null;
 327:     return ret;
 328:   }
 329: 
 330:   private XMLReader getXMLReader()
 331:     throws LSException
 332:   {
 333:     if (reader == null)
 334:       {
 335:         factory.setNamespaceAware(namespaceAware);
 336:         factory.setValidating(validating);
 337:         factory.setXIncludeAware(xIncludeAware);
 338:         try
 339:           {
 340:             SAXParser parser = factory.newSAXParser();
 341:             reader = parser.getXMLReader();
 342:           }
 343:         catch (ParserConfigurationException e)
 344:           {
 345:             throw new DomLSException(LSException.PARSE_ERR, e);
 346:           }
 347:         catch (SAXException e)
 348:           {
 349:             throw new DomLSException(LSException.PARSE_ERR, e);
 350:           }
 351:       }
 352:     return reader;
 353:   }
 354: 
 355:   private InputSource getInputSource(LSInput input)
 356:     throws LSException
 357:   {
 358:     InputSource source = null;
 359:     String systemId = input.getSystemId();
 360:     InputStream in = input.getByteStream();
 361:     if (in != null)
 362:       {
 363:         source = new InputSource(in);
 364:         source.setSystemId(systemId);
 365:       }
 366:     if (source == null && entityResolver != null)
 367:       {
 368:         String publicId = input.getPublicId();
 369:         try
 370:           {
 371:             source = entityResolver.resolveEntity(publicId, systemId);
 372:           }
 373:         catch (SAXException e)
 374:           {
 375:             throw new DomLSException(LSException.PARSE_ERR, e);
 376:           } 
 377:         catch (IOException e)
 378:           {
 379:             throw new DomLSException(LSException.PARSE_ERR, e);
 380:           } 
 381:       }
 382:     if (source == null)
 383:       {
 384:         URL url = null;
 385:         String base = input.getBaseURI();
 386:         try
 387:           {
 388:             try
 389:               {
 390:                 URL baseURL = (base == null) ? null : new URL(base);
 391:                 url = (baseURL == null) ? new URL(systemId) :
 392:                   new URL(baseURL, systemId);
 393:               }
 394:             catch (MalformedURLException e)
 395:               {
 396:                 File baseFile = (base == null) ? null : new File(base);
 397:                 url = (baseFile == null) ? new File(systemId).toURL() :
 398:                   new File(baseFile, systemId).toURL();
 399:               }
 400:             in = url.openStream();
 401:             systemId = url.toString();
 402:             source = new InputSource(in);
 403:             source.setSystemId(systemId);
 404:           }
 405:         catch (IOException e)
 406:           {
 407:             throw new DomLSException(LSException.PARSE_ERR, e);
 408:           }
 409:       }
 410:     return source;
 411:   }
 412: 
 413:   // -- DOMConfiguration --
 414: 
 415:   public void setParameter(String name, Object value)
 416:     throws DOMException
 417:   {
 418:     name = name.toLowerCase();
 419:     if ("cdata-sections".equals(name))
 420:       {
 421:         coalescing = !((Boolean) value).booleanValue();
 422:       }
 423:     else if ("comments".equals(name))
 424:       {
 425:         ignoreComments = !((Boolean) value).booleanValue();
 426:       }
 427:     else if ("element-content-whitespace".equals(name))
 428:       {
 429:         ignoreWhitespace = !((Boolean) value).booleanValue();
 430:       }
 431:     else if ("namespaces".equals(name))
 432:       {
 433:         namespaceAware = ((Boolean) value).booleanValue();
 434:       }
 435:     else if ("expand-entity-references".equals(name))
 436:       {
 437:         expandEntityReferences = ((Boolean) value).booleanValue();
 438:       }
 439:     else if ("coalescing".equals(name))
 440:       {
 441:         coalescing = ((Boolean) value).booleanValue();
 442:       }
 443:     else if ("validating".equals(name))
 444:       {
 445:         validating = ((Boolean) value).booleanValue();
 446:       }
 447:     else if ("xinclude-aware".equals(name))
 448:       {
 449:         xIncludeAware = ((Boolean) value).booleanValue();
 450:       }
 451:     else if ("entity-resolver".equals(name))
 452:       {
 453:         entityResolver = (EntityResolver) value;
 454:       }
 455:     else if ("error-handler".equals(name))
 456:       {
 457:         errorHandler = (ErrorHandler) value;
 458:       }
 459:     else
 460:       {
 461:         throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR);
 462:       }
 463:     // invalidate reader, a new one will be created
 464:     reader = null;
 465:   }
 466: 
 467:   public Object getParameter(String name)
 468:     throws DOMException
 469:   {
 470:     name = name.toLowerCase();
 471:     if ("cdata-sections".equals(name))
 472:       {
 473:         return coalescing ? Boolean.FALSE : Boolean.TRUE;
 474:       }
 475:     else if ("comments".equals(name))
 476:       {
 477:         return ignoreComments ? Boolean.FALSE : Boolean.TRUE;
 478:       }
 479:     else if ("element-content-whitespace".equals(name))
 480:       {
 481:         return ignoreWhitespace ? Boolean.FALSE : Boolean.TRUE;
 482:       }
 483:     else if ("namespaces".equals(name))
 484:       {
 485:         return namespaceAware ? Boolean.TRUE : Boolean.FALSE;
 486:       }
 487:     else if ("expand-entity-references".equals(name))
 488:       {
 489:         return expandEntityReferences ? Boolean.TRUE : Boolean.FALSE;
 490:       }
 491:     else if ("coalescing".equals(name))
 492:       {
 493:         return coalescing ? Boolean.TRUE : Boolean.FALSE;
 494:       }
 495:     else if ("validating".equals(name))
 496:       {
 497:         return validating ? Boolean.TRUE : Boolean.FALSE;
 498:       }
 499:     else if ("xinclude-aware".equals(name))
 500:       {
 501:         return xIncludeAware ? Boolean.TRUE : Boolean.FALSE;
 502:       }
 503:     else if ("entity-resolver".equals(name))
 504:       {
 505:         return entityResolver;
 506:       }
 507:     else if ("error-handler".equals(name))
 508:       {
 509:         return errorHandler;
 510:       }
 511:     else
 512:       {
 513:         throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR);
 514:       }
 515:   }
 516: 
 517:   public boolean canSetParameter(String name, Object value)
 518:   {
 519:     return contains(name);
 520:   }
 521: 
 522:   public DOMStringList getParameterNames()
 523:   {
 524:     return this;
 525:   }
 526: 
 527:   // -- DOMStringList --
 528: 
 529:   public String item(int i)
 530:   {
 531:     return (String) SUPPORTED_PARAMETERS.get(i);
 532:   }
 533: 
 534:   public int getLength()
 535:   {
 536:     return SUPPORTED_PARAMETERS.size();
 537:   }
 538: 
 539:   public boolean contains(String str)
 540:   {
 541:     return SUPPORTED_PARAMETERS.contains(str);
 542:   }
 543: 
 544:   // -- ErrorHandler --
 545: 
 546:   public void warning(SAXParseException e)
 547:     throws SAXException
 548:   {
 549:     if (errorHandler != null)
 550:       {
 551:         errorHandler.warning(e);
 552:       }
 553:   }
 554: 
 555:   public void error(SAXParseException e)
 556:     throws SAXException
 557:   {
 558:     if (errorHandler != null)
 559:       {
 560:         errorHandler.error(e);
 561:       }
 562:   }
 563: 
 564:   public void fatalError(SAXParseException e)
 565:     throws SAXException
 566:   {
 567:     if (errorHandler != null)
 568:       {
 569:         errorHandler.fatalError(e);
 570:       }
 571:     abort();
 572:   }
 573:   
 574: }