KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xml > internal > resolver > Catalog


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

3 /*
4  * Copyright 2001-2004 The Apache Software Foundation or its licensors,
5  * as applicable.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */

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

192 public class Catalog {
193   /** The BASE Catalog Entry type. */
194   public static final int BASE = CatalogEntry.addEntryType("BASE", 1);
195
196   /** The CATALOG Catalog Entry type. */
197   public static final int CATALOG = CatalogEntry.addEntryType("CATALOG", 1);
198
199   /** The DOCUMENT Catalog Entry type. */
200   public static final int DOCUMENT = CatalogEntry.addEntryType("DOCUMENT", 1);
201
202   /** The OVERRIDE Catalog Entry type. */
203   public static final int OVERRIDE = CatalogEntry.addEntryType("OVERRIDE", 1);
204
205   /** The SGMLDECL Catalog Entry type. */
206   public static final int SGMLDECL = CatalogEntry.addEntryType("SGMLDECL", 1);
207
208   /** The DELEGATE_PUBLIC Catalog Entry type. */
209   public static final int DELEGATE_PUBLIC = CatalogEntry.addEntryType("DELEGATE_PUBLIC", 2);
210
211   /** The DELEGATE_SYSTEM Catalog Entry type. */
212   public static final int DELEGATE_SYSTEM = CatalogEntry.addEntryType("DELEGATE_SYSTEM", 2);
213
214   /** The DELEGATE_URI Catalog Entry type. */
215   public static final int DELEGATE_URI = CatalogEntry.addEntryType("DELEGATE_URI", 2);
216
217   /** The DOCTYPE Catalog Entry type. */
218   public static final int DOCTYPE = CatalogEntry.addEntryType("DOCTYPE", 2);
219
220   /** The DTDDECL Catalog Entry type. */
221   public static final int DTDDECL = CatalogEntry.addEntryType("DTDDECL", 2);
222
223   /** The ENTITY Catalog Entry type. */
224   public static final int ENTITY = CatalogEntry.addEntryType("ENTITY", 2);
225
226   /** The LINKTYPE Catalog Entry type. */
227   public static final int LINKTYPE = CatalogEntry.addEntryType("LINKTYPE", 2);
228
229   /** The NOTATION Catalog Entry type. */
230   public static final int NOTATION = CatalogEntry.addEntryType("NOTATION", 2);
231
232   /** The PUBLIC Catalog Entry type. */
233   public static final int PUBLIC = CatalogEntry.addEntryType("PUBLIC", 2);
234
235   /** The SYSTEM Catalog Entry type. */
236   public static final int SYSTEM = CatalogEntry.addEntryType("SYSTEM", 2);
237
238   /** The URI Catalog Entry type. */
239   public static final int URI = CatalogEntry.addEntryType("URI", 2);
240
241   /** The REWRITE_SYSTEM Catalog Entry type. */
242   public static final int REWRITE_SYSTEM = CatalogEntry.addEntryType("REWRITE_SYSTEM", 2);
243
244   /** The REWRITE_URI Catalog Entry type. */
245   public static final int REWRITE_URI = CatalogEntry.addEntryType("REWRITE_URI", 2);
246   /** The SYSTEM_SUFFIX Catalog Entry type. */
247   public static final int SYSTEM_SUFFIX = CatalogEntry.addEntryType("SYSTEM_SUFFIX", 2);
248   /** The URI_SUFFIX Catalog Entry type. */
249   public static final int URI_SUFFIX = CatalogEntry.addEntryType("URI_SUFFIX", 2);
250
251   /**
252    * The base URI for relative system identifiers in the catalog.
253    * This may be changed by BASE entries in the catalog.
254    */

255   protected URL JavaDoc base;
256
257   /** The base URI of the Catalog file currently being parsed. */
258   protected URL JavaDoc catalogCwd;
259
260   /** The catalog entries currently known to the system. */
261   protected Vector JavaDoc catalogEntries = new Vector JavaDoc();
262
263   /** The default initial override setting. */
264   protected boolean default_override = true;
265
266   /** The catalog manager in use for this instance. */
267   protected CatalogManager catalogManager = CatalogManager.getStaticManager();
268
269   /**
270    * A vector of catalog files to be loaded.
271    *
272    * <p>This list is initially established by
273    * <code>loadSystemCatalogs</code> when
274    * it parses the system catalog list, but CATALOG entries may
275    * contribute to it during the course of parsing.</p>
276    *
277    * @see #loadSystemCatalogs
278    * @see #localCatalogFiles
279    */

280   protected Vector JavaDoc catalogFiles = new Vector JavaDoc();
281
282   /**
283    * A vector of catalog files constructed during processing of
284    * CATALOG entries in the current catalog.
285    *
286    * <p>This two-level system is actually necessary to correctly implement
287    * the semantics of the CATALOG entry. If one catalog file includes
288    * another with a CATALOG entry, the included catalog logically
289    * occurs <i>at the end</i> of the including catalog, and after any
290    * preceding CATALOG entries. In other words, the CATALOG entry
291    * cannot insert anything into the middle of a catalog file.</p>
292    *
293    * <p>When processing reaches the end of each catalog files, any
294    * elements on this vector are added to the front of the
295    * <code>catalogFiles</code> vector.</p>
296    *
297    * @see #catalogFiles
298    */

299   protected Vector JavaDoc localCatalogFiles = new Vector JavaDoc();
300
301   /**
302    * A vector of Catalogs.
303    *
304    * <p>The semantics of Catalog resolution are such that each
305    * catalog is effectively a list of Catalogs (in other words,
306    * a recursive list of Catalog instances).</p>
307    *
308    * <p>Catalogs that are processed as the result of CATALOG or
309    * DELEGATE* entries are subordinate to the catalog that contained
310    * them, but they may in turn have subordinate catalogs.</p>
311    *
312    * <p>Catalogs are only loaded when they are needed, so this vector
313    * initially contains a list of Catalog filenames (URLs). If, during
314    * processing, one of these catalogs has to be loaded, the resulting
315    * Catalog object is placed in the vector, effectively caching it
316    * for the next query.</p>
317    */

318   protected Vector JavaDoc catalogs = new Vector JavaDoc();
319
320   /**
321    * A vector of DELEGATE* Catalog entries constructed during
322    * processing of the Catalog.
323    *
324    * <p>This two-level system has two purposes; first, it allows
325    * us to sort the DELEGATE* entries by the length of the partial
326    * public identifier so that a linear search encounters them in
327    * the correct order and second, it puts them all at the end of
328    * the Catalog.</p>
329    *
330    * <p>When processing reaches the end of each catalog file, any
331    * elements on this vector are added to the end of the
332    * <code>catalogEntries</code> vector. This assures that matching
333    * PUBLIC keywords are encountered before DELEGATE* entries.</p>
334    */

335   protected Vector JavaDoc localDelegate = new Vector JavaDoc();
336
337   /**
338    * A hash of CatalogReaders.
339    *
340    * <p>This hash maps MIME types to elements in the readerArr
341    * vector. This allows the Catalog to quickly locate the reader
342    * for a particular MIME type.</p>
343    */

344   protected Hashtable JavaDoc readerMap = new Hashtable JavaDoc();
345
346   /**
347    * A vector of CatalogReaders.
348    *
349    * <p>This vector contains all of the readers in the order that they
350    * were added. In the event that a catalog is read from a file, where
351    * the MIME type is unknown, each reader is attempted in turn until
352    * one succeeds.</p>
353    */

354   protected Vector JavaDoc readerArr = new Vector JavaDoc();
355
356   /**
357    * Constructs an empty Catalog.
358    *
359    * <p>The constructor interrogates the relevant system properties
360    * using the default (static) CatalogManager
361    * and initializes the catalog data structures.</p>
362    */

363   public Catalog() {
364     // nop;
365
}
366
367   /**
368    * Constructs an empty Catalog with a specific CatalogManager.
369    *
370    * <p>The constructor interrogates the relevant system properties
371    * using the specified Catalog Manager
372    * and initializes the catalog data structures.</p>
373    */

374   public Catalog(CatalogManager manager) {
375     catalogManager = manager;
376   }
377
378   /**
379    * Return the CatalogManager used by this catalog.
380    *
381    */

382   public CatalogManager getCatalogManager() {
383     return catalogManager;
384   }
385
386   /**
387    * Establish the CatalogManager used by this catalog.
388    *
389    */

390   public void setCatalogManager(CatalogManager manager) {
391     catalogManager = manager;
392   }
393
394   /**
395    * Setup readers.
396    */

397   public void setupReaders() {
398     SAXParserFactory JavaDoc spf = SAXParserFactory.newInstance();
399     spf.setNamespaceAware(true);
400     spf.setValidating(false);
401
402     SAXCatalogReader saxReader = new SAXCatalogReader(spf);
403
404     saxReader.setCatalogParser(null, "XMLCatalog",
405                    "com.sun.org.apache.xml.internal.resolver.readers.XCatalogReader");
406
407     saxReader.setCatalogParser(OASISXMLCatalogReader.namespaceName,
408                    "catalog",
409                    "com.sun.org.apache.xml.internal.resolver.readers.OASISXMLCatalogReader");
410
411     addReader("application/xml", saxReader);
412
413     TR9401CatalogReader textReader = new TR9401CatalogReader();
414     addReader("text/plain", textReader);
415   }
416
417   /**
418    * Add a new CatalogReader to the Catalog.
419    *
420    * <p>This method allows you to add a new CatalogReader to the
421    * catalog. The reader will be associated with the specified mimeType.
422    * You can only have one reader per mimeType.</p>
423    *
424    * <p>In the absence of a mimeType (e.g., when reading a catalog
425    * directly from a file on the local system), the readers are attempted
426    * in the order that you add them to the Catalog.</p>
427    *
428    * <p>Note that subordinate catalogs (created by CATALOG or
429    * DELEGATE* entries) get a copy of the set of readers present in
430    * the primary catalog when they are created. Readers added subsequently
431    * will not be available. For this reason, it is best to add all
432    * of the readers before the first call to parse a catalog.</p>
433    *
434    * @param mimeType The MIME type associated with this reader.
435    * @param reader The CatalogReader to use.
436    */

437   public void addReader(String JavaDoc mimeType, CatalogReader reader) {
438     if (readerMap.containsKey(mimeType)) {
439       Integer JavaDoc pos = (Integer JavaDoc) readerMap.get(mimeType);
440       readerArr.set(pos.intValue(), reader);
441     } else {
442       readerArr.add(reader);
443       Integer JavaDoc pos = new Integer JavaDoc(readerArr.size()-1);
444       readerMap.put(mimeType, pos);
445     }
446   }
447
448   /**
449    * Copies the reader list from the current Catalog to a new Catalog.
450    *
451    * <p>This method is used internally when constructing a new catalog.
452    * It copies the current reader associations over to the new catalog.
453    * </p>
454    *
455    * @param newCatalog The new Catalog.
456    */

457   protected void copyReaders(Catalog newCatalog) {
458     // Have to copy the readers in the right order...convert hash to arr
459
Vector JavaDoc mapArr = new Vector JavaDoc(readerMap.size());
460
461     // Pad the mapArr out to the right length
462
for (int count = 0; count < readerMap.size(); count++) {
463       mapArr.add(null);
464     }
465
466     Enumeration JavaDoc en = readerMap.keys();
467     while (en.hasMoreElements()) {
468       String JavaDoc mimeType = (String JavaDoc) en.nextElement();
469       Integer JavaDoc pos = (Integer JavaDoc) readerMap.get(mimeType);
470       mapArr.set(pos.intValue(), mimeType);
471     }
472
473     for (int count = 0; count < mapArr.size(); count++) {
474       String JavaDoc mimeType = (String JavaDoc) mapArr.get(count);
475       Integer JavaDoc pos = (Integer JavaDoc) readerMap.get(mimeType);
476       newCatalog.addReader(mimeType,
477                (CatalogReader)
478                readerArr.get(pos.intValue()));
479     }
480   }
481
482   /**
483    * Create a new Catalog object.
484    *
485    * <p>This method constructs a new instance of the running Catalog
486    * class (which might be a subtype of com.sun.org.apache.xml.internal.resolver.Catalog).
487    * All new catalogs are managed by the same CatalogManager.
488    * </p>
489    *
490    * <p>N.B. All Catalog subtypes should call newCatalog() to construct
491    * a new Catalog. Do not simply use "new Subclass()" since that will
492    * confuse future subclasses.</p>
493    */

494   protected Catalog newCatalog() {
495     String JavaDoc catalogClass = this.getClass().getName();
496
497     try {
498       Catalog c = (Catalog) (Class.forName(catalogClass).newInstance());
499       c.setCatalogManager(catalogManager);
500       copyReaders(c);
501       return c;
502     } catch (ClassNotFoundException JavaDoc cnfe) {
503       catalogManager.debug.message(1, "Class Not Found Exception: " + catalogClass);
504     } catch (IllegalAccessException JavaDoc iae) {
505       catalogManager.debug.message(1, "Illegal Access Exception: " + catalogClass);
506     } catch (InstantiationException JavaDoc ie) {
507       catalogManager.debug.message(1, "Instantiation Exception: " + catalogClass);
508     } catch (ClassCastException JavaDoc cce) {
509       catalogManager.debug.message(1, "Class Cast Exception: " + catalogClass);
510     } catch (Exception JavaDoc e) {
511       catalogManager.debug.message(1, "Other Exception: " + catalogClass);
512     }
513
514     Catalog c = new Catalog();
515     c.setCatalogManager(catalogManager);
516     copyReaders(c);
517     return c;
518   }
519
520   /**
521    * Returns the current base URI.
522    */

523   public String JavaDoc getCurrentBase() {
524     return base.toString();
525   }
526
527   /**
528    * Returns the default override setting associated with this
529    * catalog.
530    *
531    * <p>All catalog files loaded by this catalog will have the
532    * initial override setting specified by this default.</p>
533    */

534   public String JavaDoc getDefaultOverride() {
535     if (default_override) {
536       return "yes";
537     } else {
538       return "no";
539     }
540   }
541
542   /**
543    * Load the system catalog files.
544    *
545    * <p>The method adds all of the
546    * catalogs specified in the <tt>xml.catalog.files</tt> property
547    * to the Catalog list.</p>
548    *
549    * @throws MalformedURLException One of the system catalogs is
550    * identified with a filename that is not a valid URL.
551    * @throws IOException One of the system catalogs cannot be read.
552    */

553   public void loadSystemCatalogs()
554     throws MalformedURLException JavaDoc, IOException JavaDoc {
555
556     Vector JavaDoc catalogs = catalogManager.getCatalogFiles();
557     if (catalogs != null) {
558       for (int count = 0; count < catalogs.size(); count++) {
559     catalogFiles.addElement(catalogs.elementAt(count));
560       }
561     }
562
563     if (catalogFiles.size() > 0) {
564       // This is a little odd. The parseCatalog() method expects
565
// a filename, but it adds that name to the end of the
566
// catalogFiles vector, and then processes that vector.
567
// This allows the system to handle CATALOG entries
568
// correctly.
569
//
570
// In this init case, we take the last element off the
571
// catalogFiles vector and pass it to parseCatalog. This
572
// will "do the right thing" in the init case, and allow
573
// parseCatalog() to do the right thing in the non-init
574
// case. Honest.
575
//
576
String JavaDoc catfile = (String JavaDoc) catalogFiles.lastElement();
577       catalogFiles.removeElement(catfile);
578       parseCatalog(catfile);
579     }
580   }
581
582   /**
583    * Parse a catalog file, augmenting internal data structures.
584    *
585    * @param fileName The filename of the catalog file to process
586    *
587    * @throws MalformedURLException The fileName cannot be turned into
588    * a valid URL.
589    * @throws IOException Error reading catalog file.
590    */

591   public synchronized void parseCatalog(String JavaDoc fileName)
592     throws MalformedURLException JavaDoc, IOException JavaDoc {
593
594     default_override = catalogManager.getPreferPublic();
595     catalogManager.debug.message(4, "Parse catalog: " + fileName);
596
597     // Put the file into the list of catalogs to process...
598
// In all cases except the case when initCatalog() is the
599
// caller, this will be the only catalog initially in the list...
600
catalogFiles.addElement(fileName);
601
602     // Now process all the pending catalogs...
603
parsePendingCatalogs();
604   }
605
606   /**
607    * Parse a catalog file, augmenting internal data structures.
608    *
609    * <p>Catalogs retrieved over the net may have an associated MIME type.
610    * The MIME type can be used to select an appropriate reader.</p>
611    *
612    * @param mimeType The MIME type of the catalog file.
613    * @param is The InputStream from which the catalog should be read
614    *
615    * @throws CatalogException Failed to load catalog
616    * mimeType.
617    * @throws IOException Error reading catalog file.
618    */

619   public synchronized void parseCatalog(String JavaDoc mimeType, InputStream JavaDoc is)
620     throws IOException JavaDoc, CatalogException {
621
622     default_override = catalogManager.getPreferPublic();
623     catalogManager.debug.message(4, "Parse " + mimeType + " catalog on input stream");
624
625     CatalogReader reader = null;
626
627     if (readerMap.containsKey(mimeType)) {
628       int arrayPos = ((Integer JavaDoc) readerMap.get(mimeType)).intValue();
629       reader = (CatalogReader) readerArr.get(arrayPos);
630     }
631
632     if (reader == null) {
633       String JavaDoc msg = "No CatalogReader for MIME type: " + mimeType;
634       catalogManager.debug.message(2, msg);
635       throw new CatalogException(CatalogException.UNPARSEABLE, msg);
636     }
637
638     reader.readCatalog(this, is);
639
640     // Now process all the pending catalogs...
641
parsePendingCatalogs();
642   }
643
644   /**
645    * Parse a catalog document, augmenting internal data structures.
646    *
647    * <p>This method supports catalog files stored in jar files: e.g.,
648    * jar:file:///path/to/filename.jar!/path/to/catalog.xml". That URI
649    * doesn't survive transmogrification through the URI processing that
650    * the parseCatalog(String) performs and passing it as an input stream
651    * doesn't set the base URI appropriately.</p>
652    *
653    * <p>Written by Stefan Wachter (2002-09-26)</p>
654    *
655    * @param aUrl The URL of the catalog document to process
656    *
657    * @throws IOException Error reading catalog file.
658    */

659   public synchronized void parseCatalog(URL JavaDoc aUrl) throws IOException JavaDoc {
660     catalogCwd = aUrl;
661     base = aUrl;
662
663     default_override = catalogManager.getPreferPublic();
664     catalogManager.debug.message(4, "Parse catalog: " + aUrl.toString());
665
666     DataInputStream JavaDoc inStream = null;
667     boolean parsed = false;
668
669     for (int count = 0; !parsed && count < readerArr.size(); count++) {
670       CatalogReader reader = (CatalogReader) readerArr.get(count);
671
672       try {
673         inStream = new DataInputStream JavaDoc(aUrl.openStream());
674       } catch (FileNotFoundException JavaDoc fnfe) {
675         // No catalog; give up!
676
break;
677       }
678
679       try {
680         reader.readCatalog(this, inStream);
681         parsed=true;
682       } catch (CatalogException ce) {
683         if (ce.getExceptionType() == CatalogException.PARSE_FAILED) {
684           // give up!
685
break;
686         } else {
687           // try again!
688
}
689       }
690
691       try {
692         inStream.close();
693       } catch (IOException JavaDoc e) {
694         //nop
695
}
696     }
697
698     if (parsed) parsePendingCatalogs();
699   }
700
701   /**
702    * Parse all of the pending catalogs.
703    *
704    * <p>Catalogs may refer to other catalogs, this method parses
705    * all of the currently pending catalog files.</p>
706    */

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

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

896   public void addEntry(CatalogEntry entry) {
897     int type = entry.getEntryType();
898
899     if (type == BASE) {
900       String JavaDoc value = entry.getEntryArg(0);
901       URL JavaDoc newbase = null;
902
903       if (base == null) {
904     catalogManager.debug.message(5, "BASE CUR", "null");
905       } else {
906     catalogManager.debug.message(5, "BASE CUR", base.toString());
907       }
908       catalogManager.debug.message(4, "BASE STR", value);
909
910       try {
911     value = fixSlashes(value);
912     newbase = new URL JavaDoc(base, value);
913       } catch (MalformedURLException JavaDoc e) {
914     try {
915       newbase = new URL JavaDoc("file:" + value);
916     } catch (MalformedURLException JavaDoc e2) {
917       catalogManager.debug.message(1, "Malformed URL on base", value);
918       newbase = null;
919     }
920       }
921
922       if (newbase != null) {
923     base = newbase;
924       }
925
926       catalogManager.debug.message(5, "BASE NEW", base.toString());
927     } else if (type == CATALOG) {
928       String JavaDoc fsi = makeAbsolute(entry.getEntryArg(0));
929
930       catalogManager.debug.message(4, "CATALOG", fsi);
931
932       localCatalogFiles.addElement(fsi);
933     } else if (type == PUBLIC) {
934       String JavaDoc publicid = PublicId.normalize(entry.getEntryArg(0));
935       String JavaDoc systemid = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
936
937       entry.setEntryArg(0, publicid);
938       entry.setEntryArg(1, systemid);
939
940       catalogManager.debug.message(4, "PUBLIC", publicid, systemid);
941
942       catalogEntries.addElement(entry);
943     } else if (type == SYSTEM) {
944       String JavaDoc systemid = normalizeURI(entry.getEntryArg(0));
945       String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
946
947       entry.setEntryArg(1, fsi);
948
949       catalogManager.debug.message(4, "SYSTEM", systemid, fsi);
950
951       catalogEntries.addElement(entry);
952     } else if (type == URI) {
953       String JavaDoc uri = normalizeURI(entry.getEntryArg(0));
954       String JavaDoc altURI = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
955
956       entry.setEntryArg(1, altURI);
957
958       catalogManager.debug.message(4, "URI", uri, altURI);
959
960       catalogEntries.addElement(entry);
961     } else if (type == DOCUMENT) {
962       String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(0)));
963       entry.setEntryArg(0, fsi);
964
965       catalogManager.debug.message(4, "DOCUMENT", fsi);
966
967       catalogEntries.addElement(entry);
968     } else if (type == OVERRIDE) {
969       catalogManager.debug.message(4, "OVERRIDE", entry.getEntryArg(0));
970
971       catalogEntries.addElement(entry);
972     } else if (type == SGMLDECL) {
973       // meaningless in XML
974
String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(0)));
975       entry.setEntryArg(0, fsi);
976
977       catalogManager.debug.message(4, "SGMLDECL", fsi);
978
979       catalogEntries.addElement(entry);
980     } else if (type == DELEGATE_PUBLIC) {
981       String JavaDoc ppi = PublicId.normalize(entry.getEntryArg(0));
982       String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
983
984       entry.setEntryArg(0, ppi);
985       entry.setEntryArg(1, fsi);
986
987       catalogManager.debug.message(4, "DELEGATE_PUBLIC", ppi, fsi);
988
989       addDelegate(entry);
990     } else if (type == DELEGATE_SYSTEM) {
991       String JavaDoc psi = normalizeURI(entry.getEntryArg(0));
992       String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
993
994       entry.setEntryArg(0, psi);
995       entry.setEntryArg(1, fsi);
996
997       catalogManager.debug.message(4, "DELEGATE_SYSTEM", psi, fsi);
998
999       addDelegate(entry);
1000    } else if (type == DELEGATE_URI) {
1001      String JavaDoc pui = normalizeURI(entry.getEntryArg(0));
1002      String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1003
1004      entry.setEntryArg(0, pui);
1005      entry.setEntryArg(1, fsi);
1006
1007      catalogManager.debug.message(4, "DELEGATE_URI", pui, fsi);
1008
1009      addDelegate(entry);
1010    } else if (type == REWRITE_SYSTEM) {
1011      String JavaDoc psi = normalizeURI(entry.getEntryArg(0));
1012      String JavaDoc rpx = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1013
1014      entry.setEntryArg(0, psi);
1015      entry.setEntryArg(1, rpx);
1016
1017      catalogManager.debug.message(4, "REWRITE_SYSTEM", psi, rpx);
1018
1019      catalogEntries.addElement(entry);
1020    } else if (type == REWRITE_URI) {
1021      String JavaDoc pui = normalizeURI(entry.getEntryArg(0));
1022      String JavaDoc upx = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1023
1024      entry.setEntryArg(0, pui);
1025      entry.setEntryArg(1, upx);
1026
1027      catalogManager.debug.message(4, "REWRITE_URI", pui, upx);
1028
1029      catalogEntries.addElement(entry);
1030    } else if (type == SYSTEM_SUFFIX) {
1031      String JavaDoc pui = normalizeURI(entry.getEntryArg(0));
1032      String JavaDoc upx = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
1033
1034      entry.setEntryArg(0, pui);
1035      entry.setEntryArg(1, upx);
1036
1037      catalogManager.debug.message(4, "SYSTEM_SUFFIX", pui, upx);
1038
1039      catalogEntries.addElement(entry);
1040    } else if (type == URI_SUFFIX) {
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, "URI_SUFFIX", 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 en = catalogEntries.elements();
1157    while (en.hasMoreElements()) {
1158      CatalogEntry e = (CatalogEntry) en.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 en = catalogEntries.elements();
1233    while (en.hasMoreElements()) {
1234      CatalogEntry e = (CatalogEntry) en.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 en = catalogEntries.elements();
1271    while (en.hasMoreElements()) {
1272      CatalogEntry e = (CatalogEntry) en.nextElement();
1273      if (e.getEntryType() == DOCUMENT) {
1274    return e.getEntryArg(0);
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 en = catalogEntries.elements();
1346    while (en.hasMoreElements()) {
1347      CatalogEntry e = (CatalogEntry) en.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 en = catalogEntries.elements();
1432    while (en.hasMoreElements()) {
1433      CatalogEntry e = (CatalogEntry) en.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 en = catalogEntries.elements();
1591    while (en.hasMoreElements()) {
1592      CatalogEntry e = (CatalogEntry) en.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    en = catalogEntries.elements();
1609    Vector JavaDoc delCats = new Vector JavaDoc();
1610    while (en.hasMoreElements()) {
1611      CatalogEntry e = (CatalogEntry) en.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 enCats = delCats.elements();
1631
1632      if (catalogManager.debug.getDebug() > 1) {
1633    catalogManager.debug.message(2, "Switching to delegated catalog(s):");
1634    while (enCats.hasMoreElements()) {
1635      String JavaDoc delegatedCatalog = (String JavaDoc) enCats.nextElement();
1636      catalogManager.debug.message(2, "\t" + delegatedCatalog);
1637    }
1638      }
1639
1640      Catalog dcat = newCatalog();
1641
1642      enCats = delCats.elements();
1643      while (enCats.hasMoreElements()) {
1644    String JavaDoc delegatedCatalog = (String JavaDoc) enCats.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 en = catalogEntries.elements();
1717    while (en.hasMoreElements()) {
1718      CatalogEntry e = (CatalogEntry) en.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
en = catalogEntries.elements();
1729    String JavaDoc startString = null;
1730    String JavaDoc prefix = null;
1731    while (en.hasMoreElements()) {
1732      CatalogEntry e = (CatalogEntry) en.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
1748    if (prefix != null) {
1749      // return the systemId with the new prefix
1750
return prefix + systemId.substring(startString.length());
1751    }
1752
1753    // If there's a SYSTEM_SUFFIX entry in this catalog, use it
1754
en = catalogEntries.elements();
1755    String JavaDoc suffixString = null;
1756    String JavaDoc suffixURI = null;
1757    while (en.hasMoreElements()) {
1758      CatalogEntry e = (CatalogEntry) en.nextElement();
1759
1760      if (e.getEntryType() == SYSTEM_SUFFIX) {
1761    String JavaDoc p = (String JavaDoc) e.getEntryArg(0);
1762    if (p.length() <= systemId.length()
1763        && systemId.endsWith(p)) {
1764      // Is this the longest prefix?
1765
if (suffixString == null
1766          || p.length() > suffixString.length()) {
1767        suffixString = p;
1768        suffixURI = e.getEntryArg(1);
1769      }
1770    }
1771      }
1772    }
1773
1774    if (suffixURI != null) {
1775      // return the systemId for the suffix
1776
return suffixURI;
1777    }
1778
1779    // If there's a DELEGATE_SYSTEM entry in this catalog, use it
1780
en = catalogEntries.elements();
1781    Vector JavaDoc delCats = new Vector JavaDoc();
1782    while (en.hasMoreElements()) {
1783      CatalogEntry e = (CatalogEntry) en.nextElement();
1784
1785      if (e.getEntryType() == DELEGATE_SYSTEM) {
1786    String JavaDoc p = (String JavaDoc) e.getEntryArg(0);
1787    if (p.length() <= systemId.length()
1788        && p.equals(systemId.substring(0, p.length()))) {
1789      // delegate this match to the other catalog
1790

1791      delCats.addElement(e.getEntryArg(1));
1792    }
1793      }
1794    }
1795
1796    if (delCats.size() > 0) {
1797      Enumeration JavaDoc enCats = delCats.elements();
1798
1799      if (catalogManager.debug.getDebug() > 1) {
1800    catalogManager.debug.message(2, "Switching to delegated catalog(s):");
1801    while (enCats.hasMoreElements()) {
1802      String JavaDoc delegatedCatalog = (String JavaDoc) enCats.nextElement();
1803      catalogManager.debug.message(2, "\t" + delegatedCatalog);
1804    }
1805      }
1806
1807      Catalog dcat = newCatalog();
1808
1809      enCats = delCats.elements();
1810      while (enCats.hasMoreElements()) {
1811    String JavaDoc delegatedCatalog = (String JavaDoc) enCats.nextElement();
1812    dcat.parseCatalog(delegatedCatalog);
1813      }
1814
1815      return dcat.resolveSystem(systemId);
1816    }
1817
1818    return null;
1819  }
1820
1821  /**
1822   * Return the applicable URI.
1823   *
1824   * <p>If a URI entry exists in the Catalog
1825   * for the URI specified, return the mapped value.</p>
1826   *
1827   * <p>URI comparison is case sensitive.</p>
1828   *
1829   * @param uri The URI to locate in the catalog.
1830   *
1831   * @return The resolved URI.
1832   *
1833   * @throws MalformedURLException The system identifier of a
1834   * subordinate catalog cannot be turned into a valid URL.
1835   * @throws IOException Error reading subordinate catalog file.
1836   */

1837  public String JavaDoc resolveURI(String JavaDoc uri)
1838    throws MalformedURLException JavaDoc, IOException JavaDoc {
1839
1840    catalogManager.debug.message(3, "resolveURI("+uri+")");
1841
1842    uri = normalizeURI(uri);
1843
1844    if (uri != null && uri.startsWith("urn:publicid:")) {
1845      uri = PublicId.decodeURN(uri);
1846      return resolvePublic(uri, null);
1847    }
1848
1849    // If there's a URI entry in this catalog, use it
1850
if (uri != null) {
1851      String JavaDoc resolved = resolveLocalURI(uri);
1852      if (resolved != null) {
1853    return resolved;
1854      }
1855    }
1856
1857    // Otherwise, look in the subordinate catalogs
1858
return resolveSubordinateCatalogs(URI,
1859                      null,
1860                      null,
1861                      uri);
1862  }
1863
1864  /**
1865   * Return the applicable URI in this catalog.
1866   *
1867   * <p>If a URI entry exists in the catalog file
1868   * for the URI specified, return the mapped value.</p>
1869   *
1870   * @param uri The URI to locate in the catalog
1871   *
1872   * @return The mapped URI or null
1873   */

1874  protected String JavaDoc resolveLocalURI(String JavaDoc uri)
1875    throws MalformedURLException JavaDoc, IOException JavaDoc {
1876    Enumeration JavaDoc en = catalogEntries.elements();
1877    while (en.hasMoreElements()) {
1878      CatalogEntry e = (CatalogEntry) en.nextElement();
1879      if (e.getEntryType() == URI
1880      && (e.getEntryArg(0).equals(uri))) {
1881    return e.getEntryArg(1);
1882      }
1883    }
1884
1885    // If there's a REWRITE_URI entry in this catalog, use it
1886
en = catalogEntries.elements();
1887    String JavaDoc startString = null;
1888    String JavaDoc prefix = null;
1889    while (en.hasMoreElements()) {
1890      CatalogEntry e = (CatalogEntry) en.nextElement();
1891
1892      if (e.getEntryType() == REWRITE_URI) {
1893    String JavaDoc p = (String JavaDoc) e.getEntryArg(0);
1894    if (p.length() <= uri.length()
1895        && p.equals(uri.substring(0, p.length()))) {
1896      // Is this the longest prefix?
1897
if (startString == null
1898          || p.length() > startString.length()) {
1899        startString = p;
1900        prefix = e.getEntryArg(1);
1901      }
1902    }
1903      }
1904    }
1905
1906    if (prefix != null) {
1907      // return the uri with the new prefix
1908
return prefix + uri.substring(startString.length());
1909    }
1910
1911    // If there's a URI_SUFFIX entry in this catalog, use it
1912
en = catalogEntries.elements();
1913    String JavaDoc suffixString = null;
1914    String JavaDoc suffixURI = null;
1915    while (en.hasMoreElements()) {
1916      CatalogEntry e = (CatalogEntry) en.nextElement();
1917
1918      if (e.getEntryType() == URI_SUFFIX) {
1919    String JavaDoc p = (String JavaDoc) e.getEntryArg(0);
1920    if (p.length() <= uri.length()
1921        && uri.endsWith(p)) {
1922      // Is this the longest prefix?
1923
if (suffixString == null
1924          || p.length() > suffixString.length()) {
1925        suffixString = p;
1926        suffixURI = e.getEntryArg(1);
1927      }
1928    }
1929      }
1930    }
1931
1932    if (suffixURI != null) {
1933      // return the uri for the suffix
1934
return suffixURI;
1935    }
1936
1937    // If there's a DELEGATE_URI entry in this catalog, use it
1938
en = catalogEntries.elements();
1939    Vector JavaDoc delCats = new Vector JavaDoc();
1940    while (en.hasMoreElements()) {
1941      CatalogEntry e = (CatalogEntry) en.nextElement();
1942
1943      if (e.getEntryType() == DELEGATE_URI) {
1944    String JavaDoc p = (String JavaDoc) e.getEntryArg(0);
1945    if (p.length() <= uri.length()
1946        && p.equals(uri.substring(0, p.length()))) {
1947      // delegate this match to the other catalog
1948

1949      delCats.addElement(e.getEntryArg(1));
1950    }
1951      }
1952    }
1953
1954    if (delCats.size() > 0) {
1955      Enumeration JavaDoc enCats = delCats.elements();
1956
1957      if (catalogManager.debug.getDebug() > 1) {
1958    catalogManager.debug.message(2, "Switching to delegated catalog(s):");
1959    while (enCats.hasMoreElements()) {
1960      String JavaDoc delegatedCatalog = (String JavaDoc) enCats.nextElement();
1961      catalogManager.debug.message(2, "\t" + delegatedCatalog);
1962    }
1963      }
1964
1965      Catalog dcat = newCatalog();
1966
1967      enCats = delCats.elements();
1968      while (enCats.hasMoreElements()) {
1969    String JavaDoc delegatedCatalog = (String JavaDoc) enCats.nextElement();
1970    dcat.parseCatalog(delegatedCatalog);
1971      }
1972
1973      return dcat.resolveURI(uri);
1974    }
1975
1976    return null;
1977  }
1978
1979  /**
1980   * Search the subordinate catalogs, in order, looking for a match.
1981   *
1982   * <p>This method searches the Catalog and returns the system
1983   * identifier specified for the given entity type with the given
1984   * name, public, and system identifiers. In some contexts, these
1985   * may be null.</p>
1986   *
1987   * @param entityType The CatalogEntry type for which this query is
1988   * being conducted. This is necessary in order to do the approprate
1989   * query on a subordinate catalog.
1990   * @param entityName The name of the entity being searched for, if
1991   * appropriate.
1992   * @param publicId The public identifier of the entity in question
1993   * (as provided in the source document).
1994   * @param systemId The nominal system identifier for the entity
1995   * in question (as provided in the source document). This parameter is
1996   * overloaded for the URI entry type.
1997   *
1998   * @throws MalformedURLException The formal system identifier of a
1999   * delegated catalog cannot be turned into a valid URL.
2000   * @throws IOException Error reading delegated catalog file.
2001   *
2002   * @return The system identifier to use.
2003   * Note that the nominal system identifier is not returned if a
2004   * match is not found in the catalog, instead null is returned
2005   * to indicate that no match was found.
2006   */

2007  protected synchronized String JavaDoc resolveSubordinateCatalogs(int entityType,
2008                               String JavaDoc entityName,
2009                               String JavaDoc publicId,
2010                               String JavaDoc systemId)
2011    throws MalformedURLException JavaDoc, IOException JavaDoc {
2012
2013    for (int catPos = 0; catPos < catalogs.size(); catPos++) {
2014      Catalog c = null;
2015
2016      try {
2017    c = (Catalog) catalogs.elementAt(catPos);
2018      } catch (ClassCastException JavaDoc e) {
2019    String JavaDoc catfile = (String JavaDoc) catalogs.elementAt(catPos);
2020    c = newCatalog();
2021
2022    try {
2023      c.parseCatalog(catfile);
2024    } catch (MalformedURLException JavaDoc mue) {
2025      catalogManager.debug.message(1, "Malformed Catalog URL", catfile);
2026    } catch (FileNotFoundException JavaDoc fnfe) {
2027      catalogManager.debug.message(1, "Failed to load catalog, file not found",
2028            catfile);
2029    } catch (IOException JavaDoc ioe) {
2030      catalogManager.debug.message(1, "Failed to load catalog, I/O error", catfile);
2031    }
2032
2033    catalogs.setElementAt(c, catPos);
2034      }
2035
2036      String JavaDoc resolved = null;
2037
2038      // Ok, now what are we supposed to call here?
2039
if (entityType == DOCTYPE) {
2040    resolved = c.resolveDoctype(entityName,
2041                    publicId,
2042                    systemId);
2043      } else if (entityType == DOCUMENT) {
2044    resolved = c.resolveDocument();
2045      } else if (entityType == ENTITY) {
2046    resolved = c.resolveEntity(entityName,
2047                   publicId,
2048                   systemId);
2049      } else if (entityType == NOTATION) {
2050    resolved = c.resolveNotation(entityName,
2051                     publicId,
2052                     systemId);
2053      } else if (entityType == PUBLIC) {
2054    resolved = c.resolvePublic(publicId, systemId);
2055      } else if (entityType == SYSTEM) {
2056    resolved = c.resolveSystem(systemId);
2057      } else if (entityType == URI) {
2058    resolved = c.resolveURI(systemId);
2059      }
2060
2061      if (resolved != null) {
2062    return resolved;
2063      }
2064    }
2065
2066    return null;
2067  }
2068
2069  // -----------------------------------------------------------------
2070

2071  /**
2072   * Replace backslashes with forward slashes. (URLs always use
2073   * forward slashes.)
2074   *
2075   * @param sysid The input system identifier.
2076   * @return The same system identifier with backslashes turned into
2077   * forward slashes.
2078   */

2079  protected String JavaDoc fixSlashes (String JavaDoc sysid) {
2080    return sysid.replace('\\', '/');
2081  }
2082
2083  /**
2084   * Construct an absolute URI from a relative one, using the current
2085   * base URI.
2086   *
2087   * @param sysid The (possibly relative) system identifier
2088   * @return The system identifier made absolute with respect to the
2089   * current {@link #base}.
2090   */

2091  protected String JavaDoc makeAbsolute(String JavaDoc sysid) {
2092    URL JavaDoc local = null;
2093
2094    sysid = fixSlashes(sysid);
2095
2096    try {
2097      local = new URL JavaDoc(base, sysid);
2098    } catch (MalformedURLException JavaDoc e) {
2099      catalogManager.debug.message(1, "Malformed URL on system identifier", sysid);
2100    }
2101
2102    if (local != null) {
2103      return local.toString();
2104    } else {
2105      return sysid;
2106    }
2107  }
2108
2109  /**
2110   * Perform character normalization on a URI reference.
2111   *
2112   * @param uriref The URI reference
2113   * @return The normalized URI reference.
2114   */

2115  protected String JavaDoc normalizeURI(String JavaDoc uriref) {
2116    String JavaDoc newRef = "";
2117    byte[] bytes;
2118
2119    if (uriref == null) {
2120      return null;
2121    }
2122
2123    try {
2124      bytes = uriref.getBytes("UTF-8");
2125    } catch (UnsupportedEncodingException JavaDoc uee) {
2126      // this can't happen
2127
catalogManager.debug.message(1, "UTF-8 is an unsupported encoding!?");
2128      return uriref;
2129    }
2130
2131    for (int count = 0; count < bytes.length; count++) {
2132      int ch = bytes[count] & 0xFF;
2133
2134      if ((ch <= 0x20) // ctrl
2135
|| (ch > 0x7F) // high ascii
2136
|| (ch == 0x22) // "
2137
|| (ch == 0x3C) // <
2138
|| (ch == 0x3E) // >
2139
|| (ch == 0x5C) // \
2140
|| (ch == 0x5E) // ^
2141
|| (ch == 0x60) // `
2142
|| (ch == 0x7B) // {
2143
|| (ch == 0x7C) // |
2144
|| (ch == 0x7D) // }
2145
|| (ch == 0x7F)) {
2146    newRef += encodedByte(ch);
2147      } else {
2148    newRef += (char) bytes[count];
2149      }
2150    }
2151
2152    return newRef;
2153  }
2154
2155  /**
2156   * Perform %-encoding on a single byte.
2157   *
2158   * @param b The 8-bit integer that represents th byte. (Bytes are signed
2159              but encoding needs to look at the bytes unsigned.)
2160   * @return The %-encoded string for the byte in question.
2161   */

2162  protected String JavaDoc encodedByte (int b) {
2163    String JavaDoc hex = Integer.toHexString(b).toUpperCase();
2164    if (hex.length() < 2) {
2165      return "%0" + hex;
2166    } else {
2167      return "%" + hex;
2168    }
2169  }
2170
2171  // -----------------------------------------------------------------
2172

2173  /**
2174   * Add to the current list of delegated catalogs.
2175   *
2176   * <p>This method always constructs the {@link #localDelegate}
2177   * vector so that it is ordered by length of partial
2178   * public identifier.</p>
2179   *
2180   * @param entry The DELEGATE catalog entry
2181   */

2182  protected void addDelegate(CatalogEntry entry) {
2183    int pos = 0;
2184    String JavaDoc partial = entry.getEntryArg(0);
2185
2186    Enumeration JavaDoc local = localDelegate.elements();
2187    while (local.hasMoreElements()) {
2188      CatalogEntry dpe = (CatalogEntry) local.nextElement();
2189      String JavaDoc dp = dpe.getEntryArg(0);
2190      if (dp.equals(partial)) {
2191    // we already have this prefix
2192
return;
2193      }
2194      if (dp.length() > partial.length()) {
2195    pos++;
2196      }
2197      if (dp.length() < partial.length()) {
2198    break;
2199      }
2200    }
2201
2202    // now insert partial into the vector at [pos]
2203
if (localDelegate.size() == 0) {
2204      localDelegate.addElement(entry);
2205    } else {
2206      localDelegate.insertElementAt(entry, pos);
2207    }
2208  }
2209}
2210
2211
Popular Tags