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

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [libjava/] [classpath/] [gnu/] [xml/] [util/] [XCat.java] - Blame information for rev 14

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

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