OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [gnu/] [xml/] [util/] [XCat.java] - Blame information for rev 769

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 769 jeremybenn
/* XCat.java --
2
   Copyright (C) 2001 Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
 
39
package gnu.xml.util;
40
 
41
import gnu.java.lang.CPStringBuilder;
42
 
43
import java.io.ByteArrayOutputStream;
44
import java.io.IOException;
45
import java.net.URL;
46
import java.util.Enumeration;
47
import java.util.Hashtable;
48
import java.util.StringTokenizer;
49
import java.util.Stack;
50
import java.util.Vector;
51
 
52
import org.xml.sax.Attributes;
53
import org.xml.sax.ErrorHandler;
54
import org.xml.sax.InputSource;
55
import org.xml.sax.Locator;
56
import org.xml.sax.SAXException;
57
import org.xml.sax.SAXNotRecognizedException;
58
import org.xml.sax.SAXParseException;
59
import org.xml.sax.XMLReader;
60
 
61
import org.xml.sax.ext.DefaultHandler2;
62
import org.xml.sax.ext.EntityResolver2;
63
 
64
import org.xml.sax.helpers.XMLReaderFactory;
65
 
66
/**
67
 * Packages <a href=
68
    "http://www.oasis-open.org/committees/entity/spec-2001-08-06.html"
69
    >OASIS XML Catalogs</a>,
70
 * primarily for entity resolution by parsers.
71
 * That specification defines an XML syntax for mappings between
72
 * identifiers declared in DTDs (particularly PUBLIC identifiers) and
73
 * locations.  SAX has always supported such mappings, but conventions for
74
 * an XML file syntax to maintain them have previously been lacking.
75
 *
76
 * <p> This has three main operational modes.  The primary intended mode is
77
 * to create a resolver, then preloading it with one or more site-standard
78
 * catalogs before using it with one or more SAX parsers: <pre>
79
 *      XCat    catalog = new XCat ();
80
 *      catalog.setErrorHandler (diagnosticErrorHandler);
81
 *      catalog.loadCatalog ("file:/local/catalogs/catalog.cat");
82
 *      catalog.loadCatalog ("http://shared/catalog.cat");
83
 *      ...
84
 *      catalog.disableLoading ();
85
 *      parser1.setEntityResolver (catalog);
86
 *      parser2.setEntityResolver (catalog);
87
 *      ...</pre>
88
 *
89
 * <p>A second mode is to arrange that your application uses instances of
90
 * this class as its entity resolver, and automatically loads catalogs
91
 * referenced by <em>&lt;?oasis-xml-catalog...?&gt;</em> processing
92
 * instructions found before the DTD in documents it parses.
93
 * It would then discard the resolver after each parse.
94
 *
95
 * <p> A third mode applies catalogs in contexts other than entity
96
 * resolution for parsers.
97
 * The {@link #resolveURI resolveURI()} method supports resolving URIs
98
 * stored in XML application data, rather than inside DTDs.
99
 * Catalogs would be loaded as shown above, and the catalog could
100
 * be used concurrently for parser entity resolution and for
101
 * application URI resolution.
102
 * </p>
103
 *
104
 * <center><hr width='70%'></center>
105
 *
106
 * <p>Errors in catalogs implicitly loaded (during resolution) are ignored
107
 * beyond being reported through any <em>ErrorHandler</em> assigned using
108
 * {@link #setErrorHandler setErrorHandler()}.  SAX exceptions
109
 * thrown from such a handler won't abort resolution, although throwing a
110
 * <em>RuntimeException</em> or <em>Error</em> will normally abort both
111
 * resolution and parsing.  Useful diagnostic information is available to
112
 * any <em>ErrorHandler</em> used to report problems, or from any exception
113
 * thrown from an explicit {@link #loadCatalog loadCatalog()} invocation.
114
 * Applications can use that information as troubleshooting aids.
115
 *
116
 * <p>While this class requires <em>SAX2 Extensions 1.1</em> classes in
117
 * its class path, basic functionality does not require using a SAX2
118
 * parser that supports the extended entity resolution functionality.
119
 * See the original SAX1
120
 * {@link #resolveEntity(java.lang.String,java.lang.String) resolveEntity()}
121
 * method for a list of restrictions which apply when it is used with
122
 * older SAX parsers.
123
 *
124
 * @see EntityResolver2
125
 *
126
 * @author David Brownell
127
 */
128
public class XCat implements EntityResolver2
129
{
130
    private Catalog             catalogs [];
131
    private boolean             usingPublic = true;
132
    private boolean             loadingPermitted = true;
133
    private boolean             unified = true;
134
    private String              parserClass;
135
    private ErrorHandler        errorHandler;
136
 
137
    // private EntityResolver   next;   // chain to next if we fail...
138
 
139
    //
140
    // NOTE:  This is a straightforward implementation, and if
141
    // there are lots of "nextCatalog" or "delegate*" entries
142
    // in use, two tweaks would be worth considering:
143
    //
144
    //  - Centralize some sort of cache (key by URI) for individual
145
    //    resolvers.  That'd avoid multiple copies of a given catalog.
146
    //
147
    //  - Have resolution track what catalogs (+modes) have been
148
    //    searched.  This would support loop detection.
149
    //
150
 
151
 
152
    /**
153
     * Initializes without preloading a catalog.
154
     * This API is convenient when you may want to arrange that catalogs
155
     * are automatically loaded when explicitly referenced in documents,
156
     * using the <em>oasis-xml-catalog</em> processing instruction.
157
     * In such cases you won't usually be able to preload catalogs.
158
     */
159
    public XCat () { }
160
 
161
    /**
162
     * Initializes, and preloads a catalog using the default SAX parser.
163
     * This API is convenient when you operate with one or more standard
164
     * catalogs.
165
     *
166
     * <p> This just delegates to {@link #loadCatalog loadCatalog()};
167
     * see it for exception information.
168
     *
169
     * @param uri absolute URI for the catalog file.
170
     */
171
    public XCat (String uri)
172
    throws SAXException, IOException
173
        { loadCatalog (uri); }
174
 
175
 
176
    /**
177
     * Loads an OASIS XML Catalog.
178
     * It is appended to the list of currently active catalogs, or
179
     * reloaded if a catalog with the same URI was already loaded.
180
     * Callers have control over what parser is used, how catalog parsing
181
     * errors are reported, and whether URIs will be resolved consistently.
182
     *
183
     * <p> The OASIS specification says that errors detected when loading
184
     * catalogs "must recover by ignoring the catalog entry file that
185
     * failed, and proceeding."  In this API, that action can be the
186
     * responsibility of applications, when they explicitly load any
187
     * catalog using this method.
188
     *
189
     * <p>Note that catalogs referenced by this one will not be loaded
190
     * at this time.  Catalogs referenced through <em>nextCatalog</em>
191
     * or <em>delegate*</em> elements are normally loaded only if needed.
192
     *
193
     * @see #setErrorHandler
194
     * @see #setParserClass
195
     * @see #setUnified
196
     *
197
     * @param uri absolute URI for the catalog file.
198
     *
199
     * @exception IOException As thrown by the parser, typically to
200
     *  indicate problems reading data from that URI.
201
     * @exception SAXException As thrown by the parser, typically to
202
     *  indicate problems parsing data from that URI.  It may also
203
     *  be thrown if the parser doesn't support necessary handlers.
204
     * @exception IllegalStateException When attempting to load a
205
     *  catalog after loading has been {@link #disableLoading disabled},
206
     *  such as after any entity or URI lookup has been performed.
207
     */
208
    public synchronized void loadCatalog (String uri)
209
    throws SAXException, IOException
210
    {
211
        Catalog         catalog;
212
        int             index = -1;
213
 
214
        if (!loadingPermitted)
215
            throw new IllegalStateException ();
216
 
217
        uri = normalizeURI (uri);
218
        if (catalogs != null) {
219
            // maybe just reload
220
            for (index = 0; index < catalogs.length; index++)
221
                if (uri.equals (catalogs [index].catalogURI))
222
                    break;
223
        }
224
        catalog = loadCatalog (parserClass, errorHandler, uri, unified);
225
 
226
        // add to list of catalogs
227
        if (catalogs == null) {
228
            index = 0;
229
            catalogs = new Catalog [1];
230
        } else if (index == catalogs.length) {
231
            Catalog             tmp [];
232
 
233
            tmp = new Catalog [index + 1];
234
            System.arraycopy (catalogs, 0, tmp, 0, index);
235
            catalogs = tmp;
236
        }
237
        catalogs [index] = catalog;
238
    }
239
 
240
 
241
    /**
242
     * "New Style" external entity resolution for parsers.
243
     * Calls to this method prevent explicit loading of additional catalogs
244
     * using {@link #loadCatalog loadCatalog()}.
245
     *
246
     * <p>This supports the full core catalog functionality for locating
247
     * (and relocating) parsed entities that have been declared in a
248
     * document's DTD.
249
     *
250
     * @param name Entity name, such as "dudley", "%nell", or "[dtd]".
251
     * @param publicId Either a normalized public ID, or null.
252
     * @param baseURI Absolute base URI associated with systemId.
253
     * @param systemId URI found in entity declaration (may be
254
     *  relative to baseURI).
255
     *
256
     * @return Input source for accessing the external entity, or null
257
     *  if no mapping was found.  The input source may have opened
258
     *  the stream, and will have a fully resolved URI.
259
     *
260
     * @see #getExternalSubset
261
     */
262
    public InputSource resolveEntity (
263
        String name,            // UNUSED ... systemId is always non-null
264
        String publicId,
265
        String baseURI,         // UNUSED ... it just lets sysId be relative
266
        String systemId
267
    ) throws SAXException, IOException
268
    {
269
        if (loadingPermitted)
270
            disableLoading ();
271
 
272
        try {
273
            // steps as found in OASIS XML catalog spec 7.1.2
274
            // steps 1, 8 involve looping over the list of catalogs
275
            for (int i = 0; i < catalogs.length; i++) {
276
                InputSource     retval;
277
                retval = catalogs [i].resolve (usingPublic, publicId, systemId);
278
                if (retval != null)
279
                    return retval;
280
            }
281
        } catch (DoneDelegation x) {
282
            // done!
283
        }
284
        // step 9 involves returning "no match"
285
        return null;
286
    }
287
 
288
 
289
    /**
290
     * "New Style" parser callback to add an external subset.
291
     * For documents that don't include an external subset, this may
292
     * return one according to <em>doctype</em> catalog entries.
293
     * (This functionality is not a core part of the OASIS XML Catalog
294
     * specification, though it's presented in an appendix.)
295
     * If no such entry is defined, this returns null to indicate that
296
     * this document will not be modified to include such a subset.
297
     * Calls to this method prevent explicit loading of additional catalogs
298
     * using {@link #loadCatalog loadCatalog()}.
299
     *
300
     * <p><em>Warning:</em> That catalog functionality can be dangerous.
301
     * It can provide definitions of general entities, and thereby mask
302
     * certain well formedess errors.
303
     *
304
     * @param name Name of the document element, either as declared in
305
     *  a DOCTYPE declaration or as observed in the text.
306
     * @param baseURI Document's base URI (absolute).
307
     *
308
     * @return Input source for accessing the external subset, or null
309
     *  if no mapping was found.  The input source may have opened
310
     *  the stream, and will have a fully resolved URI.
311
     */
312
    public InputSource getExternalSubset (String name, String baseURI)
313
    throws SAXException, IOException
314
    {
315
        if (loadingPermitted)
316
            disableLoading ();
317
        try {
318
            for (int i = 0; i < catalogs.length; i++) {
319
                InputSource retval = catalogs [i].getExternalSubset (name);
320
                if (retval != null)
321
                    return retval;
322
            }
323
        } catch (DoneDelegation x) {
324
            // done!
325
        }
326
        return null;
327
    }
328
 
329
 
330
    /**
331
     * "Old Style" external entity resolution for parsers.
332
     * This API provides only core functionality.
333
     * Calls to this method prevent explicit loading of additional catalogs
334
     * using {@link #loadCatalog loadCatalog()}.
335
     *
336
     * <p>The functional limitations of this interface include:</p><ul>
337
     *
338
     *  <li>Since system IDs will be absolutized before the resolver
339
     *  sees them, matching against relative URIs won't work.
340
     *  This may affect <em>system</em>, <em>rewriteSystem</em>,
341
     *  and <em>delegateSystem</em> catalog entries.
342
     *
343
     *  <li>Because of that absolutization, documents declaring entities
344
     *  with system IDs using URI schemes that the JVM does not recognize
345
     *  may be unparsable.  URI schemes such as <em>file:/</em>,
346
     *  <em>http://</em>, <em>https://</em>, and <em>ftp://</em>
347
     *  will usually work reliably.
348
     *
349
     *  <li>Because missing external subsets can't be provided, the
350
     *  <em>doctype</em> catalog entries will be ignored.
351
     *  (The {@link #getExternalSubset getExternalSubset()} method is
352
     *  a "New Style" resolution option.)
353
     *
354
     *  </ul>
355
     *
356
     * <p>Applications can tell whether this limited functionality will be
357
     * used: if the feature flag associated with the {@link EntityResolver2}
358
     * interface is not <em>true</em>, the limitations apply.  Applications
359
     * can't usually know whether a given document and catalog will trigger
360
     * those limitations.  The issue can only be bypassed by operational
361
     * procedures such as not using catalogs or documents which involve
362
     * those features.
363
     *
364
     * @param publicId Either a normalized public ID, or null
365
     * @param systemId Always an absolute URI.
366
     *
367
     * @return Input source for accessing the external entity, or null
368
     *  if no mapping was found.  The input source may have opened
369
     *  the stream, and will have a fully resolved URI.
370
     */
371
    final public InputSource resolveEntity (String publicId, String systemId)
372
    throws SAXException, IOException
373
    {
374
        return resolveEntity (null, publicId, null, systemId);
375
    }
376
 
377
 
378
    /**
379
     * Resolves a URI reference that's not defined to the DTD.
380
     * This is intended for use with URIs found in document text, such as
381
     * <em>xml-stylesheet</em> processing instructions and in attribute
382
     * values, where they are not recognized as URIs by XML parsers.
383
     * Calls to this method prevent explicit loading of additional catalogs
384
     * using {@link #loadCatalog loadCatalog()}.
385
     *
386
     * <p>This functionality is supported by the OASIS XML Catalog
387
     * specification, but will never be invoked by an XML parser.
388
     * It corresponds closely to functionality for mapping system
389
     * identifiers for entities declared in DTDs; closely enough that
390
     * this implementation's default behavior is that they be
391
     * identical, to minimize potential confusion.
392
     *
393
     * <p>This method could be useful when implementing the
394
     * {@link javax.xml.transform.URIResolver} interface, wrapping the
395
     * input source in a {@link javax.xml.transform.sax.SAXSource}.
396
     *
397
     * @see #isUnified
398
     * @see #setUnified
399
     *
400
     * @param baseURI The relevant base URI as specified by the XML Base
401
     *  specification.  This recognizes <em>xml:base</em> attributes
402
     *  as overriding the actual (physical) base URI.
403
     * @param uri Either an absolute URI, or one relative to baseURI
404
     *
405
     * @return Input source for accessing the mapped URI, or null
406
     *  if no mapping was found.  The input source may have opened
407
     *  the stream, and will have a fully resolved URI.
408
     */
409
    public InputSource resolveURI (String baseURI, String uri)
410
    throws SAXException, IOException
411
    {
412
        if (loadingPermitted)
413
            disableLoading ();
414
 
415
        // NOTE:  baseURI isn't used here, but caller MUST have it,
416
        // and heuristics _might_ use it in the future ... plus,
417
        // it's symmetric with resolveEntity ().
418
 
419
        // steps 1, 6 involve looping
420
        try {
421
            for (int i = 0; i < catalogs.length; i++) {
422
                InputSource     tmp = catalogs [i].resolveURI (uri);
423
                if (tmp != null)
424
                    return tmp;
425
            }
426
        } catch (DoneDelegation x) {
427
            // done
428
        }
429
        // step 7 reports no match
430
        return null;
431
    }
432
 
433
 
434
    /**
435
     * Records that catalog loading is no longer permitted.
436
     * Loading is automatically disabled when lookups are performed,
437
     * and should be manually disabled when <em>startDTD()</em> (or
438
     * any other DTD declaration callback) is invoked, or at the latest
439
     * when the document root element is seen.
440
     */
441
    public synchronized void disableLoading ()
442
    {
443
        // NOTE:  this method and loadCatalog() are synchronized
444
        // so that it's impossible to load (top level) catalogs
445
        // after lookups start.  Likewise, deferred loading is also
446
        // synchronized (for "next" and delegated catalogs) to
447
        // ensure that parsers can share resolvers.
448
        loadingPermitted = false;
449
    }
450
 
451
 
452
    /**
453
     * Returns the error handler used to report catalog errors.
454
     * Null is returned if the parser's default error handling
455
     * will be used.
456
     *
457
     * @see #setErrorHandler
458
     */
459
    public ErrorHandler getErrorHandler ()
460
        { return errorHandler; }
461
 
462
    /**
463
     * Assigns the error handler used to report catalog errors.
464
     * These errors may come either from the SAX2 parser or
465
     * from the catalog parsing code driven by the parser.
466
     *
467
     * <p> If you're sharing the resolver between parsers, don't
468
     * change this once lookups have begun.
469
     *
470
     * @see #getErrorHandler
471
     *
472
     * @param parser The error handler, or null saying to use the default
473
     *  (no diagnostics, and only fatal errors terminate loading).
474
     */
475
    public void setErrorHandler (ErrorHandler handler)
476
        { errorHandler = handler; }
477
 
478
 
479
    /**
480
     * Returns the name of the SAX2 parser class used to parse catalogs.
481
     * Null is returned if the system default is used.
482
     * @see #setParserClass
483
     */
484
    public String getParserClass ()
485
        { return parserClass; }
486
 
487
    /**
488
     * Names the SAX2 parser class used to parse catalogs.
489
     *
490
     * <p> If you're sharing the resolver between parsers, don't change
491
     * this once lookups have begun.
492
     *
493
     * <p> Note that in order to properly support the <em>xml:base</em>
494
     * attribute and relative URI resolution, the SAX parser used to parse
495
     * the catalog must provide a {@link Locator} and support the optional
496
     * declaration and lexical handlers.
497
     *
498
     * @see #getParserClass
499
     *
500
     * @param parser The parser class name, or null saying to use the
501
     *  system default SAX2 parser.
502
     */
503
    public void setParserClass (String parser)
504
        { parserClass = parser; }
505
 
506
 
507
    /**
508
     * Returns true (the default) if all methods resolve
509
     * a given URI in the same way.
510
     * Returns false if calls resolving URIs as entities (such as
511
     * {@link #resolveEntity resolveEntity()}) use different catalog entries
512
     * than those resolving them as URIs ({@link #resolveURI resolveURI()}),
513
     * which will generally produce different results.
514
     *
515
     * <p>The OASIS XML Catalog specification defines two related schemes
516
     * to map URIs "as URIs" or "as system IDs".
517
     * URIs use <em>uri</em>, <em>rewriteURI</em>, and <em>delegateURI</em>
518
     * elements.  System IDs do the same things with <em>systemId</em>,
519
     * <em>rewriteSystemId</em>, and <em>delegateSystemId</em>.
520
     * It's confusing and error prone to maintain two parallel copies of
521
     * such data.  Accordingly, this class makes that behavior optional.
522
     * The <em>unified</em> interpretation of URI mappings is preferred,
523
     * since it prevents surprises where one URI gets mapped to different
524
     * contents depending on whether the reference happens to have come
525
     * from a DTD (or not).
526
     *
527
     * @see #setUnified
528
     */
529
    public boolean isUnified ()
530
        { return unified; }
531
 
532
    /**
533
     * Assigns the value of the flag returned by {@link #isUnified}.
534
     * Set it to false to be strictly conformant with the OASIS XML Catalog
535
     * specification.  Set it to true to make all mappings for a given URI
536
     * give the same result, regardless of the reason for the mapping.
537
     *
538
     * <p>Don't change this once you've loaded the first catalog.
539
     *
540
     * @param value new flag setting
541
     */
542
    public void setUnified (boolean value)
543
        { unified = value; }
544
 
545
 
546
    /**
547
     * Returns true (the default) if a catalog's public identifier
548
     * mappings will be used.
549
     * When false is returned, such mappings are ignored except when
550
     * system IDs are discarded, such as for
551
     * entities using the <em>urn:publicid:</em> URI scheme in their
552
     * system identifiers.  (See RFC 3151 for information about that
553
     * URI scheme.  Using it in system identifiers may not work well
554
     * with many SAX parsers unless the <em>resolve-dtd-uris</em>
555
     * feature flag is set to false.)
556
     * @see #setUsingPublic
557
     */
558
    public boolean isUsingPublic ()
559
        { return usingPublic; }
560
 
561
    /**
562
     * Specifies which catalog search mode is used.
563
     * By default, public identifier mappings are able to override system
564
     * identifiers when both are available.
565
     * Applications may choose to ignore public
566
     * identifier mappings in such cases, so that system identifiers
567
     * declared in DTDs will only be overridden by an explicit catalog
568
     * match for that system ID.
569
     *
570
     * <p> If you're sharing the resolver between parsers, don't
571
     * change this once lookups have begun.
572
     * @see #isUsingPublic
573
     *
574
     * @param value true to always use public identifier mappings,
575
     *  false to only use them for system ids using the <em>urn:publicid:</em>
576
     *  URI scheme.
577
     */
578
    public void setUsingPublic (boolean value)
579
        { usingPublic = value; }
580
 
581
 
582
 
583
    // hmm, what's this do? :)
584
    private static Catalog loadCatalog (
585
        String          parserClass,
586
        ErrorHandler    eh,
587
        String          uri,
588
        boolean         unified
589
    ) throws SAXException, IOException
590
    {
591
        XMLReader       parser;
592
        Loader          loader;
593
        boolean         doesIntern = false;
594
 
595
        if (parserClass == null)
596
            parser = XMLReaderFactory.createXMLReader ();
597
        else
598
            parser = XMLReaderFactory.createXMLReader (parserClass);
599
        if (eh != null)
600
            parser.setErrorHandler (eh);
601
        // resolve-dtd-entities is at default value (unrecognized == true)
602
 
603
        try {
604
            doesIntern = parser.getFeature (
605
                "http://xml.org/sax/features/string-interning");
606
        } catch (SAXNotRecognizedException e) { }
607
 
608
        loader = new Loader (doesIntern, eh, unified);
609
        loader.cat.parserClass = parserClass;
610
        loader.cat.catalogURI = uri;
611
 
612
        parser.setContentHandler (loader);
613
        parser.setProperty (
614
            "http://xml.org/sax/properties/declaration-handler",
615
            loader);
616
        parser.setProperty (
617
            "http://xml.org/sax/properties/lexical-handler",
618
            loader);
619
        parser.parse (uri);
620
 
621
        return loader.cat;
622
    }
623
 
624
    // perform one or both the normalizations for public ids
625
    private static String normalizePublicId (boolean full, String publicId)
626
    {
627
        if (publicId.startsWith ("urn:publicid:")) {
628
            CPStringBuilder     buf = new CPStringBuilder ();
629
            char                chars [] = publicId.toCharArray ();
630
boolean hasbug = false;
631
 
632
            for (int i = 13; i < chars.length; i++) {
633
                switch (chars [i]) {
634
                case '+':       buf.append (' '); continue;
635
                case ':':       buf.append ("//"); continue;
636
                case ';':       buf.append ("::"); continue;
637
                case '%':
638
// FIXME unhex that char!  meanwhile, warn and fallthrough ...
639
                    hasbug = true;
640
                default:        buf.append (chars [i]); continue;
641
                }
642
            }
643
            publicId = buf.toString ();
644
if (hasbug)
645
System.err.println ("nyet unhexing public id: " + publicId);
646
            full = true;
647
        }
648
 
649
        // SAX parsers do everything except that URN mapping, but
650
        // we can't trust other sources to normalize correctly
651
        if (full) {
652
            StringTokenizer     tokens;
653
            String              token;
654
 
655
            tokens = new StringTokenizer (publicId, " \r\n");
656
            publicId = null;
657
            while (tokens.hasMoreTokens ()) {
658
                if (publicId == null)
659
                    publicId = tokens.nextToken ();
660
                else
661
                    publicId += " " + tokens.nextToken ();
662
            }
663
        }
664
        return publicId;
665
    }
666
 
667
    private static boolean isUriExcluded (int c)
668
        { return c <= 0x20 || c >= 0x7f || "\"<>^`{|}".indexOf (c) != -1; }
669
 
670
    private static int hexNibble (int c)
671
    {
672
        if (c < 10)
673
            return c + '0';
674
        return ('a' - 10) + c;
675
    }
676
 
677
    // handles URIs with "excluded" characters
678
    private static String normalizeURI (String systemId)
679
    {
680
        int                     length = systemId.length ();
681
 
682
        for (int i = 0; i < length; i++) {
683
            char        c = systemId.charAt (i);
684
 
685
            // escape non-ASCII plus "excluded" characters
686
            if (isUriExcluded (c)) {
687
                byte                    buf [];
688
                ByteArrayOutputStream   out;
689
                int                             b;
690
 
691
                // a JVM that doesn't know UTF8 and 8859_1 is unusable!
692
                try {
693
                    buf = systemId.getBytes ("UTF8");
694
                    out = new ByteArrayOutputStream (buf.length + 10);
695
 
696
                    for (i = 0; i < buf.length; i++) {
697
                        b = buf [i] & 0x0ff;
698
                        if (isUriExcluded (b)) {
699
                            out.write ((int) '%');
700
                            out.write (hexNibble (b >> 4));
701
                            out.write (hexNibble (b & 0x0f));
702
                        } else
703
                            out.write (b);
704
                    }
705
                    return out.toString ("8859_1");
706
                } catch (IOException e) {
707
                    throw new RuntimeException (
708
                        "can't normalize URI: " + e.getMessage ());
709
                }
710
            }
711
        }
712
        return systemId;
713
    }
714
 
715
    // thrown to mark authoritative end of a search
716
    private static class DoneDelegation extends SAXException
717
    {
718
        DoneDelegation () { }
719
    }
720
 
721
 
722
    /**
723
     * Represents a OASIS XML Catalog, and encapsulates much of
724
     * the catalog functionality.
725
     */
726
    private static class Catalog
727
    {
728
        // loading infrastructure
729
        String          catalogURI;
730
        ErrorHandler    eh;
731
        boolean         unified;
732
        String          parserClass;
733
 
734
        // catalog data
735
        boolean         hasPreference;
736
        boolean         usingPublic;
737
 
738
        Hashtable       publicIds;
739
        Hashtable       publicDelegations;
740
 
741
        Hashtable       systemIds;
742
        Hashtable       systemRewrites;
743
        Hashtable       systemDelegations;
744
 
745
        Hashtable       uris;
746
        Hashtable       uriRewrites;
747
        Hashtable       uriDelegations;
748
 
749
        Hashtable       doctypes;
750
 
751
        Vector          next;
752
 
753
        // nonpublic!
754
        Catalog () { }
755
 
756
 
757
        // steps as found in OASIS XML catalog spec 7.1.2
758
        private InputSource locatePublicId (String publicId)
759
        throws SAXException, IOException
760
        {
761
            // 5. return (first) 'public' entry
762
            if (publicIds != null) {
763
                String  retval = (String) publicIds.get (publicId);
764
                if (retval != null) {
765
                    // IF the URI is accessible ...
766
                    return new InputSource (retval);
767
                }
768
            }
769
 
770
            // 6. return delegatePublic catalog match [complex]
771
            if (publicDelegations != null)
772
                return checkDelegations (publicDelegations, publicId,
773
                                publicId, null);
774
 
775
            return null;
776
        }
777
 
778
        // steps as found in OASIS XML catalog spec 7.1.2 or 7.2.2
779
        private InputSource mapURI (
780
            String      uri,
781
            Hashtable   ids,
782
            Hashtable   rewrites,
783
            Hashtable   delegations
784
        ) throws SAXException, IOException
785
        {
786
            // 7.1.2: 2. return (first) 'system' entry
787
            // 7.2.2: 2. return (first) 'uri' entry
788
            if (ids != null) {
789
                String  retval = (String) ids.get (uri);
790
                if (retval != null) {
791
                    // IF the URI is accessible ...
792
                    return new InputSource (retval);
793
                }
794
            }
795
 
796
            // 7.1.2: 3. return 'rewriteSystem' entries
797
            // 7.2.2: 3. return 'rewriteURI' entries
798
            if (rewrites != null) {
799
                String  prefix = null;
800
                String  replace = null;
801
                int     prefixLen = -1;
802
 
803
                for (Enumeration e = rewrites.keys ();
804
                        e.hasMoreElements ();
805
                        /* NOP */) {
806
                    String      temp = (String) e.nextElement ();
807
                    int         len = -1;
808
 
809
                    if (!uri.startsWith (temp))
810
                        continue;
811
                    if (prefix != null
812
                            && (len = temp.length ()) < prefixLen)
813
                        continue;
814
                    prefix = temp;
815
                    prefixLen = len;
816
                    replace = (String) rewrites.get (temp);
817
                }
818
                if (prefix != null) {
819
                    CPStringBuilder     buf = new CPStringBuilder (replace);
820
                    buf.append (uri.substring (prefixLen));
821
                    // IF the URI is accessible ...
822
                    return new InputSource (buf.toString ());
823
                }
824
            }
825
 
826
            // 7.1.2: 4. return 'delegateSystem' catalog match [complex]
827
            // 7.2.2: 4. return 'delegateURI' catalog match [complex]
828
            if (delegations != null)
829
                return checkDelegations (delegations, uri, null, uri);
830
 
831
            return null;
832
        }
833
 
834
 
835
        /**
836
         * Returns a URI for an external entity.
837
         */
838
        public InputSource resolve (
839
            boolean     usingPublic,
840
            String      publicId,
841
            String      systemId
842
        ) throws SAXException, IOException
843
        {
844
            boolean     preferSystem;
845
            InputSource retval;
846
 
847
            if (hasPreference)
848
                preferSystem = !this.usingPublic;
849
            else
850
                preferSystem = !usingPublic;
851
 
852
            if (publicId != null)
853
                publicId = normalizePublicId (false, publicId);
854
 
855
            // behavior here matches section 7.1.1 of the oasis spec
856
            if (systemId != null) {
857
                if (systemId.startsWith ("urn:publicid:")) {
858
                    String      temp = normalizePublicId (true, systemId);
859
                    if (publicId == null) {
860
                        publicId = temp;
861
                        systemId = null;
862
                    } else if (!publicId.equals (temp)) {
863
                        // error; ok to recover by:
864
                        systemId = null;
865
                    }
866
                } else
867
                    systemId = normalizeURI (systemId);
868
            }
869
 
870
            if (systemId == null && publicId == null)
871
                return null;
872
 
873
            if (systemId != null) {
874
                retval = mapURI (systemId, systemIds, systemRewrites,
875
                                        systemDelegations);
876
                if (retval != null) {
877
                    retval.setPublicId (publicId);
878
                    return retval;
879
                }
880
            }
881
 
882
            if (publicId != null
883
                    && !(systemId != null && preferSystem)) {
884
                retval = locatePublicId (publicId);
885
                if (retval != null) {
886
                    retval.setPublicId (publicId);
887
                    return retval;
888
                }
889
            }
890
 
891
            // 7. apply nextCatalog entries
892
            if (next != null) {
893
                int     length = next.size ();
894
                for (int i = 0; i < length; i++) {
895
                    Catalog     n = getNext (i);
896
                    retval = n.resolve (usingPublic, publicId, systemId);
897
                    if (retval != null)
898
                        return retval;
899
                }
900
            }
901
 
902
            return null;
903
        }
904
 
905
        /**
906
         * Maps one URI into another, for resources that are not defined
907
         * using XML external entity or notation syntax.
908
         */
909
        public InputSource resolveURI (String uri)
910
        throws SAXException, IOException
911
        {
912
            if (uri.startsWith ("urn:publicid:"))
913
                return resolve (true, normalizePublicId (true, uri), null);
914
 
915
            InputSource retval;
916
 
917
            uri = normalizeURI (uri);
918
 
919
            // 7.2.2 steps 2-4
920
            retval = mapURI (uri, uris, uriRewrites, uriDelegations);
921
            if (retval != null)
922
                return retval;
923
 
924
            // 7.2.2 step 5. apply nextCatalog entries
925
            if (next != null) {
926
                int     length = next.size ();
927
                for (int i = 0; i < length; i++) {
928
                    Catalog     n = getNext (i);
929
                    retval = n.resolveURI (uri);
930
                    if (retval != null)
931
                        return retval;
932
                }
933
            }
934
 
935
            return null;
936
        }
937
 
938
 
939
        /**
940
         * Finds the external subset associated with a given root element.
941
         */
942
        public InputSource getExternalSubset (String name)
943
        throws SAXException, IOException
944
        {
945
            if (doctypes != null) {
946
                String  value = (String) doctypes.get (name);
947
                if (value != null) {
948
                    // IF the URI is accessible ...
949
                    return new InputSource (value);
950
                }
951
            }
952
            if (next != null) {
953
                int     length = next.size ();
954
                for (int i = 0; i < length; i++) {
955
                    Catalog     n = getNext (i);
956
                    if (n == null)
957
                        continue;
958
                    InputSource retval = n.getExternalSubset (name);
959
                    if (retval != null)
960
                        return retval;
961
                }
962
            }
963
            return null;
964
        }
965
 
966
        private synchronized Catalog getNext (int i)
967
        throws SAXException, IOException
968
        {
969
            Object      obj;
970
 
971
            if (next == null || i < 0 || i >= next.size ())
972
                return null;
973
            obj = next.elementAt (i);
974
            if (obj instanceof Catalog)
975
                return (Catalog) obj;
976
 
977
            // ok, we deferred reading that catalog till now.
978
            // load and cache it.
979
            Catalog     cat = null;
980
 
981
            try {
982
                cat = loadCatalog (parserClass, eh, (String) obj, unified);
983
                next.setElementAt (cat, i);
984
            } catch (SAXException e) {
985
                // must fail quietly, says the OASIS spec
986
            } catch (IOException e) {
987
                // same applies here
988
            }
989
            return cat;
990
        }
991
 
992
        private InputSource checkDelegations (
993
            Hashtable   delegations,
994
            String      id,
995
            String      publicId,       // only one of public/system
996
            String      systemId        // will be non-null...
997
        ) throws SAXException, IOException
998
        {
999
            Vector      matches = null;
1000
            int         length = 0;
1001
 
1002
            // first, see if any prefixes match.
1003
            for (Enumeration e = delegations.keys ();
1004
                    e.hasMoreElements ();
1005
                    /* NOP */) {
1006
                String  prefix = (String) e.nextElement ();
1007
 
1008
                if (!id.startsWith (prefix))
1009
                    continue;
1010
                if (matches == null)
1011
                    matches = new Vector ();
1012
 
1013
                // maintain in longer->shorter sorted order
1014
                // NOTE:  assumes not many matches will fire!
1015
                int     index;
1016
 
1017
                for (index = 0; index < length; index++) {
1018
                    String      temp = (String) matches.elementAt (index);
1019
                    if (prefix.length () > temp.length ()) {
1020
                        matches.insertElementAt (prefix, index);
1021
                        break;
1022
                    }
1023
                }
1024
                if (index == length)
1025
                    matches.addElement (prefix);
1026
                length++;
1027
            }
1028
            if (matches == null)
1029
                return null;
1030
 
1031
            // now we know the list of catalogs to replace our "top level"
1032
            // list ... we use it here, rather than somehow going back and
1033
            // restarting, since this helps avoid reading most catalogs.
1034
            // this assumes stackspace won't be a problem.
1035
            for (int i = 0; i < length; i++) {
1036
                Catalog         catalog = null;
1037
                InputSource     result;
1038
 
1039
                // get this catalog.  we may not have read it yet.
1040
                synchronized (delegations) {
1041
                    Object      prefix = matches.elementAt (i);
1042
                    Object      cat = delegations.get (prefix);
1043
 
1044
                    if (cat instanceof Catalog)
1045
                        catalog = (Catalog) cat;
1046
                    else {
1047
                        try {
1048
                            // load and cache that catalog
1049
                            catalog = loadCatalog (parserClass, eh,
1050
                                    (String) cat, unified);
1051
                            delegations.put (prefix, catalog);
1052
                        } catch (SAXException e) {
1053
                            // must ignore, says the OASIS spec
1054
                        } catch (IOException e) {
1055
                            // same applies here
1056
                        }
1057
                    }
1058
                }
1059
 
1060
                // ignore failed loads, and proceed
1061
                if (catalog == null)
1062
                    continue;
1063
 
1064
                // we have a catalog ... resolve!
1065
                // usingPublic value can't matter, there's no choice
1066
                result = catalog.resolve (true, publicId, systemId);
1067
                if (result != null)
1068
                    return result;
1069
            }
1070
 
1071
            // if there were no successes, the entire
1072
            // lookup failed (all the way to top level)
1073
            throw new DoneDelegation ();
1074
        }
1075
    }
1076
 
1077
 
1078
    /** This is the namespace URI used for OASIS XML Catalogs.  */
1079
    private static final String catalogNamespace =
1080
        "urn:oasis:names:tc:entity:xmlns:xml:catalog";
1081
 
1082
 
1083
    /**
1084
     * Loads/unmarshals one catalog.
1085
     */
1086
    private static class Loader extends DefaultHandler2
1087
    {
1088
        private boolean         preInterned;
1089
        private ErrorHandler    handler;
1090
        private boolean         unified;
1091
        private int             ignoreDepth;
1092
        private Locator         locator;
1093
        private boolean         started;
1094
        private Hashtable       externals;
1095
        private Stack           bases;
1096
 
1097
        Catalog                 cat = new Catalog ();
1098
 
1099
 
1100
        /**
1101
         * Constructor.
1102
         * @param flag true iff the parser already interns strings.
1103
         * @param eh Errors and warnings are delegated to this.
1104
         * @param unified true keeps one table for URI mappings;
1105
         *      false matches OASIS spec, storing mappings
1106
         *      for URIs and SYSTEM ids in parallel tables.
1107
         */
1108
        Loader (boolean flag, ErrorHandler eh, boolean unified)
1109
        {
1110
            preInterned = flag;
1111
            handler = eh;
1112
            this.unified = unified;
1113
            cat.unified = unified;
1114
            cat.eh = eh;
1115
        }
1116
 
1117
 
1118
        // strips out fragments
1119
        private String nofrag (String uri)
1120
        throws SAXException
1121
        {
1122
            if (uri.indexOf ('#') != -1) {
1123
                warn ("URI with fragment: " + uri);
1124
                uri = uri.substring (0, uri.indexOf ('#'));
1125
            }
1126
            return uri;
1127
        }
1128
 
1129
        // absolutizes relative URIs
1130
        private String absolutize (String uri)
1131
        throws SAXException
1132
        {
1133
            // avoid creating URLs if they're already absolutized,
1134
            // or if the URI is already using a known scheme
1135
            if (uri.startsWith ("file:/")
1136
                    || uri.startsWith ("http:/")
1137
                    || uri.startsWith ("https:/")
1138
                    || uri.startsWith ("ftp:/")
1139
                    || uri.startsWith ("urn:")
1140
                    )
1141
                return uri;
1142
 
1143
            // otherwise, let's hope the JDK handles this URI scheme.
1144
            try {
1145
                URL     base = (URL) bases.peek ();
1146
                return new URL (base, uri).toString ();
1147
            } catch (Exception e) {
1148
                fatal ("can't absolutize URI: " + uri);
1149
                return null;
1150
            }
1151
        }
1152
 
1153
        // recoverable error
1154
        private void error (String message)
1155
        throws SAXException
1156
        {
1157
            if (handler == null)
1158
                return;
1159
            handler.error (new SAXParseException (message, locator));
1160
        }
1161
 
1162
        // nonrecoverable error
1163
        private void fatal (String message)
1164
        throws SAXException
1165
        {
1166
            SAXParseException   spe;
1167
 
1168
            spe = new SAXParseException (message, locator);
1169
            if (handler != null)
1170
                handler.fatalError (spe);
1171
            throw spe;
1172
        }
1173
 
1174
        // low severity problem
1175
        private void warn (String message)
1176
        throws SAXException
1177
        {
1178
            if (handler == null)
1179
                return;
1180
            handler.warning (new SAXParseException (message, locator));
1181
        }
1182
 
1183
        // callbacks:
1184
 
1185
        public void setDocumentLocator (Locator l)
1186
            { locator = l; }
1187
 
1188
        public void startDocument ()
1189
        throws SAXException
1190
        {
1191
            if (locator == null)
1192
                error ("no locator!");
1193
            bases = new Stack ();
1194
            String      uri = locator.getSystemId ();
1195
            try {
1196
                bases.push (new URL (uri));
1197
            } catch (IOException e) {
1198
                fatal ("bad document base URI: " + uri);
1199
            }
1200
        }
1201
 
1202
        public void endDocument ()
1203
        throws SAXException
1204
        {
1205
            try {
1206
                if (!started)
1207
                    error ("not a catalog!");
1208
            } finally {
1209
                locator = null;
1210
                handler = null;
1211
                externals = null;
1212
                bases = null;
1213
            }
1214
        }
1215
 
1216
        // XML Base support for external entities.
1217
 
1218
        // NOTE: expects parser is in default "resolve-dtd-uris" mode.
1219
        public void externalEntityDecl (String name, String pub, String sys)
1220
        throws SAXException
1221
        {
1222
            if (externals == null)
1223
                externals = new Hashtable ();
1224
            if (externals.get (name) == null)
1225
                externals.put (name, pub);
1226
        }
1227
 
1228
        public void startEntity (String name)
1229
        throws SAXException
1230
        {
1231
            if (externals == null)
1232
                return;
1233
            String uri = (String) externals.get (name);
1234
 
1235
            // NOTE: breaks if an EntityResolver substitutes these URIs.
1236
            // If toplevel loader supports one, must intercept calls...
1237
            if (uri != null) {
1238
                try {
1239
                    bases.push (new URL (uri));
1240
                } catch (IOException e) {
1241
                    fatal ("entity '" + name + "', bad URI: " + uri);
1242
                }
1243
            }
1244
        }
1245
 
1246
        public void endEntity (String name)
1247
        {
1248
            if (externals == null)
1249
                return;
1250
            String value = (String) externals.get (name);
1251
 
1252
            if (value != null)
1253
                bases.pop ();
1254
        }
1255
 
1256
        /**
1257
         * Processes catalog elements, saving their data.
1258
         */
1259
        public void startElement (String namespace, String local,
1260
            String qName, Attributes atts)
1261
        throws SAXException
1262
        {
1263
            // must ignore non-catalog elements, and their contents
1264
            if (ignoreDepth != 0 || !catalogNamespace.equals (namespace)) {
1265
                ignoreDepth++;
1266
                return;
1267
            }
1268
 
1269
            // basic sanity checks
1270
            if (!preInterned)
1271
                local = local.intern ();
1272
            if (!started) {
1273
                started = true;
1274
                if ("catalog" != local)
1275
                    fatal ("root element not 'catalog': " + local);
1276
            }
1277
 
1278
            // Handle any xml:base attribute
1279
            String      xmlbase = atts.getValue ("xml:base");
1280
 
1281
            if (xmlbase != null) {
1282
                URL     base = (URL) bases.peek ();
1283
                try {
1284
                    base = new URL (base, xmlbase);
1285
                } catch (IOException e) {
1286
                    fatal ("can't resolve xml:base attribute: " + xmlbase);
1287
                }
1288
                bases.push (base);
1289
            } else
1290
                bases.push (bases.peek ());
1291
 
1292
            // fetch multi-element attributes, apply standard tweaks
1293
            // values (uri, catalog, rewritePrefix) get normalized too,
1294
            // as a precaution and since we may compare the values
1295
            String      catalog = atts.getValue ("catalog");
1296
            if (catalog != null)
1297
                catalog = normalizeURI (absolutize (catalog));
1298
 
1299
            String      rewritePrefix = atts.getValue ("rewritePrefix");
1300
            if (rewritePrefix != null)
1301
                rewritePrefix = normalizeURI (absolutize (rewritePrefix));
1302
 
1303
            String      systemIdStartString;
1304
            systemIdStartString = atts.getValue ("systemIdStartString");
1305
            if (systemIdStartString != null) {
1306
                systemIdStartString = normalizeURI (systemIdStartString);
1307
                // unmatchable <rewriteSystemId>, <delegateSystemId> elements
1308
                if (systemIdStartString.startsWith ("urn:publicid:")) {
1309
                    error ("systemIdStartString is really a publicId!!");
1310
                    return;
1311
                }
1312
            }
1313
 
1314
            String      uri = atts.getValue ("uri");
1315
            if (uri != null)
1316
                uri = normalizeURI (absolutize (uri));
1317
 
1318
            String      uriStartString;
1319
            uriStartString = atts.getValue ("uriStartString");
1320
            if (uriStartString != null) {
1321
                uriStartString = normalizeURI (uriStartString);
1322
                // unmatchable <rewriteURI>, <delegateURI> elements
1323
                if (uriStartString.startsWith ("urn:publicid:")) {
1324
                    error ("uriStartString is really a publicId!!");
1325
                    return;
1326
                }
1327
            }
1328
 
1329
            // strictly speaking "group" and "catalog" shouldn't nest
1330
            // ... arbitrary restriction, no evident motivation
1331
 
1332
// FIXME stack "prefer" settings (two elements only!) and use
1333
// them to populate different public mapping/delegation tables
1334
 
1335
            if ("catalog" == local || "group" == local) {
1336
                String  prefer = atts.getValue ("prefer");
1337
 
1338
                if (prefer != null && !"public".equals (prefer)) {
1339
                    if (!"system".equals (prefer)) {
1340
                        error ("in <" + local + " ... prefer='...'>, "
1341
                            + "assuming 'public'");
1342
                        prefer = "public";
1343
                    }
1344
                }
1345
                if (prefer != null) {
1346
                    if ("catalog" == local) {
1347
                        cat.hasPreference = true;
1348
                        cat.usingPublic = "public".equals (prefer);
1349
                    } else {
1350
                        if (!cat.hasPreference || cat.usingPublic
1351
                                    != "public".equals (prefer)) {
1352
fatal ("<group prefer=...> case not handled");
1353
                        }
1354
                    }
1355
                } else if ("group" == local && cat.hasPreference) {
1356
fatal ("<group prefer=...> case not handled");
1357
                }
1358
 
1359
            //
1360
            // PUBLIC ids:  cleanly set up for id substitution
1361
            //
1362
            } else if ("public" == local) {
1363
                String  publicId = atts.getValue ("publicId");
1364
                String  value = null;
1365
 
1366
                if (publicId == null || uri == null) {
1367
                    error ("expecting <public publicId=... uri=.../>");
1368
                    return;
1369
                }
1370
                publicId = normalizePublicId (true, publicId);
1371
                uri = nofrag (uri);
1372
                if (cat.publicIds == null)
1373
                    cat.publicIds = new Hashtable ();
1374
                else
1375
                    value = (String) cat.publicIds.get (publicId);
1376
                if (value != null) {
1377
                    if (!value.equals (uri))
1378
                        warn ("ignoring <public...> entry for " + publicId);
1379
                } else
1380
                    cat.publicIds.put (publicId, uri);
1381
 
1382
            } else if ("delegatePublic" == local) {
1383
                String  publicIdStartString;
1384
                Object  value = null;
1385
 
1386
                publicIdStartString = atts.getValue ("publicIdStartString");
1387
                if (publicIdStartString == null || catalog == null) {
1388
                    error ("expecting <delegatePublic "
1389
                        + "publicIdStartString=... catalog=.../>");
1390
                    return;
1391
                }
1392
                publicIdStartString = normalizePublicId (true,
1393
                        publicIdStartString);
1394
                if (cat.publicDelegations == null)
1395
                    cat.publicDelegations = new Hashtable ();
1396
                else
1397
                    value = cat.publicDelegations.get (publicIdStartString);
1398
                if (value != null) {
1399
                    if (!value.equals (catalog))
1400
                        warn ("ignoring <delegatePublic...> entry for "
1401
                            + uriStartString);
1402
                } else
1403
                    cat.publicDelegations.put (publicIdStartString, catalog);
1404
 
1405
 
1406
            //
1407
            // SYSTEM ids:  need substitution due to operational issues
1408
            //
1409
            } else if ("system" == local) {
1410
                String  systemId = atts.getValue ("systemId");
1411
                String  value = null;
1412
 
1413
                if (systemId == null || uri == null) {
1414
                    error ("expecting <system systemId=... uri=.../>");
1415
                    return;
1416
                }
1417
                systemId = normalizeURI (systemId);
1418
                uri = nofrag (uri);
1419
                if (systemId.startsWith ("urn:publicid:")) {
1420
                    error ("systemId is really a publicId!!");
1421
                    return;
1422
                }
1423
                if (cat.systemIds == null) {
1424
                    cat.systemIds = new Hashtable ();
1425
                    if (unified)
1426
                        cat.uris = cat.systemIds;
1427
                } else
1428
                    value = (String) cat.systemIds.get (systemId);
1429
                if (value != null) {
1430
                    if (!value.equals (uri))
1431
                        warn ("ignoring <system...> entry for " + systemId);
1432
                } else
1433
                    cat.systemIds.put (systemId, uri);
1434
 
1435
            } else if ("rewriteSystem" == local) {
1436
                String  value = null;
1437
 
1438
                if (systemIdStartString == null || rewritePrefix == null
1439
                        || systemIdStartString.length () == 0
1440
                        || rewritePrefix.length () == 0
1441
                        ) {
1442
                    error ("expecting <rewriteSystem "
1443
                        + "systemIdStartString=... rewritePrefix=.../>");
1444
                    return;
1445
                }
1446
                if (cat.systemRewrites == null) {
1447
                    cat.systemRewrites = new Hashtable ();
1448
                    if (unified)
1449
                        cat.uriRewrites = cat.systemRewrites;
1450
                } else
1451
                    value = (String) cat.systemRewrites.get (
1452
                                                systemIdStartString);
1453
                if (value != null) {
1454
                    if (!value.equals (rewritePrefix))
1455
                        warn ("ignoring <rewriteSystem...> entry for "
1456
                            + systemIdStartString);
1457
                } else
1458
                    cat.systemRewrites.put (systemIdStartString,
1459
                                rewritePrefix);
1460
 
1461
            } else if ("delegateSystem" == local) {
1462
                Object  value = null;
1463
 
1464
                if (systemIdStartString == null || catalog == null) {
1465
                    error ("expecting <delegateSystem "
1466
                        + "systemIdStartString=... catalog=.../>");
1467
                    return;
1468
                }
1469
                if (cat.systemDelegations == null) {
1470
                    cat.systemDelegations = new Hashtable ();
1471
                    if (unified)
1472
                        cat.uriDelegations = cat.systemDelegations;
1473
                } else
1474
                    value = cat.systemDelegations.get (systemIdStartString);
1475
                if (value != null) {
1476
                    if (!value.equals (catalog))
1477
                        warn ("ignoring <delegateSystem...> entry for "
1478
                            + uriStartString);
1479
                } else
1480
                    cat.systemDelegations.put (systemIdStartString, catalog);
1481
 
1482
 
1483
            //
1484
            // URI:  just like "system" ID support, except that
1485
            // fragment IDs are disallowed in "system" elements.
1486
            //
1487
            } else if ("uri" == local) {
1488
                String  name = atts.getValue ("name");
1489
                String  value = null;
1490
 
1491
                if (name == null || uri == null) {
1492
                    error ("expecting <uri name=... uri=.../>");
1493
                    return;
1494
                }
1495
                if (name.startsWith ("urn:publicid:")) {
1496
                    error ("name is really a publicId!!");
1497
                    return;
1498
                }
1499
                name = normalizeURI (name);
1500
                if (cat.uris == null) {
1501
                    cat.uris = new Hashtable ();
1502
                    if (unified)
1503
                        cat.systemIds = cat.uris;
1504
                } else
1505
                    value = (String) cat.uris.get (name);
1506
                if (value != null) {
1507
                    if (!value.equals (uri))
1508
                        warn ("ignoring <uri...> entry for " + name);
1509
                } else
1510
                    cat.uris.put (name, uri);
1511
 
1512
            } else if ("rewriteURI" == local) {
1513
                String value = null;
1514
 
1515
                if (uriStartString == null || rewritePrefix == null
1516
                        || uriStartString.length () == 0
1517
                        || rewritePrefix.length () == 0
1518
                        ) {
1519
                    error ("expecting <rewriteURI "
1520
                        + "uriStartString=... rewritePrefix=.../>");
1521
                    return;
1522
                }
1523
                if (cat.uriRewrites == null) {
1524
                    cat.uriRewrites = new Hashtable ();
1525
                    if (unified)
1526
                        cat.systemRewrites = cat.uriRewrites;
1527
                } else
1528
                    value = (String) cat.uriRewrites.get (uriStartString);
1529
                if (value != null) {
1530
                    if (!value.equals (rewritePrefix))
1531
                        warn ("ignoring <rewriteURI...> entry for "
1532
                            + uriStartString);
1533
                } else
1534
                    cat.uriRewrites.put (uriStartString, rewritePrefix);
1535
 
1536
            } else if ("delegateURI" == local) {
1537
                Object  value = null;
1538
 
1539
                if (uriStartString == null || catalog == null) {
1540
                    error ("expecting <delegateURI "
1541
                        + "uriStartString=... catalog=.../>");
1542
                    return;
1543
                }
1544
                if (cat.uriDelegations == null) {
1545
                    cat.uriDelegations = new Hashtable ();
1546
                    if (unified)
1547
                        cat.systemDelegations = cat.uriDelegations;
1548
                } else
1549
                    value = cat.uriDelegations.get (uriStartString);
1550
                if (value != null) {
1551
                    if (!value.equals (catalog))
1552
                        warn ("ignoring <delegateURI...> entry for "
1553
                            + uriStartString);
1554
                } else
1555
                    cat.uriDelegations.put (uriStartString, catalog);
1556
 
1557
            //
1558
            // NON-DELEGATING approach to modularity
1559
            //
1560
            } else if ("nextCatalog" == local) {
1561
                if (catalog == null) {
1562
                    error ("expecting <nextCatalog catalog=.../>");
1563
                    return;
1564
                }
1565
                if (cat.next == null)
1566
                    cat.next = new Vector ();
1567
                cat.next.addElement (catalog);
1568
 
1569
            //
1570
            // EXTENSIONS from appendix E
1571
            //
1572
            } else if ("doctype" == local) {
1573
                String  name = atts.getValue ("name");
1574
                String  value = null;
1575
 
1576
                if (name == null || uri == null) {
1577
                    error ("expecting <doctype name=... uri=.../>");
1578
                    return;
1579
                }
1580
                name = normalizeURI (name);
1581
                if (cat.doctypes == null)
1582
                    cat.doctypes = new Hashtable ();
1583
                else
1584
                    value = (String) cat.doctypes.get (name);
1585
                if (value != null) {
1586
                    if (!value.equals (uri))
1587
                        warn ("ignoring <doctype...> entry for "
1588
                            + uriStartString);
1589
                } else
1590
                    cat.doctypes.put (name, uri);
1591
 
1592
 
1593
            //
1594
            // RESERVED ... ignore (like reserved attributes) but warn
1595
            //
1596
            } else {
1597
                warn ("ignoring unknown catalog element: " + local);
1598
                ignoreDepth++;
1599
            }
1600
        }
1601
 
1602
        public void endElement (String uri, String local, String qName)
1603
        throws SAXException
1604
        {
1605
            if (ignoreDepth != 0)
1606
                ignoreDepth--;
1607
            else
1608
                bases.pop ();
1609
        }
1610
    }
1611
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.