1 /*
   2  * Licensed to the Apache Software Foundation (ASF) under one or more
   3  * contributor license agreements.  See the NOTICE file distributed with
   4  * this work for additional information regarding copyright ownership.
   5  * The ASF licenses this file to You under the Apache License, Version 2.0
   6  * (the "License"); you may not use this file except in compliance with
   7  * the License.  You may obtain a copy of the License at
   8  *
   9  *      http://www.apache.org/licenses/LICENSE-2.0
  10  *
  11  * Unless required by applicable law or agreed to in writing, software
  12  * distributed under the License is distributed on an "AS IS" BASIS,
  13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14  * See the License for the specific language governing permissions and
  15  * limitations under the License.
  16  */
  17 
  18 package com.sun.org.apache.xml.internal.resolver.readers;
  19 
  20 import com.sun.org.apache.xml.internal.resolver.Catalog;
  21 import com.sun.org.apache.xml.internal.resolver.CatalogEntry;
  22 import com.sun.org.apache.xml.internal.resolver.CatalogException;
  23 import com.sun.org.apache.xml.internal.resolver.helpers.PublicId;
  24 import java.util.Enumeration;
  25 import java.util.Stack;
  26 import java.util.Vector;
  27 import javax.xml.parsers.SAXParserFactory;
  28 import org.w3c.dom.*;
  29 import org.xml.sax.*;
  30 
  31 /**
  32  * Parse OASIS Entity Resolution Technical Committee
  33  * XML Catalog files.
  34  *
  35  * @see Catalog
  36  *
  37  * @author Norman Walsh
  38  * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a>
  39  *
  40  */
  41 public class OASISXMLCatalogReader extends SAXCatalogReader implements SAXCatalogParser {
  42   /** The catalog object needs to be stored by the object so that
  43    * SAX callbacks can use it.
  44    */
  45   protected Catalog catalog = null;
  46 
  47   /** The namespace name of OASIS ERTC catalogs */
  48   public static final String namespaceName = "urn:oasis:names:tc:entity:xmlns:xml:catalog";
  49 
  50   /** The namespace name of OASIS ERTC TR9401 catalog extension */
  51   public static final String tr9401NamespaceName = "urn:oasis:names:tc:entity:xmlns:tr9401:catalog";
  52 
  53   protected Stack baseURIStack = new Stack();
  54   protected Stack overrideStack = new Stack();
  55   protected Stack namespaceStack = new Stack();
  56 
  57   /** Set the current catalog. */
  58   public void setCatalog (Catalog catalog) {
  59     this.catalog = catalog;
  60     debug = catalog.getCatalogManager().debug;
  61   }
  62 
  63   /** Get the current catalog. */
  64   public Catalog getCatalog () {
  65     return catalog;
  66   }
  67 
  68   /** Default constructor */
  69   public OASISXMLCatalogReader() {
  70     super();
  71   }
  72 
  73   /** Constructor allowing for providing custom SAX parser factory */
  74   public OASISXMLCatalogReader(SAXParserFactory parserFactory, Catalog catalog) {
  75     super(parserFactory);
  76     setCatalog(catalog);
  77   }
  78 
  79   /**
  80    * Are we in an extension namespace?
  81    *
  82    * @return true if the current stack of open namespaces includes
  83    *               an extension namespace.
  84    */
  85   protected boolean inExtensionNamespace() {
  86     boolean inExtension = false;
  87 
  88     Enumeration elements = namespaceStack.elements();
  89     while (!inExtension && elements.hasMoreElements()) {
  90       String ns = (String) elements.nextElement();
  91       if (ns == null) {
  92         inExtension = true;
  93       } else {
  94         inExtension = (!ns.equals(tr9401NamespaceName)
  95                        && !ns.equals(namespaceName));
  96       }
  97     }
  98 
  99     return inExtension;
 100   }
 101 
 102   // ----------------------------------------------------------------------
 103   // Implement the SAX ContentHandler interface
 104 
 105   /** The SAX <code>setDocumentLocator</code> method does nothing. */
 106   public void setDocumentLocator (Locator locator) {
 107     return;
 108   }
 109 
 110   /** The SAX <code>startDocument</code> */
 111   public void startDocument ()
 112     throws SAXException {
 113     baseURIStack.push(catalog.getCurrentBase());
 114     overrideStack.push(catalog.getDefaultOverride());
 115     return;
 116   }
 117 
 118   /** The SAX <code>endDocument</code> method does nothing. */
 119   public void endDocument ()
 120     throws SAXException {
 121     return;
 122   }
 123 
 124   /**
 125    * The SAX <code>startElement</code> method recognizes elements
 126    * from the plain catalog format and instantiates CatalogEntry
 127    * objects for them.
 128    *
 129    * @param namespaceURI The namespace name of the element.
 130    * @param localName The local name of the element.
 131    * @param qName The QName of the element.
 132    * @param atts The list of attributes on the element.
 133    *
 134    * @see CatalogEntry
 135    */
 136   public void startElement (String namespaceURI,
 137                             String localName,
 138                             String qName,
 139                             Attributes atts)
 140     throws SAXException {
 141 
 142     int entryType = -1;
 143     Vector entryArgs = new Vector();
 144 
 145     namespaceStack.push(namespaceURI);
 146 
 147     boolean inExtension = inExtensionNamespace();
 148 
 149     if (namespaceURI != null && namespaceName.equals(namespaceURI)
 150         && !inExtension) {
 151       // This is an XML Catalog entry
 152 
 153       if (atts.getValue("xml:base") != null) {
 154         String baseURI = atts.getValue("xml:base");
 155         entryType = Catalog.BASE;
 156         entryArgs.add(baseURI);
 157         baseURIStack.push(baseURI);
 158 
 159         debug.message(4, "xml:base", baseURI);
 160 
 161         try {
 162           CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
 163           catalog.addEntry(ce);
 164         } catch (CatalogException cex) {
 165           if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
 166             debug.message(1, "Invalid catalog entry type", localName);
 167           } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
 168             debug.message(1, "Invalid catalog entry (base)", localName);
 169           }
 170         }
 171 
 172         entryType = -1;
 173         entryArgs = new Vector();
 174 
 175       } else {
 176         baseURIStack.push(baseURIStack.peek());
 177       }
 178 
 179       if ((localName.equals("catalog") || localName.equals("group"))
 180           && atts.getValue("prefer") != null) {
 181         String override = atts.getValue("prefer");
 182 
 183         if (override.equals("public")) {
 184           override = "yes";
 185         } else if (override.equals("system")) {
 186           override = "no";
 187         } else {
 188           debug.message(1,
 189                         "Invalid prefer: must be 'system' or 'public'",
 190                         localName);
 191           override = catalog.getDefaultOverride();
 192         }
 193 
 194         entryType = Catalog.OVERRIDE;
 195         entryArgs.add(override);
 196         overrideStack.push(override);
 197 
 198         debug.message(4, "override", override);
 199 
 200         try {
 201           CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
 202           catalog.addEntry(ce);
 203         } catch (CatalogException cex) {
 204           if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
 205             debug.message(1, "Invalid catalog entry type", localName);
 206           } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
 207             debug.message(1, "Invalid catalog entry (override)", localName);
 208           }
 209         }
 210 
 211         entryType = -1;
 212         entryArgs = new Vector();
 213 
 214       } else {
 215         overrideStack.push(overrideStack.peek());
 216       }
 217 
 218       if (localName.equals("delegatePublic")) {
 219         if (checkAttributes(atts, "publicIdStartString", "catalog")) {
 220           entryType = Catalog.DELEGATE_PUBLIC;
 221           entryArgs.add(atts.getValue("publicIdStartString"));
 222           entryArgs.add(atts.getValue("catalog"));
 223 
 224           debug.message(4, "delegatePublic",
 225                         PublicId.normalize(atts.getValue("publicIdStartString")),
 226                         atts.getValue("catalog"));
 227         }
 228       } else if (localName.equals("delegateSystem")) {
 229         if (checkAttributes(atts, "systemIdStartString", "catalog")) {
 230           entryType = Catalog.DELEGATE_SYSTEM;
 231           entryArgs.add(atts.getValue("systemIdStartString"));
 232           entryArgs.add(atts.getValue("catalog"));
 233 
 234           debug.message(4, "delegateSystem",
 235                         atts.getValue("systemIdStartString"),
 236                         atts.getValue("catalog"));
 237         }
 238       } else if (localName.equals("delegateURI")) {
 239         if (checkAttributes(atts, "uriStartString", "catalog")) {
 240           entryType = Catalog.DELEGATE_URI;
 241           entryArgs.add(atts.getValue("uriStartString"));
 242           entryArgs.add(atts.getValue("catalog"));
 243 
 244           debug.message(4, "delegateURI",
 245                         atts.getValue("uriStartString"),
 246                         atts.getValue("catalog"));
 247         }
 248       } else if (localName.equals("rewriteSystem")) {
 249         if (checkAttributes(atts, "systemIdStartString", "rewritePrefix")) {
 250           entryType = Catalog.REWRITE_SYSTEM;
 251           entryArgs.add(atts.getValue("systemIdStartString"));
 252           entryArgs.add(atts.getValue("rewritePrefix"));
 253 
 254           debug.message(4, "rewriteSystem",
 255                         atts.getValue("systemIdStartString"),
 256                         atts.getValue("rewritePrefix"));
 257         }
 258       } else if (localName.equals("systemSuffix")) {
 259         if (checkAttributes(atts, "systemIdSuffix", "uri")) {
 260           entryType = Catalog.SYSTEM_SUFFIX;
 261           entryArgs.add(atts.getValue("systemIdSuffix"));
 262           entryArgs.add(atts.getValue("uri"));
 263 
 264           debug.message(4, "systemSuffix",
 265                         atts.getValue("systemIdSuffix"),
 266                         atts.getValue("uri"));
 267         }
 268       } else if (localName.equals("rewriteURI")) {
 269         if (checkAttributes(atts, "uriStartString", "rewritePrefix")) {
 270           entryType = Catalog.REWRITE_URI;
 271           entryArgs.add(atts.getValue("uriStartString"));
 272           entryArgs.add(atts.getValue("rewritePrefix"));
 273 
 274           debug.message(4, "rewriteURI",
 275                         atts.getValue("uriStartString"),
 276                         atts.getValue("rewritePrefix"));
 277         }
 278       } else if (localName.equals("uriSuffix")) {
 279         if (checkAttributes(atts, "uriSuffix", "uri")) {
 280           entryType = Catalog.URI_SUFFIX;
 281           entryArgs.add(atts.getValue("uriSuffix"));
 282           entryArgs.add(atts.getValue("uri"));
 283 
 284           debug.message(4, "uriSuffix",
 285                         atts.getValue("uriSuffix"),
 286                         atts.getValue("uri"));
 287         }
 288       } else if (localName.equals("nextCatalog")) {
 289         if (checkAttributes(atts, "catalog")) {
 290           entryType = Catalog.CATALOG;
 291           entryArgs.add(atts.getValue("catalog"));
 292 
 293           debug.message(4, "nextCatalog", atts.getValue("catalog"));
 294         }
 295       } else if (localName.equals("public")) {
 296         if (checkAttributes(atts, "publicId", "uri")) {
 297           entryType = Catalog.PUBLIC;
 298           entryArgs.add(atts.getValue("publicId"));
 299           entryArgs.add(atts.getValue("uri"));
 300 
 301           debug.message(4, "public",
 302                         PublicId.normalize(atts.getValue("publicId")),
 303                         atts.getValue("uri"));
 304         }
 305       } else if (localName.equals("system")) {
 306         if (checkAttributes(atts, "systemId", "uri")) {
 307           entryType = Catalog.SYSTEM;
 308           entryArgs.add(atts.getValue("systemId"));
 309           entryArgs.add(atts.getValue("uri"));
 310 
 311           debug.message(4, "system",
 312                         atts.getValue("systemId"),
 313                         atts.getValue("uri"));
 314         }
 315       } else if (localName.equals("uri")) {
 316         if (checkAttributes(atts, "name", "uri")) {
 317           entryType = Catalog.URI;
 318           entryArgs.add(atts.getValue("name"));
 319           entryArgs.add(atts.getValue("uri"));
 320 
 321           debug.message(4, "uri",
 322                         atts.getValue("name"),
 323                         atts.getValue("uri"));
 324         }
 325       } else if (localName.equals("catalog")) {
 326         // nop, start of catalog
 327       } else if (localName.equals("group")) {
 328         // nop, a group
 329       } else {
 330         // This is equivalent to an invalid catalog entry type
 331         debug.message(1, "Invalid catalog entry type", localName);
 332       }
 333 
 334       if (entryType >= 0) {
 335         try {
 336           CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
 337           catalog.addEntry(ce);
 338         } catch (CatalogException cex) {
 339           if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
 340             debug.message(1, "Invalid catalog entry type", localName);
 341           } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
 342             debug.message(1, "Invalid catalog entry", localName);
 343           }
 344         }
 345       }
 346     }
 347 
 348     if (namespaceURI != null && tr9401NamespaceName.equals(namespaceURI)
 349         && !inExtension) {
 350       // This is a TR9401 Catalog entry
 351 
 352       if (atts.getValue("xml:base") != null) {
 353         String baseURI = atts.getValue("xml:base");
 354         entryType = Catalog.BASE;
 355         entryArgs.add(baseURI);
 356         baseURIStack.push(baseURI);
 357 
 358         debug.message(4, "xml:base", baseURI);
 359 
 360         try {
 361           CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
 362           catalog.addEntry(ce);
 363         } catch (CatalogException cex) {
 364           if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
 365             debug.message(1, "Invalid catalog entry type", localName);
 366           } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
 367             debug.message(1, "Invalid catalog entry (base)", localName);
 368           }
 369         }
 370 
 371         entryType = -1;
 372         entryArgs = new Vector();
 373 
 374       } else {
 375         baseURIStack.push(baseURIStack.peek());
 376       }
 377 
 378       if (localName.equals("doctype")) {
 379         entryType = Catalog.DOCTYPE;
 380         entryArgs.add(atts.getValue("name"));
 381         entryArgs.add(atts.getValue("uri"));
 382       } else if (localName.equals("document")) {
 383         entryType = Catalog.DOCUMENT;
 384         entryArgs.add(atts.getValue("uri"));
 385       } else if (localName.equals("dtddecl")) {
 386         entryType = Catalog.DTDDECL;
 387         entryArgs.add(atts.getValue("publicId"));
 388         entryArgs.add(atts.getValue("uri"));
 389       } else if (localName.equals("entity")) {
 390         entryType = Catalog.ENTITY;
 391         entryArgs.add(atts.getValue("name"));
 392         entryArgs.add(atts.getValue("uri"));
 393       } else if (localName.equals("linktype")) {
 394         entryType = Catalog.LINKTYPE;
 395         entryArgs.add(atts.getValue("name"));
 396         entryArgs.add(atts.getValue("uri"));
 397       } else if (localName.equals("notation")) {
 398         entryType = Catalog.NOTATION;
 399         entryArgs.add(atts.getValue("name"));
 400         entryArgs.add(atts.getValue("uri"));
 401       } else if (localName.equals("sgmldecl")) {
 402         entryType = Catalog.SGMLDECL;
 403         entryArgs.add(atts.getValue("uri"));
 404       } else {
 405         // This is equivalent to an invalid catalog entry type
 406         debug.message(1, "Invalid catalog entry type", localName);
 407       }
 408 
 409       if (entryType >= 0) {
 410         try {
 411           CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
 412           catalog.addEntry(ce);
 413         } catch (CatalogException cex) {
 414           if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
 415             debug.message(1, "Invalid catalog entry type", localName);
 416           } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
 417             debug.message(1, "Invalid catalog entry", localName);
 418           }
 419         }
 420       }
 421     }
 422   }
 423 
 424   public boolean checkAttributes (Attributes atts, String attName) {
 425     if (atts.getValue(attName) == null) {
 426       debug.message(1, "Error: required attribute " + attName + " missing.");
 427       return false;
 428     } else {
 429       return true;
 430     }
 431   }
 432 
 433   public boolean checkAttributes (Attributes atts,
 434                                   String attName1,
 435                                   String attName2) {
 436     return checkAttributes(atts, attName1)
 437       && checkAttributes(atts, attName2);
 438   }
 439 
 440   /** The SAX <code>endElement</code> */
 441   public void endElement (String namespaceURI,
 442                           String localName,
 443                           String qName)
 444     throws SAXException {
 445 
 446     int entryType = -1;
 447     Vector entryArgs = new Vector();
 448 
 449     boolean inExtension = inExtensionNamespace();
 450 
 451     if (namespaceURI != null
 452         && !inExtension
 453         && (namespaceName.equals(namespaceURI)
 454             || tr9401NamespaceName.equals(namespaceURI))) {
 455 
 456       String popURI = (String) baseURIStack.pop();
 457       String baseURI = (String) baseURIStack.peek();
 458 
 459       if (!baseURI.equals(popURI)) {
 460         entryType = Catalog.BASE;
 461         entryArgs.add(baseURI);
 462 
 463         debug.message(4, "(reset) xml:base", baseURI);
 464 
 465         try {
 466           CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
 467           catalog.addEntry(ce);
 468         } catch (CatalogException cex) {
 469           if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
 470             debug.message(1, "Invalid catalog entry type", localName);
 471           } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
 472             debug.message(1, "Invalid catalog entry (rbase)", localName);
 473           }
 474         }
 475       }
 476     }
 477 
 478     if (namespaceURI != null && namespaceName.equals(namespaceURI)
 479         && !inExtension) {
 480       if (localName.equals("catalog") || localName.equals("group")) {
 481         String popOverride = (String) overrideStack.pop();
 482         String override = (String) overrideStack.peek();
 483 
 484         if (!override.equals(popOverride)) {
 485           entryType = Catalog.OVERRIDE;
 486           entryArgs.add(override);
 487           overrideStack.push(override);
 488 
 489           debug.message(4, "(reset) override", override);
 490 
 491           try {
 492             CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
 493             catalog.addEntry(ce);
 494           } catch (CatalogException cex) {
 495             if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
 496               debug.message(1, "Invalid catalog entry type", localName);
 497             } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
 498               debug.message(1, "Invalid catalog entry (roverride)", localName);
 499             }
 500           }
 501         }
 502       }
 503     }
 504 
 505     namespaceStack.pop();
 506 
 507     return;
 508   }
 509 
 510   /** The SAX <code>characters</code> method does nothing. */
 511   public void characters (char ch[], int start, int length)
 512     throws SAXException {
 513     return;
 514   }
 515 
 516   /** The SAX <code>ignorableWhitespace</code> method does nothing. */
 517   public void ignorableWhitespace (char ch[], int start, int length)
 518     throws SAXException {
 519     return;
 520   }
 521 
 522   /** The SAX <code>processingInstruction</code> method does nothing. */
 523   public void processingInstruction (String target, String data)
 524     throws SAXException {
 525     return;
 526   }
 527 
 528   /** The SAX <code>skippedEntity</code> method does nothing. */
 529   public void skippedEntity (String name)
 530     throws SAXException {
 531     return;
 532   }
 533 
 534   /** The SAX <code>startPrefixMapping</code> method does nothing. */
 535   public void startPrefixMapping(String prefix, String uri)
 536     throws SAXException {
 537     return;
 538   }
 539 
 540   /** The SAX <code>endPrefixMapping</code> method does nothing. */
 541   public void endPrefixMapping(String prefix)
 542     throws SAXException {
 543     return;
 544   }
 545 
 546 }