KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > util > xml > catalog > Catalog


1 // Catalog.java - Represents OASIS Open Catalog files.
2

3 /* ====================================================================
4  * The Apache Software License, Version 1.1
5  *
6  * Copyright (c) 2001-2003 The Apache Software Foundation. All rights
7  * reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution,
22  * if any, must include the following acknowledgment:
23  * "This product includes software developed by the
24  * Apache Software Foundation (http://www.apache.org/)."
25  * Alternately, this acknowledgment may appear in the software itself,
26  * if and wherever such third-party acknowledgments normally appear.
27  *
28  * 4. The names "Apache" and "Apache Software Foundation" must
29  * not be used to endorse or promote products derived from this
30  * software without prior written permission. For written
31  * permission, please contact apache@apache.org.
32  *
33  * 5. Products derived from this software may not be called "Apache",
34  * nor may "Apache" appear in their name, without prior written
35  * permission of the Apache Software Foundation.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals on behalf of the Apache Software Foundation. For more
53  * information on the Apache Software Foundation, please see
54  * <http://www.apache.org/>.
55  */

56
57 package org.jboss.util.xml.catalog;
58
59 import java.io.IOException JavaDoc;
60 import java.io.FileNotFoundException JavaDoc;
61 import java.io.InputStream JavaDoc;
62 import java.io.UnsupportedEncodingException JavaDoc;
63 import java.io.DataInputStream JavaDoc;
64 import java.util.Enumeration JavaDoc;
65 import java.util.Hashtable JavaDoc;
66 import java.util.Vector JavaDoc;
67 import java.net.URL JavaDoc;
68 import java.net.MalformedURLException JavaDoc;
69 import org.jboss.util.xml.catalog.CatalogManager;
70 import org.jboss.util.xml.catalog.helpers.PublicId;
71 import org.jboss.util.xml.catalog.readers.CatalogReader;
72 import org.jboss.util.xml.catalog.readers.OASISXMLCatalogReader;
73 import org.jboss.util.xml.catalog.readers.SAXCatalogReader;
74 import org.jboss.util.xml.catalog.readers.TR9401CatalogReader;
75
76 import javax.xml.parsers.SAXParserFactory JavaDoc;
77
78 /**
79  * Represents OASIS Open Catalog files.
80  *
81  * <p>This class implements the semantics of OASIS Open Catalog files
82  * (defined by
83  * <a HREF="http://www.oasis-open.org/html/a401.htm">OASIS Technical
84  * Resolution 9401:1997 (Amendment 2 to TR 9401)</a>).</p>
85  *
86  * <p>The primary purpose of the Catalog is to associate resources in the
87  * document with local system identifiers. Some entities
88  * (document types, XML entities, and notations) have names and all of them
89  * can have either public or system identifiers or both. (In XML, only a
90  * notation can have a public identifier without a system identifier, but
91  * the methods implemented in this class obey the Catalog semantics
92  * from the SGML
93  * days when system identifiers were optional.)</p>
94  *
95  * <p>The system identifiers returned by the resolution methods in this
96  * class are valid, i.e. usable by, and in fact constructed by, the
97  * <tt>java.net.URL</tt> class. Unfortunately, this class seems to behave in
98  * somewhat non-standard ways and the system identifiers returned may
99  * not be directly usable in a browser or filesystem context.
100  *
101  * <p>This class recognizes all of the Catalog entries defined in
102  * TR9401:1997:</p>
103  *
104  * <ul>
105  * <li><b>BASE</b>
106  * changes the base URI for resolving relative system identifiers. The
107  * initial base URI is the URI of the location of the catalog (which is,
108  * in turn, relative to the location of the current working directory
109  * at startup, as returned by the <tt>user.dir</tt> system property).</li>
110  * <li><b>CATALOG</b>
111  * processes other catalog files. An included catalog occurs logically
112  * at the end of the including catalog.</li>
113  * <li><b>DELEGATE_PUBLIC</b>
114  * specifies alternate catalogs for some public identifiers. The delegated
115  * catalogs are not loaded until they are needed, but they are cached
116  * once loaded.</li>
117  * <li><b>DELEGATE_SYSTEM</b>
118  * specifies alternate catalogs for some system identifiers. The delegated
119  * catalogs are not loaded until they are needed, but they are cached
120  * once loaded.</li>
121  * <li><b>DELEGATE_URI</b>
122  * specifies alternate catalogs for some URIs. The delegated
123  * catalogs are not loaded until they are needed, but they are cached
124  * once loaded.</li>
125  * <li><b>REWRITE_SYSTEM</b>
126  * specifies alternate prefix for a system identifier.</li>
127  * <li><b>REWRITE_URI</b>
128  * specifies alternate prefix for a URI.</li>
129  * <li><b>DOCTYPE</b>
130  * associates the names of root elements with URIs. (In other words, an XML
131  * processor might infer the doctype of an XML document that does not include
132  * a doctype declaration by looking for the DOCTYPE entry in the
133  * catalog which matches the name of the root element of the document.)</li>
134  * <li><b>DOCUMENT</b>
135  * provides a default document.</li>
136  * <li><b>DTDDECL</b>
137  * recognized and silently ignored. Not relevant for XML.</li>
138  * <li><b>ENTITY</b>
139  * associates entity names with URIs.</li>
140  * <li><b>LINKTYPE</b>
141  * recognized and silently ignored. Not relevant for XML.</li>
142  * <li><b>NOTATION</b>
143  * associates notation names with URIs.</li>
144  * <li><b>OVERRIDE</b>
145  * changes the override behavior. Initial behavior is set by the
146  * system property <tt>xml.catalog.override</tt>. The default initial
147  * behavior is 'YES', that is, entries in the catalog override
148  * system identifiers specified in the document.</li>
149  * <li><b>PUBLIC</b>
150  * maps a public identifier to a system identifier.</li>
151  * <li><b>SGMLDECL</b>
152  * recognized and silently ignored. Not relevant for XML.</li>
153  * <li><b>SYSTEM</b>
154  * maps a system identifier to another system identifier.</li>
155  * <li><b>URI</b>
156  * maps a URI to another URI.</li>
157  * </ul>
158  *
159  * <p>Note that BASE entries are treated as described by RFC2396. In
160  * particular, this has the counter-intuitive property that after a BASE
161  * entry identifing "http://example.com/a/b/c" as the base URI,
162  * the relative URI "foo" is resolved to the absolute URI
163  * "http://example.com/a/b/foo". You must provide the trailing slash if
164  * you do not want the final component of the path to be discarded as a
165  * filename would in a URI for a resource: "http://example.com/a/b/c/".
166  * </p>
167  *
168  * <p>Note that subordinate catalogs (all catalogs except the first,
169  * including CATALOG and DELEGATE* catalogs) are only loaded if and when
170  * they are required.</p>
171  *
172  * <p>This class relies on classes which implement the CatalogReader
173  * interface to actually load catalog files. This allows the catalog
174  * semantics to be implemented for TR9401 text-based catalogs, XML
175  * catalogs, or any number of other storage formats.</p>
176  *
177  * <p>Additional catalogs may also be loaded with the
178  * {@link #parseCatalog} method.</p>
179  * </dd>
180  * </dl>
181  *
182  * <p><b>Change Log:</b></p>
183  * <dl>
184  * <dt>2.0</dt>
185  * <dd><p>Rewrite to use CatalogReaders.</p></dd>
186  * <dt>1.1</dt>
187  * <dd><p>Allow quoted components in <tt>xml.catalog.files</tt>
188  * so that URLs containing colons can be used on Unix.
189  * The string passed to <tt>xml.catalog.files</tt> can now have the form:</p>
190  * <pre>
191  * unquoted-path-with-no-sep-chars:"double-quoted path with or without sep chars":'single-quoted path with or without sep chars'
192  * </pre>
193  * <p>(Where ":" is the separater character in this example.)</p>
194  * <p>If an unquoted path contains an embedded double or single quote
195  * character, no special processig is performed on that character. No
196  * path can contain separater characters, double, and single quotes
197  * simultaneously.</p>
198  * <p>Fix bug in calculation of BASE entries: if
199  * a catalog contains multiple BASE entries, each is relative to the preceding
200  * base, not the default base URI of the catalog.</p>
201  * </dd>
202  * <dt>1.0.1</dt>
203  * <dd><p>Fixed a bug in the calculation of the list of subordinate catalogs.
204  * This bug caused an infinite loop where parsing would alternately process
205  * two catalogs indefinitely.</p>
206  * </dd>
207  * </dl>
208  *
209  * @see CatalogReader
210  * @see CatalogEntry
211  *
212  * @author Norman Walsh
213  * <a HREF="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a>
214  *
215  * @version 1.0
216  *
217  * <p>Derived from public domain code originally published by Arbortext,
218  * Inc.</p>
219  */

220 public class Catalog {
221   /** The BASE Catalog Entry type. */
222   public static final int BASE = CatalogEntry.addEntryType("BASE", 1);
223
224   /** The CATALOG Catalog Entry type. */
225   public static final int CATALOG = CatalogEntry.addEntryType("CATALOG", 1);
226
227   /** The DOCUMENT Catalog Entry type. */
228   public static final int DOCUMENT = CatalogEntry.addEntryType("DOCUMENT", 1);
229
230   /** The OVERRIDE Catalog Entry type. */
231   public static final int OVERRIDE = CatalogEntry.addEntryType("OVERRIDE", 1);
232
233   /** The SGMLDECL Catalog Entry type. */
234   public static final int SGMLDECL = CatalogEntry.addEntryType("SGMLDECL", 1);
235
236   /** The DELEGATE_PUBLIC Catalog Entry type. */
237   public static final int DELEGATE_PUBLIC = CatalogEntry.addEntryType("DELEGATE_PUBLIC", 2);
238
239   /** The DELEGATE_SYSTEM Catalog Entry type. */
240   public static final int DELEGATE_SYSTEM = CatalogEntry.addEntryType("DELEGATE_SYSTEM", 2);
241
242   /** The DELEGATE_URI Catalog Entry type. */
243   public static final int DELEGATE_URI = CatalogEntry.addEntryType("DELEGATE_URI", 2);
244
245   /** The DOCTYPE Catalog Entry type. */
246   public static final int DOCTYPE = CatalogEntry.addEntryType("DOCTYPE", 2);
247
248   /** The DTDDECL Catalog Entry type. */
249   public static final int DTDDECL = CatalogEntry.addEntryType("DTDDECL", 2);
250
251   /** The ENTITY Catalog Entry type. */
252   public static final int ENTITY = CatalogEntry.addEntryType("ENTITY", 2);
253
254   /** The LINKTYPE Catalog Entry type. */
255   public static final int LINKTYPE = CatalogEntry.addEntryType("LINKTYPE", 2);
256
257   /** The NOTATION Catalog Entry type. */
258   public static final int NOTATION = CatalogEntry.addEntryType("NOTATION", 2);
259
260   /** The PUBLIC Catalog Entry type. */
261   public static final int PUBLIC = CatalogEntry.addEntryType("PUBLIC", 2);
262
263   /** The SYSTEM Catalog Entry type. */
264   public static final int SYSTEM = CatalogEntry.addEntryType("SYSTEM", 2);
265
266   /** The URI Catalog Entry type. */
267   public static final int URI = CatalogEntry.addEntryType("URI", 2);
268
269   /** The REWRITE_SYSTEM Catalog Entry type. */
270   public static final int REWRITE_SYSTEM = CatalogEntry.addEntryType("REWRITE_SYSTEM", 2);
271
272   /** The REWRITE_URI Catalog Entry type. */
273   public static final int REWRITE_URI = CatalogEntry.addEntryType("REWRITE_URI", 2);
274
275   /**
276    * The base URI for relative system identifiers in the catalog.
277    * This may be changed by BASE entries in the catalog.
278    */

279   protected URL JavaDoc base;
280
281   /** The base URI of the Catalog file currently being parsed. */
282   protected URL JavaDoc catalogCwd;
283
284   /** The catalog entries currently known to the system. */
285   protected Vector JavaDoc catalogEntries = new Vector JavaDoc();
286
287   /** The default initial override setting. */
288   protected boolean default_override = true;
289
290   /** The catalog manager in use for this instance. */
291   protected CatalogManager catalogManager = CatalogManager.getStaticManager();
292
293   /**
294    * A vector of catalog files to be loaded.
295    *
296    * <p>This list is initially established by
297    * <code>loadSystemCatalogs</code> when
298    * it parses the system catalog list, but CATALOG entries may
299    * contribute to it during the course of parsing.</p>
300    *
301    * @see #loadSystemCatalogs
302    * @see #localCatalogFiles
303    */

304   protected Vector JavaDoc catalogFiles = new Vector JavaDoc();
305
306   /**
307    * A vector of catalog files constructed during processing of
308    * CATALOG entries in the current catalog.
309    *
310    * <p>This two-level system is actually necessary to correctly implement
311    * the semantics of the CATALOG entry. If one catalog file includes
312    * another with a CATALOG entry, the included catalog logically
313    * occurs <i>at the end</i> of the including catalog, and after any
314    * preceding CATALOG entries. In other words, the CATALOG entry
315    * cannot insert anything into the middle of a catalog file.</p>
316    *
317    * <p>When processing reaches the end of each catalog files, any
318    * elements on this vector are added to the front of the
319    * <code>catalogFiles</code> vector.</p>
320    *
321    * @see #catalogFiles
322    */

323   protected Vector JavaDoc localCatalogFiles = new Vector JavaDoc();
324
325   /**
326    * A vector of Catalogs.
327    *
328    * <p>The semantics of Catalog resolution are such that each
329    * catalog is effectively a list of Catalogs (in other words,
330    * a recursive list of Catalog instances).</p>
331    *
332    * <p>Catalogs that are processed as the result of CATALOG or
333    * DELEGATE* entries are subordinate to the catalog that contained
334    * them, but they may in turn have subordinate catalogs.</p>
335    *
336    * <p>Catalogs are only loaded when they are needed, so this vector
337    * initially contains a list of Catalog filenames (URLs). If, during
338    * processing, one of these catalogs has to be loaded, the resulting
339    * Catalog object is placed in the vector, effectively caching it
340    * for the next query.</p>
341    */

342   protected Vector JavaDoc catalogs = new Vector JavaDoc();
343
344   /**
345    * A vector of DELEGATE* Catalog entries constructed during
346    * processing of the Catalog.
347    *
348    * <p>This two-level system has two purposes; first, it allows
349    * us to sort the DELEGATE* entries by the length of the partial
350    * public identifier so that a linear search encounters them in
351    * the correct order and second, it puts them all at the end of
352    * the Catalog.</p>
353    *
354    * <p>When processing reaches the end of each catalog file, any
355    * elements on this vector are added to the end of the
356    * <code>catalogEntries</code> vector. This assures that matching
357    * PUBLIC keywords are encountered before DELEGATE* entries.</p>
358    */

359   protected Vector JavaDoc localDelegate = new Vector JavaDoc();
360
361   /**
362    * A hash of CatalogReaders.
363    *
364    * <p>This hash maps MIME types to elements in the readerArr
365    * vector. This allows the Catalog to quickly locate the reader
366    * for a particular MIME type.</p>
367    */

368   protected Hashtable JavaDoc readerMap = new Hashtable JavaDoc();
369
370   /**
371    * A vector of CatalogReaders.
372    *
373    * <p>This vector contains all of the readers in the order that they
374    * were added. In the event that a catalog is read from a file, where
375    * the MIME type is unknown, each reader is attempted in turn until
376    * one succeeds.</p>
377    */

378   protected Vector JavaDoc readerArr = new Vector JavaDoc();
379
380   /**
381    * Constructs an empty Catalog.
382    *
383    * <p>The constructor interrogates the relevant system properties
384    * using the default (static) CatalogManager
385    * and initializes the catalog data structures.</p>
386    */

387   public Catalog() {
388     // nop;
389
}
390
391   /**
392    * Constructs an empty Catalog with a specific CatalogManager.
393    *
394    * <p>The constructor interrogates the relevant system properties
395    * using the specified Catalog Manager
396    * and initializes the catalog data structures.</p>
397    */

398   public Catalog(CatalogManager manager) {
399     catalogManager = manager;
400   }
401
402   /**
403    * Return the CatalogManager used by this catalog.
404    *
405    */

406   public CatalogManager getCatalogManager() {
407     return catalogManager;
408   }
409
410   /**
411    * Establish the CatalogManager used by this catalog.
412    *
413    */

414   public void setCatalogManager(CatalogManager manager) {
415     catalogManager = manager;
416   }
417
418   /**
419    * Setup readers.
420    */

421   public void setupReaders() {
422     SAXParserFactory JavaDoc spf = SAXParserFactory.newInstance();
423     spf.setNamespaceAware(true);
424     spf.setValidating(false);
425
426     SAXCatalogReader saxReader = new SAXCatalogReader(spf);
427
428     saxReader.setCatalogParser(null, "XMLCatalog",
429                    "org.apache.xml.resolver.readers.XCatalogReader");
430
431     saxReader.setCatalogParser(OASISXMLCatalogReader.namespaceName,
432                    "catalog",
433                    "org.apache.xml.resolver.readers.OASISXMLCatalogReader");
434
435     addReader("application/xml", saxReader);
436
437     TR9401CatalogReader textReader = new TR9401CatalogReader();
438     addReader("text/plain", textReader);
439   }
440
441   /**
442    * Add a new CatalogReader to the Catalog.
443    *
444    * <p>This method allows you to add a new CatalogReader to the
445    * catalog. The reader will be associated with the specified mimeType.
446    * You can only have one reader per mimeType.</p>
447    *
448    * <p>In the absence of a mimeType (e.g., when reading a catalog
449    * directly from a file on the local system), the readers are attempted
450    * in the order that you add them to the Catalog.</p>
451    *
452    * <p>Note that subordinate catalogs (created by CATALOG or
453    * DELEGATE* entries) get a copy of the set of readers present in
454    * the primary catalog when they are created. Readers added subsequently
455    * will not be available. For this reason, it is best to add all
456    * of the readers before the first call to parse a catalog.</p>
457    *
458    * @param mimeType The MIME type associated with this reader.
459    * @param reader The CatalogReader to use.
460    */

461   public void addReader(String JavaDoc mimeType, CatalogReader reader) {
462     if (readerMap.containsKey(mimeType)) {
463       Integer JavaDoc pos = (Integer JavaDoc) readerMap.get(mimeType);
464       readerArr.set(pos.intValue(), reader);
465     } else {
466       readerArr.add(reader);
467       Integer JavaDoc pos = new Integer JavaDoc(readerArr.size()-1);
468       readerMap.put(mimeType, pos);
469     }
470   }
471
472   /**
473    * Copies the reader list from the current Catalog to a new Catalog.
474    *
475    * <p>This method is used internally when constructing a new catalog.
476    * It copies the current reader associations over to the new catalog.
477    * </p>
478    *
479    * @param newCatalog The new Catalog.
480    */

481   protected void copyReaders(Catalog newCatalog) {
482     // Have to copy the readers in the right order...convert hash to arr
483
Vector JavaDoc mapArr = new Vector JavaDoc(readerMap.size());
484
485     // Pad the mapArr out to the right length
486
for (int count = 0; count < readerMap.size(); count++) {
487       mapArr.add(null);
488     }
489
490     Enumeration JavaDoc enumt = readerMap.keys();
491     while (enumt.hasMoreElements()) {
492       String JavaDoc mimeType = (String JavaDoc) enumt.nextElement();
493       Integer JavaDoc pos = (Integer JavaDoc) readerMap.get(mimeType);
494       mapArr.set(pos.intValue(), mimeType);
495     }
496
497     for (int count = 0; count < mapArr.size(); count++) {
498       String JavaDoc mimeType = (String JavaDoc) mapArr.get(count);
499       Integer JavaDoc pos = (Integer JavaDoc) readerMap.get(mimeType);
500       newCatalog.addReader(mimeType,
501                (CatalogReader)
502                readerArr.get(pos.intValue()));
503     }
504   }
505
506   /**
507    * Create a new Catalog object.
508    *
509    * <p>This method constructs a new instance of the running Catalog
510    * class (which might be a subtype of org.apache.xml.resolver.Catalog).
511    * All new catalogs are managed by the same CatalogManager.
512    * </p>
513    *
514    * <p>N.B. All Catalog subtypes should call newCatalog() to construct
515    * a new Catalog. Do not simply use "new Subclass()" since that will
516    * confuse future subclasses.</p>
517    */

518   protected Catalog newCatalog() {
519     String JavaDoc catalogClass = this.getClass().getName();
520
521     try {
522       Catalog c = (Catalog) (Class.forName(catalogClass).newInstance());
523       c.setCatalogManager(catalogManager);
524       copyReaders(c);
525       return c;
526     } catch (ClassNotFoundException JavaDoc cnfe) {
527       catalogManager.debug.message(1, "Class Not Found Exception: " + catalogClass);
528     } catch (IllegalAccessException JavaDoc iae) {
529       catalogManager.debug.message(1, "Illegal Access Exception: " + catalogClass);
530     } catch (InstantiationException JavaDoc ie) {
531       catalogManager.debug.message(1, "Instantiation Exception: " + catalogClass);
532     } catch (ClassCastException JavaDoc cce) {
533       catalogManager.debug.message(1, "Class Cast Exception: " + catalogClass);
534     } catch (Exception JavaDoc e) {
535       catalogManager.debug.message(1, "Other Exception: " + catalogClass);
536     }
537
538     Catalog c = new Catalog();
539     c.setCatalogManager(catalogManager);
540     copyReaders(c);
541     return c;
542   }
543
544   /**
545    * Returns the current base URI.
546    */

547   public String JavaDoc getCurrentBase() {
548     return base.toString();
549   }
550
551   /**
552    * Returns the default override setting associated with this
553    * catalog.
554    *
555    * <p>All catalog files loaded by this catalog will have the
556    * initial override setting specified by this default.</p>
557    */

558   public String JavaDoc getDefaultOverride() {
559     if (default_override) {
560       return "yes";
561     } else {
562       return "no";
563     }
564   }
565
566   /**
567    * Load the system catalog files.
568    *
569    * <p>The method adds all of the
570    * catalogs specified in the <tt>xml.catalog.files</tt> property
571    * to the Catalog list.</p>
572    *
573    * @throws MalformedURLException One of the system catalogs is
574    * identified with a filename that is not a valid URL.
575    * @throws IOException One of the system catalogs cannot be read.
576    */

577   public void loadSystemCatalogs()
578     throws MalformedURLException JavaDoc, IOException JavaDoc {
579
580     Vector JavaDoc catalogs = catalogManager.getCatalogFiles();
581     if (catalogs != null) {
582       for (int count = 0; count < catalogs.size(); count++) {
583     catalogFiles.addElement(catalogs.elementAt(count));
584       }
585     }
586
587     if (catalogFiles.size() > 0) {
588       // This is a little odd. The parseCatalog() method expects
589
// a filename, but it adds that name to the end of the
590
// catalogFiles vector, and then processes that vector.
591
// This allows the system to handle CATALOG entries
592
// correctly.
593
//
594
// In this init case, we take the last element off the
595
// catalogFiles vector and pass it to parseCatalog. This
596
// will "do the right thing" in the init case, and allow
597
// parseCatalog() to do the right thing in the non-init
598
// case. Honest.
599
//
600
String JavaDoc catfile = (String JavaDoc) catalogFiles.lastElement();
601       catalogFiles.removeElement(catfile);
602       parseCatalog(catfile);
603     }
604   }
605
606   /**
607    * Parse a catalog file, augmenting internal data structures.
608    *
609    * @param fileName The filename of the catalog file to process
610    *
611    * @throws MalformedURLException The fileName cannot be turned into
612    * a valid URL.
613    * @throws IOException Error reading catalog file.
614    */

615   public synchronized void parseCatalog(String JavaDoc fileName)
616     throws MalformedURLException JavaDoc, IOException JavaDoc {
617
618     default_override = catalogManager.getPreferPublic();
619     catalogManager.debug.message(4, "Parse catalog: " + fileName);
620
621     // Put the file into the list of catalogs to process...
622
// In all cases except the case when initCatalog() is the
623
// caller, this will be the only catalog initially in the list...
624
catalogFiles.addElement(fileName);
625
626     // Now process all the pending catalogs...
627
parsePendingCatalogs();
628   }
629
630   /**
631    * Parse a catalog file, augmenting internal data structures.
632    *
633    * <p>Catalogs retrieved over the net may have an associated MIME type.
634    * The MIME type can be used to select an appropriate reader.</p>
635    *
636    * @param mimeType The MIME type of the catalog file.
637    * @param is The InputStream from which the catalog should be read
638    *
639    * @throws CatalogException Failed to load catalog
640    * mimeType.
641    * @throws IOException Error reading catalog file.
642    */

643   public synchronized void parseCatalog(String JavaDoc mimeType, InputStream JavaDoc is)
644     throws IOException JavaDoc, CatalogException {
645
646     default_override = catalogManager.getPreferPublic();
647     catalogManager.debug.message(4, "Parse " + mimeType + " catalog on input stream");
648
649     CatalogReader reader = null;
650
651     if (readerMap.containsKey(mimeType)) {
652       int arrayPos = ((Integer JavaDoc) readerMap.get(mimeType)).intValue();
653       reader = (CatalogReader) readerArr.get(arrayPos);
654     }
655
656     if (reader == null) {
657       String JavaDoc msg = "No CatalogReader for MIME type: " + mimeType;
658       catalogManager.debug.message(2, msg);
659       throw new CatalogException(CatalogException.UNPARSEABLE, msg);
660     }
661
662     reader.readCatalog(this, is);
663
664     // Now process all the pending catalogs...
665
parsePendingCatalogs();
666   }
667
668   /**
669    * Parse a catalog document, augmenting internal data structures.
670    *
671    * <p>This method supports catalog files stored in jar files: e.g.,
672    * jar:file:///path/to/filename.jar!/path/to/catalog.xml". That URI
673    * doesn't survive transmogrification through the URI processing that
674    * the parseCatalog(String) performs and passing it as an input stream
675    * doesn't set the base URI appropriately.</p>
676    *
677    * <p>Written by Stefan Wachter (2002-09-26)</p>
678    *
679    * @param aUrl The URL of the catalog document to process
680    *
681    * @throws IOException Error reading catalog file.
682    */

683   public synchronized void parseCatalog(URL JavaDoc aUrl) throws IOException JavaDoc {
684     catalogCwd = aUrl;
685     base = aUrl;
686
687     default_override = catalogManager.getPreferPublic();
688     catalogManager.debug.message(4, "Parse catalog: " + aUrl.toString());
689
690     DataInputStream JavaDoc inStream = null;
691     boolean parsed = false;
692
693     for (int count = 0; !parsed && count < readerArr.size(); count++) {
694       CatalogReader reader = (CatalogReader) readerArr.get(count);
695
696       try {
697         inStream = new DataInputStream JavaDoc(aUrl.openStream());
698       } catch (FileNotFoundException JavaDoc fnfe) {
699         // No catalog; give up!
700
break;
701       }
702
703       try {
704         reader.readCatalog(this, inStream);
705         parsed=true;
706       } catch (CatalogException ce) {
707         if (ce.getExceptionType() == CatalogException.PARSE_FAILED) {
708           // give up!
709
break;
710         } else {
711           // try again!
712
}
713       }
714
715       try {
716         inStream.close();
717       } catch (IOException JavaDoc e) {
718         //nop
719
}
720     }
721
722     if (parsed) parsePendingCatalogs();
723   }
724
725   /**
726    * Parse all of the pending catalogs.
727    *
728    * <p>Catalogs may refer to other catalogs, this method parses
729    * all of the currently pending catalog files.</p>
730    */

731   protected synchronized void parsePendingCatalogs()
732     throws MalformedURLException JavaDoc, IOException JavaDoc {
733
734     if (!localCatalogFiles.isEmpty()) {
735       // Move all the localCatalogFiles into the front of
736
// the catalogFiles queue
737
Vector JavaDoc newQueue = new Vector JavaDoc();
738       Enumeration JavaDoc q = localCatalogFiles.elements();
739       while (q.hasMoreElements()) {
740     newQueue.addElement(q.nextElement());
741       }
742
743       // Put the rest of the catalogs on the end of the new list
744
for (int curCat = 0; curCat < catalogFiles.size(); curCat++) {
745     String JavaDoc catfile = (String JavaDoc) catalogFiles.elementAt(curCat);
746     newQueue.addElement(catfile);
747       }
748
749       catalogFiles = newQueue;
750       localCatalogFiles.clear();
751     }
752
753     // Suppose there are no catalog files to process, but the
754
// single catalog already parsed included some delegate
755
// entries? Make sure they don't get lost.
756
if (catalogFiles.isEmpty() && !localDelegate.isEmpty()) {
757       Enumeration JavaDoc e = localDelegate.elements();
758       while (e.hasMoreElements()) {
759     catalogEntries.addElement(e.nextElement());
760       }
761       localDelegate.clear();
762     }
763
764     // Now process all the files on the catalogFiles vector. This
765
// vector can grow during processing if CATALOG entries are
766
// encountered in the catalog
767
while (!catalogFiles.isEmpty()) {
768       String JavaDoc catfile = (String JavaDoc) catalogFiles.elementAt(0);
769       try {
770     catalogFiles.remove(0);
771       } catch (ArrayIndexOutOfBoundsException JavaDoc e) {
772     // can't happen
773
}
774
775       if (catalogEntries.size() == 0 && catalogs.size() == 0) {
776     // We haven't parsed any catalogs yet, let this
777
// catalog be the first...
778
try {
779       parseCatalogFile(catfile);
780     } catch (CatalogException ce) {
781       System.out.println("FIXME: " + ce.toString());
782     }
783       } else {
784     // This is a subordinate catalog. We save its name,
785
// but don't bother to load it unless it's necessary.
786
catalogs.addElement(catfile);
787       }
788
789       if (!localCatalogFiles.isEmpty()) {
790     // Move all the localCatalogFiles into the front of
791
// the catalogFiles queue
792
Vector JavaDoc newQueue = new Vector JavaDoc();
793     Enumeration JavaDoc q = localCatalogFiles.elements();
794     while (q.hasMoreElements()) {
795       newQueue.addElement(q.nextElement());
796     }
797
798     // Put the rest of the catalogs on the end of the new list
799
for (int curCat = 0; curCat < catalogFiles.size(); curCat++) {
800       catfile = (String JavaDoc) catalogFiles.elementAt(curCat);
801       newQueue.addElement(catfile);
802     }
803
804     catalogFiles = newQueue;
805     localCatalogFiles.clear();
806       }
807
808       if (!localDelegate.isEmpty()) {
809     Enumeration JavaDoc e = localDelegate.elements();
810     while (e.hasMoreElements()) {
811       catalogEntries.addElement(e.nextElement());
812     }
813     localDelegate.clear();
814       }
815     }
816
817     // We've parsed them all, reinit the vector...
818
catalogFiles.clear();
819   }
820
821   /**
822    * Parse a single catalog file, augmenting internal data structures.
823    *
824    * @param fileName The filename of the catalog file to process
825    *
826    * @throws MalformedURLException The fileName cannot be turned into
827    * a valid URL.
828    * @throws IOException Error reading catalog file.
829    */

830   protected synchronized void parseCatalogFile(String JavaDoc fileName)
831     throws MalformedURLException JavaDoc, IOException JavaDoc, CatalogException {
832
833     CatalogEntry entry;
834
835     // The base-base is the cwd. If the catalog file is specified
836
// with a relative path, this assures that it gets resolved
837
// properly...
838
try {
839       // tack on a basename because URLs point to files not dirs
840
String JavaDoc userdir = fixSlashes(System.getProperty("user.dir"));
841       catalogCwd = new URL JavaDoc("file:" + userdir + "/basename");
842     } catch (MalformedURLException JavaDoc e) {
843       String JavaDoc userdir = fixSlashes(System.getProperty("user.dir"));
844       catalogManager.debug.message(1, "Malformed URL on cwd", userdir);
845       catalogCwd = null;
846     }
847
848     // The initial base URI is the location of the catalog file
849
try {
850       base = new URL JavaDoc(catalogCwd, fixSlashes(fileName));
851     } catch (MalformedURLException JavaDoc e) {
852       try {
853     base = new URL JavaDoc("file:" + fixSlashes(fileName));
854       } catch (MalformedURLException JavaDoc e2) {
855     catalogManager.debug.message(1, "Malformed URL on catalog filename",
856               fixSlashes(fileName));
857     base = null;
858       }
859     }
860
861     catalogManager.debug.message(2, "Loading catalog", fileName);
862     catalogManager.debug.message(4, "Default BASE", base.toString());
863
864     fileName = base.toString();
865
866     DataInputStream JavaDoc inStream = null;
867     boolean parsed = false;
868     boolean notFound = false;
869
870     for (int count = 0; !parsed && count < readerArr.size(); count++) {
871       CatalogReader reader = (CatalogReader) readerArr.get(count);
872
873       try {
874     notFound = false;
875     inStream = new DataInputStream JavaDoc(base.openStream());
876       } catch (FileNotFoundException JavaDoc fnfe) {
877     // No catalog; give up!
878
notFound = true;
879     break;
880       }
881
882       try {
883     reader.readCatalog(this, inStream);
884     parsed = true;
885       } catch (CatalogException ce) {
886     if (ce.getExceptionType() == CatalogException.PARSE_FAILED) {
887       // give up!
888
break;
889     } else {
890       // try again!
891
}
892       }
893
894       try {
895     inStream.close();
896       } catch (IOException JavaDoc e) {
897     //nop
898
}
899     }
900
901     if (!parsed) {
902       if (notFound) {
903     catalogManager.debug.message(3, "Catalog does not exist", fileName);
904       } else {
905     catalogManager.debug.message(1, "Failed to parse catalog", fileName);
906       }
907     }
908   }
909
910   /**
911    * Cleanup and process a Catalog entry.
912    *
913    * <p>This method processes each Catalog entry, changing mapped
914    * relative system identifiers into absolute ones (based on the current
915    * base URI), and maintaining other information about the current
916    * catalog.</p>
917    *
918    * @param entry The CatalogEntry to process.
919    */

920   public void addEntry(CatalogEntry entry) {
921     int type = entry.getEntryType();
922
923     if (type == BASE) {
924       String JavaDoc value = entry.getEntryArg(0);
925       URL JavaDoc newbase = null;
926
927       catalogManager.debug.message(5, "BASE CUR", base.toString());
928       catalogManager.debug.message(4, "BASE STR", value);
929
930       try {
931     value = fixSlashes(value);
932     newbase = new URL JavaDoc(base, value);
933       } catch (MalformedURLException JavaDoc e) {
934     try {
935       newbase = new URL JavaDoc("file:" + value);
936     } catch (MalformedURLException JavaDoc e2) {
937       catalogManager.debug.message(1, "Malformed URL on base", value);
938       newbase = null;
939     }
940       }
941
942       if (newbase != null) {
943     base = newbase;
944       }
945
946       catalogManager.debug.message(5, "BASE NEW", base.toString());
947     } else if (type == CATALOG) {
948       String JavaDoc fsi = makeAbsolute(entry.getEntryArg(0));
949
950       catalogManager.debug.message(4, "CATALOG", fsi);
951
952       localCatalogFiles.addElement(fsi);
953     } else if (type == PUBLIC) {
954       String JavaDoc publicid = PublicId.normalize(entry.getEntryArg(0));
955       String JavaDoc systemid = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
956
957       entry.setEntryArg(0, publicid);
958       entry.setEntryArg(1, systemid);
959
960       catalogManager.debug.message(4, "PUBLIC", publicid, systemid);
961
962       catalogEntries.addElement(entry);
963     } else if (type == SYSTEM) {
964       String JavaDoc systemid = normalizeURI(entry.getEntryArg(0));
965       String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
966
967       entry.setEntryArg(1, fsi);
968
969       catalogManager.debug.message(4, "SYSTEM", systemid, fsi);
970
971       catalogEntries.addElement(entry);
972     } else if (type == URI) {
973       String JavaDoc uri = normalizeURI(entry.getEntryArg(0));
974       String JavaDoc altURI = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
975
976       entry.setEntryArg(1, altURI);
977
978       catalogManager.debug.message(4, "URI", uri, altURI);
979
980       catalogEntries.addElement(entry);
981     } else if (type == DOCUMENT) {
982       String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(0)));
983       entry.setEntryArg(0, fsi);
984
985       catalogManager.debug.message(4, "DOCUMENT", fsi);
986
987       catalogEntries.addElement(entry);
988     } else if (type == OVERRIDE) {
989       catalogManager.debug.message(4, "OVERRIDE", entry.getEntryArg(0));
990
991       catalogEntries.addElement(entry);
992     } else if (type == SGMLDECL) {
993       // meaningless in XML
994
String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(0)));
995       entry.setEntryArg(0, fsi);
996
997       catalogManager.debug.message(4, "SGMLDECL", fsi);
998
999       catalogEntries.addElement(entry);
1000    } else if (type == DELEGATE_PUBLIC) {
1001      String JavaDoc ppi = PublicId.normalize(entry.getEntryArg(0));
1002      String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1003
1004      entry.setEntryArg(0, ppi);
1005      entry.setEntryArg(1, fsi);
1006
1007      catalogManager.debug.message(4, "DELEGATE_PUBLIC", ppi, fsi);
1008
1009      addDelegate(entry);
1010    } else if (type == DELEGATE_SYSTEM) {
1011      String JavaDoc psi = normalizeURI(entry.getEntryArg(0));
1012      String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1013
1014      entry.setEntryArg(0, psi);
1015      entry.setEntryArg(1, fsi);
1016
1017      catalogManager.debug.message(4, "DELEGATE_SYSTEM", psi, fsi);
1018
1019      addDelegate(entry);
1020    } else if (type == DELEGATE_URI) {
1021      String JavaDoc pui = normalizeURI(entry.getEntryArg(0));
1022      String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1023
1024      entry.setEntryArg(0, pui);
1025      entry.setEntryArg(1, fsi);
1026
1027      catalogManager.debug.message(4, "DELEGATE_URI", pui, fsi);
1028
1029      addDelegate(entry);
1030    } else if (type == REWRITE_SYSTEM) {
1031      String JavaDoc psi = normalizeURI(entry.getEntryArg(0));
1032      String JavaDoc rpx = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1033
1034      entry.setEntryArg(0, psi);
1035      entry.setEntryArg(1, rpx);
1036
1037      catalogManager.debug.message(4, "REWRITE_SYSTEM", psi, rpx);
1038
1039      catalogEntries.addElement(entry);
1040    } else if (type == REWRITE_URI) {
1041      String JavaDoc pui = normalizeURI(entry.getEntryArg(0));
1042      String JavaDoc upx = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1043
1044      entry.setEntryArg(0, pui);
1045      entry.setEntryArg(1, upx);
1046
1047      catalogManager.debug.message(4, "REWRITE_URI", pui, upx);
1048
1049      catalogEntries.addElement(entry);
1050    } else if (type == DOCTYPE) {
1051      String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1052      entry.setEntryArg(1, fsi);
1053
1054      catalogManager.debug.message(4, "DOCTYPE", entry.getEntryArg(0), fsi);
1055
1056      catalogEntries.addElement(entry);
1057    } else if (type == DTDDECL) {
1058      // meaningless in XML
1059
String JavaDoc fpi = PublicId.normalize(entry.getEntryArg(0));
1060      entry.setEntryArg(0, fpi);
1061      String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1062      entry.setEntryArg(1, fsi);
1063
1064      catalogManager.debug.message(4, "DTDDECL", fpi, fsi);
1065
1066      catalogEntries.addElement(entry);
1067    } else if (type == ENTITY) {
1068      String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1069      entry.setEntryArg(1, fsi);
1070
1071      catalogManager.debug.message(4, "ENTITY", entry.getEntryArg(0), fsi);
1072
1073      catalogEntries.addElement(entry);
1074    } else if (type == LINKTYPE) {
1075      // meaningless in XML
1076
String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1077      entry.setEntryArg(1, fsi);
1078
1079      catalogManager.debug.message(4, "LINKTYPE", entry.getEntryArg(0), fsi);
1080
1081      catalogEntries.addElement(entry);
1082    } else if (type == NOTATION) {
1083      String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1084      entry.setEntryArg(1, fsi);
1085
1086      catalogManager.debug.message(4, "NOTATION", entry.getEntryArg(0), fsi);
1087
1088      catalogEntries.addElement(entry);
1089    } else {
1090      catalogEntries.addElement(entry);
1091    }
1092  }
1093
1094  /**
1095   * Handle unknown CatalogEntry types.
1096   *
1097   * <p>This method exists to allow subclasses to deal with unknown
1098   * entry types.</p>
1099   */

1100  public void unknownEntry(Vector JavaDoc strings) {
1101    if (strings != null && strings.size() > 0) {
1102      String JavaDoc keyword = (String JavaDoc) strings.elementAt(0);
1103      catalogManager.debug.message(2, "Unrecognized token parsing catalog", keyword);
1104    }
1105  }
1106
1107  /**
1108   * Parse all subordinate catalogs.
1109   *
1110   * <p>This method recursively parses all of the subordinate catalogs.
1111   * If this method does not throw an exception, you can be confident that
1112   * no subsequent call to any resolve*() method will either, with two
1113   * possible exceptions:</p>
1114   *
1115   * <ol>
1116   * <li><p>Delegated catalogs are re-parsed each time they are needed
1117   * (because a variable list of them may be needed in each case,
1118   * depending on the length of the matching partial public identifier).</p>
1119   * <p>But they are parsed by this method, so as long as they don't
1120   * change or disappear while the program is running, they shouldn't
1121   * generate errors later if they don't generate errors now.</p>
1122   * <li><p>If you add new catalogs with <code>parseCatalog</code>, they
1123   * won't be loaded until they are needed or until you call
1124   * <code>parseAllCatalogs</code> again.</p>
1125   * </ol>
1126   *
1127   * <p>On the other hand, if you don't call this method, you may
1128   * successfully parse documents without having to load all possible
1129   * catalogs.</p>
1130   *
1131   * @throws MalformedURLException The filename (URL) for a
1132   * subordinate or delegated catalog is not a valid URL.
1133   * @throws IOException Error reading some subordinate or delegated
1134   * catalog file.
1135   */

1136  public void parseAllCatalogs()
1137    throws MalformedURLException JavaDoc, IOException JavaDoc {
1138
1139    // Parse all the subordinate catalogs
1140
for (int catPos = 0; catPos < catalogs.size(); catPos++) {
1141      Catalog c = null;
1142
1143      try {
1144    c = (Catalog) catalogs.elementAt(catPos);
1145      } catch (ClassCastException JavaDoc e) {
1146    String JavaDoc catfile = (String JavaDoc) catalogs.elementAt(catPos);
1147    c = newCatalog();
1148
1149    c.parseCatalog(catfile);
1150    catalogs.setElementAt(c, catPos);
1151    c.parseAllCatalogs();
1152      }
1153    }
1154
1155    // Parse all the DELEGATE catalogs
1156
Enumeration JavaDoc enumt = catalogEntries.elements();
1157    while (enumt.hasMoreElements()) {
1158      CatalogEntry e = (CatalogEntry) enumt.nextElement();
1159      if (e.getEntryType() == DELEGATE_PUBLIC
1160      || e.getEntryType() == DELEGATE_SYSTEM
1161      || e.getEntryType() == DELEGATE_URI) {
1162    Catalog dcat = newCatalog();
1163    dcat.parseCatalog(e.getEntryArg(1));
1164      }
1165    }
1166  }
1167
1168
1169  /**
1170   * Return the applicable DOCTYPE system identifier.
1171   *
1172   * @param entityName The name of the entity (element) for which
1173   * a doctype is required.
1174   * @param publicId The nominal public identifier for the doctype
1175   * (as provided in the source document).
1176   * @param systemId The nominal system identifier for the doctype
1177   * (as provided in the source document).
1178   *
1179   * @return The system identifier to use for the doctype.
1180   *
1181   * @throws MalformedURLException The formal system identifier of a
1182   * subordinate catalog cannot be turned into a valid URL.
1183   * @throws IOException Error reading subordinate catalog file.
1184   */

1185  public String JavaDoc resolveDoctype(String JavaDoc entityName,
1186                   String JavaDoc publicId,
1187                   String JavaDoc systemId)
1188    throws MalformedURLException JavaDoc, IOException JavaDoc {
1189    String JavaDoc resolved = null;
1190
1191    catalogManager.debug.message(3, "resolveDoctype("
1192          +entityName+","+publicId+","+systemId+")");
1193
1194    systemId = normalizeURI(systemId);
1195
1196    if (publicId != null && publicId.startsWith("urn:publicid:")) {
1197      publicId = PublicId.decodeURN(publicId);
1198    }
1199
1200    if (systemId != null && systemId.startsWith("urn:publicid:")) {
1201      systemId = PublicId.decodeURN(systemId);
1202      if (publicId != null && !publicId.equals(systemId)) {
1203    catalogManager.debug.message(1, "urn:publicid: system identifier differs from public identifier; using public identifier");
1204    systemId = null;
1205      } else {
1206    publicId = systemId;
1207    systemId = null;
1208      }
1209    }
1210
1211    if (systemId != null) {
1212      // If there's a SYSTEM entry in this catalog, use it
1213
resolved = resolveLocalSystem(systemId);
1214      if (resolved != null) {
1215    return resolved;
1216      }
1217    }
1218
1219    if (publicId != null) {
1220      // If there's a PUBLIC entry in this catalog, use it
1221
resolved = resolveLocalPublic(DOCTYPE,
1222                    entityName,
1223                    publicId,
1224                    systemId);
1225      if (resolved != null) {
1226    return resolved;
1227      }
1228    }
1229
1230    // If there's a DOCTYPE entry in this catalog, use it
1231
boolean over = default_override;
1232    Enumeration JavaDoc enumt = catalogEntries.elements();
1233    while (enumt.hasMoreElements()) {
1234      CatalogEntry e = (CatalogEntry) enumt.nextElement();
1235      if (e.getEntryType() == OVERRIDE) {
1236    over = e.getEntryArg(0).equalsIgnoreCase("YES");
1237    continue;
1238      }
1239
1240      if (e.getEntryType() == DOCTYPE
1241      && e.getEntryArg(0).equals(entityName)) {
1242    if (over || systemId == null) {
1243      return e.getEntryArg(1);
1244    }
1245      }
1246    }
1247
1248    // Otherwise, look in the subordinate catalogs
1249
return resolveSubordinateCatalogs(DOCTYPE,
1250                      entityName,
1251                      publicId,
1252                      systemId);
1253  }
1254
1255  /**
1256   * Return the applicable DOCUMENT entry.
1257   *
1258   * @return The system identifier to use for the doctype.
1259   *
1260   * @throws MalformedURLException The formal system identifier of a
1261   * subordinate catalog cannot be turned into a valid URL.
1262   * @throws IOException Error reading subordinate catalog file.
1263   */

1264  public String JavaDoc resolveDocument()
1265    throws MalformedURLException JavaDoc, IOException JavaDoc {
1266    // If there's a DOCUMENT entry, return it
1267

1268    catalogManager.debug.message(3, "resolveDocument");
1269
1270    Enumeration JavaDoc enumt = catalogEntries.elements();
1271    while (enumt.hasMoreElements()) {
1272      CatalogEntry e = (CatalogEntry) enumt.nextElement();
1273      if (e.getEntryType() == DOCUMENT) {
1274    return e.getEntryArg(1); //FIXME check this
1275
}
1276    }
1277
1278    return resolveSubordinateCatalogs(DOCUMENT,
1279                      null, null, null);
1280  }
1281
1282  /**
1283   * Return the applicable ENTITY system identifier.
1284   *
1285   * @param entityName The name of the entity for which
1286   * a system identifier is required.
1287   * @param publicId The nominal public identifier for the entity
1288   * (as provided in the source document).
1289   * @param systemId The nominal system identifier for the entity
1290   * (as provided in the source document).
1291   *
1292   * @return The system identifier to use for the entity.
1293   *
1294   * @throws MalformedURLException The formal system identifier of a
1295   * subordinate catalog cannot be turned into a valid URL.
1296   * @throws IOException Error reading subordinate catalog file.
1297   */

1298  public String JavaDoc resolveEntity(String JavaDoc entityName,
1299                  String JavaDoc publicId,
1300                  String JavaDoc systemId)
1301    throws MalformedURLException JavaDoc, IOException JavaDoc {
1302    String JavaDoc resolved = null;
1303
1304    catalogManager.debug.message(3, "resolveEntity("
1305          +entityName+","+publicId+","+systemId+")");
1306
1307    systemId = normalizeURI(systemId);
1308
1309    if (publicId != null && publicId.startsWith("urn:publicid:")) {
1310      publicId = PublicId.decodeURN(publicId);
1311    }
1312
1313    if (systemId != null && systemId.startsWith("urn:publicid:")) {
1314      systemId = PublicId.decodeURN(systemId);
1315      if (publicId != null && !publicId.equals(systemId)) {
1316    catalogManager.debug.message(1, "urn:publicid: system identifier differs from public identifier; using public identifier");
1317    systemId = null;
1318      } else {
1319    publicId = systemId;
1320    systemId = null;
1321      }
1322    }
1323
1324    if (systemId != null) {
1325      // If there's a SYSTEM entry in this catalog, use it
1326
resolved = resolveLocalSystem(systemId);
1327      if (resolved != null) {
1328    return resolved;
1329      }
1330    }
1331
1332    if (publicId != null) {
1333      // If there's a PUBLIC entry in this catalog, use it
1334
resolved = resolveLocalPublic(ENTITY,
1335                    entityName,
1336                    publicId,
1337                    systemId);
1338      if (resolved != null) {
1339    return resolved;
1340      }
1341    }
1342
1343    // If there's a ENTITY entry in this catalog, use it
1344
boolean over = default_override;
1345    Enumeration JavaDoc enumt = catalogEntries.elements();
1346    while (enumt.hasMoreElements()) {
1347      CatalogEntry e = (CatalogEntry) enumt.nextElement();
1348      if (e.getEntryType() == OVERRIDE) {
1349    over = e.getEntryArg(0).equalsIgnoreCase("YES");
1350    continue;
1351      }
1352
1353      if (e.getEntryType() == ENTITY
1354      && e.getEntryArg(0).equals(entityName)) {
1355    if (over || systemId == null) {
1356      return e.getEntryArg(1);
1357    }
1358      }
1359    }
1360
1361    // Otherwise, look in the subordinate catalogs
1362
return resolveSubordinateCatalogs(ENTITY,
1363                      entityName,
1364                      publicId,
1365                      systemId);
1366  }
1367
1368  /**
1369   * Return the applicable NOTATION system identifier.
1370   *
1371   * @param notationName The name of the notation for which
1372   * a doctype is required.
1373   * @param publicId The nominal public identifier for the notation
1374   * (as provided in the source document).
1375   * @param systemId The nominal system identifier for the notation
1376   * (as provided in the source document).
1377   *
1378   * @return The system identifier to use for the notation.
1379   *
1380   * @throws MalformedURLException The formal system identifier of a
1381   * subordinate catalog cannot be turned into a valid URL.
1382   * @throws IOException Error reading subordinate catalog file.
1383   */

1384  public String JavaDoc resolveNotation(String JavaDoc notationName,
1385                String JavaDoc publicId,
1386                String JavaDoc systemId)
1387    throws MalformedURLException JavaDoc, IOException JavaDoc {
1388    String JavaDoc resolved = null;
1389
1390    catalogManager.debug.message(3, "resolveNotation("
1391          +notationName+","+publicId+","+systemId+")");
1392
1393    systemId = normalizeURI(systemId);
1394
1395    if (publicId != null && publicId.startsWith("urn:publicid:")) {
1396      publicId = PublicId.decodeURN(publicId);
1397    }
1398
1399    if (systemId != null && systemId.startsWith("urn:publicid:")) {
1400      systemId = PublicId.decodeURN(systemId);
1401      if (publicId != null && !publicId.equals(systemId)) {
1402    catalogManager.debug.message(1, "urn:publicid: system identifier differs from public identifier; using public identifier");
1403    systemId = null;
1404      } else {
1405    publicId = systemId;
1406    systemId = null;
1407      }
1408    }
1409
1410    if (systemId != null) {
1411      // If there's a SYSTEM entry in this catalog, use it
1412
resolved = resolveLocalSystem(systemId);
1413      if (resolved != null) {
1414    return resolved;
1415      }
1416    }
1417
1418    if (publicId != null) {
1419      // If there's a PUBLIC entry in this catalog, use it
1420
resolved = resolveLocalPublic(NOTATION,
1421                    notationName,
1422                    publicId,
1423                    systemId);
1424      if (resolved != null) {
1425    return resolved;
1426      }
1427    }
1428
1429    // If there's a NOTATION entry in this catalog, use it
1430
boolean over = default_override;
1431    Enumeration JavaDoc enumt = catalogEntries.elements();
1432    while (enumt.hasMoreElements()) {
1433      CatalogEntry e = (CatalogEntry) enumt.nextElement();
1434      if (e.getEntryType() == OVERRIDE) {
1435    over = e.getEntryArg(0).equalsIgnoreCase("YES");
1436    continue;
1437      }
1438
1439      if (e.getEntryType() == NOTATION
1440      && e.getEntryArg(0).equals(notationName)) {
1441    if (over || systemId == null) {
1442      return e.getEntryArg(1);
1443    }
1444      }
1445    }
1446
1447    // Otherwise, look in the subordinate catalogs
1448
return resolveSubordinateCatalogs(NOTATION,
1449                      notationName,
1450                      publicId,
1451                      systemId);
1452  }
1453
1454  /**
1455   * Return the applicable PUBLIC or SYSTEM identifier.
1456   *
1457   * <p>This method searches the Catalog and returns the system
1458   * identifier specified for the given system or
1459   * public identifiers. If
1460   * no appropriate PUBLIC or SYSTEM entry is found in the Catalog,
1461   * null is returned.</p>
1462   *
1463   * @param publicId The public identifier to locate in the catalog.
1464   * Public identifiers are normalized before comparison.
1465   * @param systemId The nominal system identifier for the entity
1466   * in question (as provided in the source document).
1467   *
1468   * @throws MalformedURLException The formal system identifier of a
1469   * subordinate catalog cannot be turned into a valid URL.
1470   * @throws IOException Error reading subordinate catalog file.
1471   *
1472   * @return The system identifier to use.
1473   * Note that the nominal system identifier is not returned if a
1474   * match is not found in the catalog, instead null is returned
1475   * to indicate that no match was found.
1476   */

1477  public String JavaDoc resolvePublic(String JavaDoc publicId, String JavaDoc systemId)
1478    throws MalformedURLException JavaDoc, IOException JavaDoc {
1479
1480    catalogManager.debug.message(3, "resolvePublic("+publicId+","+systemId+")");
1481
1482    systemId = normalizeURI(systemId);
1483
1484    if (publicId != null && publicId.startsWith("urn:publicid:")) {
1485      publicId = PublicId.decodeURN(publicId);
1486    }
1487
1488    if (systemId != null && systemId.startsWith("urn:publicid:")) {
1489      systemId = PublicId.decodeURN(systemId);
1490      if (publicId != null && !publicId.equals(systemId)) {
1491    catalogManager.debug.message(1, "urn:publicid: system identifier differs from public identifier; using public identifier");
1492    systemId = null;
1493      } else {
1494    publicId = systemId;
1495    systemId = null;
1496      }
1497    }
1498
1499    // If there's a SYSTEM entry in this catalog, use it
1500
if (systemId != null) {
1501      String JavaDoc resolved = resolveLocalSystem(systemId);
1502      if (resolved != null) {
1503    return resolved;
1504      }
1505    }
1506
1507    // If there's a PUBLIC entry in this catalog, use it
1508
String JavaDoc resolved = resolveLocalPublic(PUBLIC,
1509                     null,
1510                     publicId,
1511                     systemId);
1512    if (resolved != null) {
1513      return resolved;
1514    }
1515
1516    // Otherwise, look in the subordinate catalogs
1517
return resolveSubordinateCatalogs(PUBLIC,
1518                      null,
1519                      publicId,
1520                      systemId);
1521  }
1522
1523  /**
1524   * Return the applicable PUBLIC or SYSTEM identifier.
1525   *
1526   * <p>This method searches the Catalog and returns the system
1527   * identifier specified for the given system or public identifiers.
1528   * If no appropriate PUBLIC or SYSTEM entry is found in the Catalog,
1529   * delegated Catalogs are interrogated.</p>
1530   *
1531   * <p>There are four possible cases:</p>
1532   *
1533   * <ul>
1534   * <li>If the system identifier provided matches a SYSTEM entry
1535   * in the current catalog, the SYSTEM entry is returned.
1536   * <li>If the system identifier is not null, the PUBLIC entries
1537   * that were encountered when OVERRIDE YES was in effect are
1538   * interrogated and the first matching entry is returned.</li>
1539   * <li>If the system identifier is null, then all of the PUBLIC
1540   * entries are interrogated and the first matching entry
1541   * is returned. This may not be the same as the preceding case, if
1542   * some PUBLIC entries are encountered when OVERRIDE NO is in effect. In
1543   * XML, the only place where a public identifier may occur without
1544   * a system identifier is in a notation declaration.</li>
1545   * <li>Finally, if the public identifier matches one of the partial
1546   * public identifiers specified in a DELEGATE* entry in
1547   * the Catalog, the delegated catalog is interrogated. The first
1548   * time that the delegated catalog is required, it will be
1549   * retrieved and parsed. It is subsequently cached.
1550   * </li>
1551   * </ul>
1552   *
1553   * @param entityType The CatalogEntry type for which this query is
1554   * being conducted. This is necessary in order to do the approprate
1555   * query on a delegated catalog.
1556   * @param entityName The name of the entity being searched for, if
1557   * appropriate.
1558   * @param publicId The public identifier of the entity in question.
1559   * @param systemId The nominal system identifier for the entity
1560   * in question (as provided in the source document).
1561   *
1562   * @throws MalformedURLException The formal system identifier of a
1563   * delegated catalog cannot be turned into a valid URL.
1564   * @throws IOException Error reading delegated catalog file.
1565   *
1566   * @return The system identifier to use.
1567   * Note that the nominal system identifier is not returned if a
1568   * match is not found in the catalog, instead null is returned
1569   * to indicate that no match was found.
1570   */

1571  protected synchronized String JavaDoc resolveLocalPublic(int entityType,
1572                           String JavaDoc entityName,
1573                           String JavaDoc publicId,
1574                           String JavaDoc systemId)
1575    throws MalformedURLException JavaDoc, IOException JavaDoc {
1576
1577    // Always normalize the public identifier before attempting a match
1578
publicId = PublicId.normalize(publicId);
1579
1580    // If there's a SYSTEM entry in this catalog, use it
1581
if (systemId != null) {
1582      String JavaDoc resolved = resolveLocalSystem(systemId);
1583      if (resolved != null) {
1584    return resolved;
1585      }
1586    }
1587
1588    // If there's a PUBLIC entry in this catalog, use it
1589
boolean over = default_override;
1590    Enumeration JavaDoc enumt = catalogEntries.elements();
1591    while (enumt.hasMoreElements()) {
1592      CatalogEntry e = (CatalogEntry) enumt.nextElement();
1593      if (e.getEntryType() == OVERRIDE) {
1594    over = e.getEntryArg(0).equalsIgnoreCase("YES");
1595    continue;
1596      }
1597
1598      if (e.getEntryType() == PUBLIC
1599      && e.getEntryArg(0).equals(publicId)) {
1600    if (over || systemId == null) {
1601      return e.getEntryArg(1);
1602    }
1603      }
1604    }
1605
1606    // If there's a DELEGATE_PUBLIC entry in this catalog, use it
1607
over = default_override;
1608    enumt = catalogEntries.elements();
1609    Vector JavaDoc delCats = new Vector JavaDoc();
1610    while (enumt.hasMoreElements()) {
1611      CatalogEntry e = (CatalogEntry) enumt.nextElement();
1612      if (e.getEntryType() == OVERRIDE) {
1613    over = e.getEntryArg(0).equalsIgnoreCase("YES");
1614    continue;
1615      }
1616
1617      if (e.getEntryType() == DELEGATE_PUBLIC
1618      && (over || systemId == null)) {
1619    String JavaDoc p = (String JavaDoc) e.getEntryArg(0);
1620    if (p.length() <= publicId.length()
1621        && p.equals(publicId.substring(0, p.length()))) {
1622      // delegate this match to the other catalog
1623

1624      delCats.addElement(e.getEntryArg(1));
1625    }
1626      }
1627    }
1628
1629    if (delCats.size() > 0) {
1630      Enumeration JavaDoc enumCats = delCats.elements();
1631
1632      if (catalogManager.debug.getDebug() > 1) {
1633    catalogManager.debug.message(2, "Switching to delegated catalog(s):");
1634    while (enumCats.hasMoreElements()) {
1635      String JavaDoc delegatedCatalog = (String JavaDoc) enumCats.nextElement();
1636      catalogManager.debug.message(2, "\t" + delegatedCatalog);
1637    }
1638      }
1639
1640      Catalog dcat = newCatalog();
1641
1642      enumCats = delCats.elements();
1643      while (enumCats.hasMoreElements()) {
1644    String JavaDoc delegatedCatalog = (String JavaDoc) enumCats.nextElement();
1645    dcat.parseCatalog(delegatedCatalog);
1646      }
1647
1648      return dcat.resolvePublic(publicId, null);
1649    }
1650
1651    // Nada!
1652
return null;
1653  }
1654
1655  /**
1656   * Return the applicable SYSTEM system identifier.
1657   *
1658   * <p>If a SYSTEM entry exists in the Catalog
1659   * for the system ID specified, return the mapped value.</p>
1660   *
1661   * <p>On Windows-based operating systems, the comparison between
1662   * the system identifier provided and the SYSTEM entries in the
1663   * Catalog is case-insensitive.</p>
1664   *
1665   * @param systemId The system ID to locate in the catalog.
1666   *
1667   * @return The resolved system identifier.
1668   *
1669   * @throws MalformedURLException The formal system identifier of a
1670   * subordinate catalog cannot be turned into a valid URL.
1671   * @throws IOException Error reading subordinate catalog file.
1672   */

1673  public String JavaDoc resolveSystem(String JavaDoc systemId)
1674    throws MalformedURLException JavaDoc, IOException JavaDoc {
1675
1676    catalogManager.debug.message(3, "resolveSystem("+systemId+")");
1677
1678    systemId = normalizeURI(systemId);
1679
1680    if (systemId != null && systemId.startsWith("urn:publicid:")) {
1681      systemId = PublicId.decodeURN(systemId);
1682      return resolvePublic(systemId, null);
1683    }
1684
1685    // If there's a SYSTEM entry in this catalog, use it
1686
if (systemId != null) {
1687      String JavaDoc resolved = resolveLocalSystem(systemId);
1688      if (resolved != null) {
1689    return resolved;
1690      }
1691    }
1692
1693    // Otherwise, look in the subordinate catalogs
1694
return resolveSubordinateCatalogs(SYSTEM,
1695                      null,
1696                      null,
1697                      systemId);
1698  }
1699
1700  /**
1701   * Return the applicable SYSTEM system identifier in this
1702   * catalog.
1703   *
1704   * <p>If a SYSTEM entry exists in the catalog file
1705   * for the system ID specified, return the mapped value.</p>
1706   *
1707   * @param systemId The system ID to locate in the catalog
1708   *
1709   * @return The mapped system identifier or null
1710   */

1711  protected String JavaDoc resolveLocalSystem(String JavaDoc systemId)
1712    throws MalformedURLException JavaDoc, IOException JavaDoc {
1713
1714    String JavaDoc osname = System.getProperty("os.name");
1715    boolean windows = (osname.indexOf("Windows") >= 0);
1716    Enumeration JavaDoc enumt = catalogEntries.elements();
1717    while (enumt.hasMoreElements()) {
1718      CatalogEntry e = (CatalogEntry) enumt.nextElement();
1719      if (e.getEntryType() == SYSTEM
1720      && (e.getEntryArg(0).equals(systemId)
1721          || (windows
1722          && e.getEntryArg(0).equalsIgnoreCase(systemId)))) {
1723    return e.getEntryArg(1);
1724      }
1725    }
1726
1727    // If there's a REWRITE_SYSTEM entry in this catalog, use it
1728
enumt = catalogEntries.elements();
1729    String JavaDoc startString = null;
1730    String JavaDoc prefix = null;
1731    while (enumt.hasMoreElements()) {
1732      CatalogEntry e = (CatalogEntry) enumt.nextElement();
1733
1734      if (e.getEntryType() == REWRITE_SYSTEM) {
1735    String JavaDoc p = (String JavaDoc) e.getEntryArg(0);
1736    if (p.length() <= systemId.length()
1737        && p.equals(systemId.substring(0, p.length()))) {
1738      // Is this the longest prefix?
1739
if (startString == null
1740          || p.length() > startString.length()) {
1741        startString = p;
1742        prefix = e.getEntryArg(1);
1743      }
1744    }
1745      }
1746
1747      if (prefix != null) {
1748    // return the systemId with the new prefix
1749
return prefix + systemId.substring(startString.length());
1750      }
1751    }
1752
1753    // If there's a DELEGATE_SYSTEM entry in this catalog, use it
1754
enumt = catalogEntries.elements();
1755    Vector JavaDoc delCats = new Vector JavaDoc();
1756    while (enumt.hasMoreElements()) {
1757      CatalogEntry e = (CatalogEntry) enumt.nextElement();
1758
1759      if (e.getEntryType() == DELEGATE_SYSTEM) {
1760    String JavaDoc p = (String JavaDoc) e.getEntryArg(0);
1761    if (p.length() <= systemId.length()
1762        && p.equals(systemId.substring(0, p.length()))) {
1763      // delegate this match to the other catalog
1764

1765      delCats.addElement(e.getEntryArg(1));
1766    }
1767      }
1768    }
1769
1770    if (delCats.size() > 0) {
1771      Enumeration JavaDoc enumCats = delCats.elements();
1772
1773      if (catalogManager.debug.getDebug() > 1) {
1774    catalogManager.debug.message(2, "Switching to delegated catalog(s):");
1775    while (enumCats.hasMoreElements()) {
1776      String JavaDoc delegatedCatalog = (String JavaDoc) enumCats.nextElement();
1777      catalogManager.debug.message(2, "\t" + delegatedCatalog);
1778    }
1779      }
1780
1781      Catalog dcat = newCatalog();
1782
1783      enumCats = delCats.elements();
1784      while (enumCats.hasMoreElements()) {
1785    String JavaDoc delegatedCatalog = (String JavaDoc) enumCats.nextElement();
1786    dcat.parseCatalog(delegatedCatalog);
1787      }
1788
1789      return dcat.resolveSystem(systemId);
1790    }
1791
1792    return null;
1793  }
1794
1795  /**
1796   * Return the applicable URI.
1797   *
1798   * <p>If a URI entry exists in the Catalog
1799   * for the URI specified, return the mapped value.</p>
1800   *
1801   * <p>URI comparison is case sensitive.</p>
1802   *
1803   * @param uri The URI to locate in the catalog.
1804   *
1805   * @return The resolved URI.
1806   *
1807   * @throws MalformedURLException The system identifier of a
1808   * subordinate catalog cannot be turned into a valid URL.
1809   * @throws IOException Error reading subordinate catalog file.
1810   */

1811  public String JavaDoc resolveURI(String JavaDoc uri)
1812    throws MalformedURLException JavaDoc, IOException JavaDoc {
1813
1814    catalogManager.debug.message(3, "resolveURI("+uri+")");
1815
1816    uri = normalizeURI(uri);
1817
1818    if (uri != null && uri.startsWith("urn:publicid:")) {
1819      uri = PublicId.decodeURN(uri);
1820      return resolvePublic(uri, null);
1821    }
1822
1823    // If there's a URI entry in this catalog, use it
1824
if (uri != null) {
1825      String JavaDoc resolved = resolveLocalURI(uri);
1826      if (resolved != null) {
1827    return resolved;
1828      }
1829    }
1830
1831    // Otherwise, look in the subordinate catalogs
1832
return resolveSubordinateCatalogs(URI,
1833                      null,
1834                      null,
1835                      uri);
1836  }
1837
1838  /**
1839   * Return the applicable URI in this catalog.
1840   *
1841   * <p>If a URI entry exists in the catalog file
1842   * for the URI specified, return the mapped value.</p>
1843   *
1844   * @param uri The URI to locate in the catalog
1845   *
1846   * @return The mapped URI or null
1847   */

1848  protected String JavaDoc resolveLocalURI(String JavaDoc uri)
1849    throws MalformedURLException JavaDoc, IOException JavaDoc {
1850    Enumeration JavaDoc enumt = catalogEntries.elements();
1851    while (enumt.hasMoreElements()) {
1852      CatalogEntry e = (CatalogEntry) enumt.nextElement();
1853      if (e.getEntryType() == URI
1854      && (e.getEntryArg(0).equals(uri))) {
1855    return e.getEntryArg(1);
1856      }
1857    }
1858
1859    // If there's a REWRITE_URI entry in this catalog, use it
1860
enumt = catalogEntries.elements();
1861    String JavaDoc startString = null;
1862    String JavaDoc prefix = null;
1863    while (enumt.hasMoreElements()) {
1864      CatalogEntry e = (CatalogEntry) enumt.nextElement();
1865
1866      if (e.getEntryType() == REWRITE_URI) {
1867    String JavaDoc p = (String JavaDoc) e.getEntryArg(0);
1868    if (p.length() <= uri.length()
1869        && p.equals(uri.substring(0, p.length()))) {
1870      // Is this the longest prefix?
1871
if (startString == null
1872          || p.length() > startString.length()) {
1873        startString = p;
1874        prefix = e.getEntryArg(1);
1875      }
1876    }
1877      }
1878
1879      if (prefix != null) {
1880    // return the systemId with the new prefix
1881
return prefix + uri.substring(startString.length());
1882      }
1883    }
1884
1885    // If there's a DELEGATE_URI entry in this catalog, use it
1886
enumt = catalogEntries.elements();
1887    Vector JavaDoc delCats = new Vector JavaDoc();
1888    while (enumt.hasMoreElements()) {
1889      CatalogEntry e = (CatalogEntry) enumt.nextElement();
1890
1891      if (e.getEntryType() == DELEGATE_URI) {
1892    String JavaDoc p = (String JavaDoc) e.getEntryArg(0);
1893    if (p.length() <= uri.length()
1894        && p.equals(uri.substring(0, p.length()))) {
1895      // delegate this match to the other catalog
1896

1897      delCats.addElement(e.getEntryArg(1));
1898    }
1899      }
1900    }
1901
1902    if (delCats.size() > 0) {
1903      Enumeration JavaDoc enumCats = delCats.elements();
1904
1905      if (catalogManager.debug.getDebug() > 1) {
1906    catalogManager.debug.message(2, "Switching to delegated catalog(s):");
1907    while (enumCats.hasMoreElements()) {
1908      String JavaDoc delegatedCatalog = (String JavaDoc) enumCats.nextElement();
1909      catalogManager.debug.message(2, "\t" + delegatedCatalog);
1910    }
1911      }
1912
1913      Catalog dcat = newCatalog();
1914
1915      enumCats = delCats.elements();
1916      while (enumCats.hasMoreElements()) {
1917    String JavaDoc delegatedCatalog = (String JavaDoc) enumCats.nextElement();
1918    dcat.parseCatalog(delegatedCatalog);
1919      }
1920
1921      return dcat.resolveURI(uri);
1922    }
1923
1924    return null;
1925  }
1926
1927  /**
1928   * Search the subordinate catalogs, in order, looking for a match.
1929   *
1930   * <p>This method searches the Catalog and returns the system
1931   * identifier specified for the given entity type with the given
1932   * name, public, and system identifiers. In some contexts, these
1933   * may be null.</p>
1934   *
1935   * @param entityType The CatalogEntry type for which this query is
1936   * being conducted. This is necessary in order to do the approprate
1937   * query on a subordinate catalog.
1938   * @param entityName The name of the entity being searched for, if
1939   * appropriate.
1940   * @param publicId The public identifier of the entity in question
1941   * (as provided in the source document).
1942   * @param systemId The nominal system identifier for the entity
1943   * in question (as provided in the source document). This parameter is
1944   * overloaded for the URI entry type.
1945   *
1946   * @throws MalformedURLException The formal system identifier of a
1947   * delegated catalog cannot be turned into a valid URL.
1948   * @throws IOException Error reading delegated catalog file.
1949   *
1950   * @return The system identifier to use.
1951   * Note that the nominal system identifier is not returned if a
1952   * match is not found in the catalog, instead null is returned
1953   * to indicate that no match was found.
1954   */

1955  protected synchronized String JavaDoc resolveSubordinateCatalogs(int entityType,
1956                               String JavaDoc entityName,
1957                               String JavaDoc publicId,
1958                               String JavaDoc systemId)
1959    throws MalformedURLException JavaDoc, IOException JavaDoc {
1960
1961    for (int catPos = 0; catPos < catalogs.size(); catPos++) {
1962      Catalog c = null;
1963
1964      try {
1965    c = (Catalog) catalogs.elementAt(catPos);
1966      } catch (ClassCastException JavaDoc e) {
1967    String JavaDoc catfile = (String JavaDoc) catalogs.elementAt(catPos);
1968    c = newCatalog();
1969
1970    try {
1971      c.parseCatalog(catfile);
1972    } catch (MalformedURLException JavaDoc mue) {
1973      catalogManager.debug.message(1, "Malformed Catalog URL", catfile);
1974    } catch (FileNotFoundException JavaDoc fnfe) {
1975      catalogManager.debug.message(1, "Failed to load catalog, file not found",
1976            catfile);
1977    } catch (IOException JavaDoc ioe) {
1978      catalogManager.debug.message(1, "Failed to load catalog, I/O error", catfile);
1979    }
1980
1981    catalogs.setElementAt(c, catPos);
1982      }
1983
1984      String JavaDoc resolved = null;
1985
1986      // Ok, now what are we supposed to call here?
1987
if (entityType == DOCTYPE) {
1988    resolved = c.resolveDoctype(entityName,
1989                    publicId,
1990                    systemId);
1991      } else if (entityType == DOCUMENT) {
1992    resolved = c.resolveDocument();
1993      } else if (entityType == ENTITY) {
1994    resolved = c.resolveEntity(entityName,
1995                   publicId,
1996                   systemId);
1997      } else if (entityType == NOTATION) {
1998    resolved = c.resolveNotation(entityName,
1999                     publicId,
2000                     systemId);
2001      } else if (entityType == PUBLIC) {
2002    resolved = c.resolvePublic(publicId, systemId);
2003      } else if (entityType == SYSTEM) {
2004    resolved = c.resolveSystem(systemId);
2005      } else if (entityType == URI) {
2006    resolved = c.resolveURI(systemId);
2007      }
2008
2009      if (resolved != null) {
2010    return resolved;
2011      }
2012    }
2013
2014    return null;
2015  }
2016
2017  // -----------------------------------------------------------------
2018

2019  /**
2020   * Replace backslashes with forward slashes. (URLs always use
2021   * forward slashes.)
2022   *
2023   * @param sysid The input system identifier.
2024   * @return The same system identifier with backslashes turned into
2025   * forward slashes.
2026   */

2027  protected String JavaDoc fixSlashes (String JavaDoc sysid) {
2028    return sysid.replace('\\', '/');
2029  }
2030
2031  /**
2032   * Construct an absolute URI from a relative one, using the current
2033   * base URI.
2034   *
2035   * @param sysid The (possibly relative) system identifier
2036   * @return The system identifier made absolute with respect to the
2037   * current {@link #base}.
2038   */

2039  protected String JavaDoc makeAbsolute(String JavaDoc sysid) {
2040    URL JavaDoc local = null;
2041
2042    sysid = fixSlashes(sysid);
2043
2044    try {
2045      local = new URL JavaDoc(base, sysid);
2046    } catch (MalformedURLException JavaDoc e) {
2047      catalogManager.debug.message(1, "Malformed URL on system identifier", sysid);
2048    }
2049
2050    if (local != null) {
2051      return local.toString();
2052    } else {
2053      return sysid;
2054    }
2055  }
2056
2057  /**
2058   * Perform character normalization on a URI reference.
2059   *
2060   * @param uriref The URI reference
2061   * @return The normalized URI reference.
2062   */

2063  protected String JavaDoc normalizeURI(String JavaDoc uriref) {
2064    String JavaDoc newRef = "";
2065    byte[] bytes;
2066
2067    if (uriref == null) {
2068      return null;
2069    }
2070
2071    try {
2072      bytes = uriref.getBytes("UTF-8");
2073    } catch (UnsupportedEncodingException JavaDoc uee) {
2074      // this can't happen
2075
catalogManager.debug.message(1, "UTF-8 is an unsupported encoding!?");
2076      return uriref;
2077    }
2078
2079    for (int count = 0; count < bytes.length; count++) {
2080      int ch = bytes[count] & 0xFF;
2081
2082      if ((ch <= 0x20) // ctrl
2083
|| (ch > 0x7F) // high ascii
2084
|| (ch == 0x22) // "
2085
|| (ch == 0x3C) // <
2086
|| (ch == 0x3E) // >
2087
|| (ch == 0x5C) // \
2088
|| (ch == 0x5E) // ^
2089
|| (ch == 0x60) // `
2090
|| (ch == 0x7B) // {
2091
|| (ch == 0x7C) // |
2092
|| (ch == 0x7D) // }
2093
|| (ch == 0x7F)) {
2094    newRef += encodedByte(ch);
2095      } else {
2096    newRef += (char) bytes[count];
2097      }
2098    }
2099
2100    return newRef;
2101  }
2102
2103  /**
2104   * Perform %-encoding on a single byte.
2105   *
2106   * @param b The 8-bit integer that represents th byte. (Bytes are signed
2107              but encoding needs to look at the bytes unsigned.)
2108   * @return The %-encoded string for the byte in question.
2109   */

2110  protected String JavaDoc encodedByte (int b) {
2111    String JavaDoc hex = Integer.toHexString(b).toUpperCase();
2112    if (hex.length() < 2) {
2113      return "%0" + hex;
2114    } else {
2115      return "%" + hex;
2116    }
2117  }
2118
2119  // -----------------------------------------------------------------
2120

2121  /**
2122   * Add to the current list of delegated catalogs.
2123   *
2124   * <p>This method always constructs the {@link #localDelegate}
2125   * vector so that it is ordered by length of partial
2126   * public identifier.</p>
2127   *
2128   * @param entry The DELEGATE catalog entry
2129   */

2130  protected void addDelegate(CatalogEntry entry) {
2131    int pos = 0;
2132    String JavaDoc partial = entry.getEntryArg(0);
2133
2134    Enumeration JavaDoc local = localDelegate.elements();
2135    while (local.hasMoreElements()) {
2136      CatalogEntry dpe = (CatalogEntry) local.nextElement();
2137      String JavaDoc dp = dpe.getEntryArg(0);
2138      if (dp.equals(partial)) {
2139    // we already have this prefix
2140
return;
2141      }
2142      if (dp.length() > partial.length()) {
2143    pos++;
2144      }
2145      if (dp.length() < partial.length()) {
2146    break;
2147      }
2148    }
2149
2150    // now insert partial into the vector at [pos]
2151
if (localDelegate.size() == 0) {
2152      localDelegate.addElement(entry);
2153    } else {
2154      localDelegate.insertElementAt(entry, pos);
2155    }
2156  }
2157}
2158
2159
Popular Tags