// XMLCatalogReader.java - Read XML Catalog files // Written by Norman Walsh, nwalsh@arbortext.com // NO WARRANTY! This class is in the public domain. package com.arbortext.catalog; import java.lang.Integer; import java.util.Vector; import java.util.Enumeration; import java.io.IOException; import java.net.URL; import java.net.MalformedURLException; import com.arbortext.catalog.CatalogEntry; import com.arbortext.catalog.CatalogReader; import com.arbortext.catalog.InvalidCatalogEntryTypeException; import com.arbortext.catalog.InvalidCatalogEntryException; import com.arbortext.catalog.UnknownCatalogFormatException; import com.arbortext.catalog.NotXMLCatalogException; import com.arbortext.catalog.NoXMLParserException; import org.xml.sax.*; /** *
Parses XML Catalog files.
* ** This module, both source code and documentation, is in the * Public Domain, and comes with NO WARRANTY. ** *
This class reads XML Catalog files, returning a stream * of tokens. At present, it recognizes John Cowan's * XML Catalogs * (formerly XCatalogs). In the future, additional XML Catalog formats * may be supported.
* *This code interrogates the following non-standard system properties:
* *Sets the debug level. A value of 0 is assumed if the * property is not set or is not a number.
The debug level
* *In general, higher numbers produce more information:
*The list of entries scanned from the catalog.
* *The SAX Parser is event-driven, but the Catalog class expects * to iterate through the entries with * nextToken(). So this class builds a * vector of entries during the parse and returns them sequentially * when nextToken() is called.
* * @see Catalog */ private Vector catalogEntries = new Vector(); /** * An enumerator for walking through the list of catalogEntries. */ private Enumeration catalogEnum = null; /** *The name of the parser class to load when parsing XML Catalogs.
* *If a parser class is provided, * subsequent attempts to parse Catalog files will begin * by attemptiing an XML parse of the catalog file using a parser * of this class. * If the XML parse fails, the "default" text parse will be done * instead.
*/ private String parserClass = null; /** *Construct an XMLCatalogReader object.
*/ public XMLCatalogReader() { String property = System.getProperty("xml.catalog.debug"); if (property != null) { try { debug = Integer.parseInt(property); } catch (NumberFormatException e) { debug = 0; } } } /** *Sets the parser class, enabling XML Catalog parsing.
* *Sets the parser class that will be used for loading XML Catalogs.
* If this method is not called, all attempts to use the
* XMLCatalogParser
will fail, throwing a
* NoXMLParserException
.
Attempt to parse an XML Catalog file.
* * @param fileUrl The URL or filename of the catalog file to process * @param catParser A SAX-compliant parser to use for reading the files * * @throws SAXException Error parsing catalog file. * @throws IOException Error reading catalog file. * @throws NoXMLParserException No Parser class provided. * @throws NotXMLCatalogException The Catalog appears not to be XML. * @throws UnknownCatalogFormatException Unexpected XML catalog type. * @throws ClassNotFoundException Parser class can't be found. * @throws InstantiationException Parser class can't be instantiated. * @throws IllegalAccessException Error instantiating parser class. * @throws ClassCastException Parser class isn't a SAX Parser. */ public void parseCatalog(String fileUrl) throws SAXException, IOException, NotXMLCatalogException, NoXMLParserException, UnknownCatalogFormatException, ClassNotFoundException, InstantiationException, IllegalAccessException, ClassCastException { // Create an instance of the parser if (parserClass == null) { throw new NoXMLParserException(); } Parser parser = (Parser) Class.forName(parserClass).newInstance(); catfilename = fileUrl; parser.setDocumentHandler(this); parser.parse(fileUrl); if (catalogType == NOTXMLCATALOG) { // Why doesn't the attempt to parse this file throw a // SAX Exception??? throw new NotXMLCatalogException(); } if (catalogType == UNKNOWNCATALOG) { throw new UnknownCatalogFormatException(); } } /** *Get the next entry from the file
* * @throws IOException Error reading catalog file * @return A CatalogEntry object for the next entry in the catalog */ public CatalogEntry nextEntry() throws IOException { if (catalogEnum == null) { catalogEnum = catalogEntries.elements(); } if (catalogEnum.hasMoreElements()) { return (CatalogEntry) catalogEnum.nextElement(); } else { return null; } } // ---------------------------------------------------------------------- /* *Parse elements from John Cowan's XML Catalog doctype.
* *Each recognized element is turned into an appropriate * CatalogEntry and put onto the entries vector for later * retrieval.
* * @param name The name of the element. * @param atts The list of attributes on the element. * * @see CatalogEntry */ private void xCatalogEntry (String name, AttributeList atts) { CatalogEntry ce = null; try { if (name.equals("Base")) { ce = new CatalogEntry(CatalogEntry.BASE, atts.getValue("HRef")); debug(3, "Base", atts.getValue("HRef")); } if (name.equals("Delegate")) { ce = new CatalogEntry(CatalogEntry.DELEGATE, CatalogReader.normalize(atts.getValue("PublicId")), atts.getValue("HRef")); debug(3, "Delegate", CatalogReader.normalize(atts.getValue("PublicId")), atts.getValue("HRef")); } if (name.equals("Extend")) { ce = new CatalogEntry(CatalogEntry.CATALOG, atts.getValue("HRef")); debug(3, "Extend", atts.getValue("HRef")); } if (name.equals("Map")) { ce = new CatalogEntry(CatalogEntry.PUBLIC, CatalogReader.normalize(atts.getValue("PublicId")), atts.getValue("HRef")); debug(3, "Map", CatalogReader.normalize(atts.getValue("PublicId")), atts.getValue("HRef")); } if (name.equals("Remap")) { ce = new CatalogEntry(CatalogEntry.SYSTEM, atts.getValue("SystemId"), atts.getValue("HRef")); debug(3, "Remap", CatalogReader.normalize(atts.getValue("SystemId")), atts.getValue("HRef")); } if (ce == null) { // This is equivalent to an invalid catalog entry type debug(1, "Invalid catalog entry type", name); } } catch (InvalidCatalogEntryTypeException icete) { debug(1, "Invalid catalog entry type", name); } catch (InvalidCatalogEntryException icete) { debug(1, "Invalid catalog entry", name); } if (ce != null) { catalogEntries.addElement(ce); } } // ---------------------------------------------------------------------- // Implement the SAX DocumentHandler interface /**The SAX setDocumentLocator
method. Does nothing.
The SAX startDocument
method. Does nothing.
The SAX endDocument
method. Does nothing.
The SAX startElement
method.
This element attempts to identify the type of catalog
* by looking at the name of the first element encountered.
* If it recognizes the element, it sets the catalogType
* appropriately.
After the catalog type has been identified, the appropriate * entry parser is called for each subsequent element in the * catalog.
* * @param name The name of the element. * @param atts The list of attributes on the element. * */ public void startElement (String name, AttributeList atts) throws SAXException { if (catalogType == UNKNOWNCATALOG || catalogType == NOTXMLCATALOG) { if (name.equals("XMLCatalog")) { catalogType = XCATALOG; return; } } if (catalogType == XCATALOG) { xCatalogEntry(name, atts); } } /**The SAX endElement
method. Does nothing.
The SAX characters
method. Does nothing.
The SAX ignorableWhitespace
method. Does nothing.
The SAX processingInstruction
method. Does nothing.
Print debug message (if the debug level is high enough).
* * @param level The debug level of this message. This message * will only be * displayed if the current debug level is at least equal to this * value. * @param message The text of the message. * @param token The catalog file token being processed. */ private void debug(int level, String message, String token) { if (debug >= level) { System.out.println(message + ": " + token); } } /** *Print debug message (if the debug level is high enough).
* * @param level The debug level of this message. This message * will only be * displayed if the current debug level is at least equal to this * value. * @param message The text of the message. * @param token The catalog file token being processed. * @param spec The argument to the token. */ private void debug(int level, String message, String token, String spec) { if (debug >= level) { System.out.println(message + ": " + token + " " + spec); } } /** *Print debug message (if the debug level is high enough).
* * @param level The debug level of this message. This message * will only be * displayed if the current debug level is at least equal to this * value. * @param message The text of the message. * @param token The catalog file token being processed. * @param spec1 The first argument to the token. * @param spec2 The second argument to the token. */ private void debug(int level, String message, String token, String spec1, String spec2) { if (debug >= level) { System.out.println(message + ": " + token + " " + spec1); System.out.println("\t" + spec2); } } }