KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > apache > xerces > readers > XCatalog


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

57
58 package org.enhydra.apache.xerces.readers;
59
60 import java.io.IOException JavaDoc;
61 import java.io.InputStream JavaDoc;
62 import java.io.InputStreamReader JavaDoc;
63 import java.util.Enumeration JavaDoc;
64 import java.util.Hashtable JavaDoc;
65 import java.util.Vector JavaDoc;
66
67 import org.enhydra.apache.xerces.parsers.SAXParser;
68 import org.xml.sax.AttributeList JavaDoc;
69 import org.xml.sax.DocumentHandler JavaDoc;
70 import org.xml.sax.EntityResolver JavaDoc;
71 import org.xml.sax.InputSource JavaDoc;
72 import org.xml.sax.SAXException JavaDoc;
73
74 /**
75  * This catalog supports the XCatalog proposal draft 0.2 posted
76  * to the xml-dev mailing list by
77  * <a HREF="mailto:cowan@locke.ccil.org">John Cowan</a>. XCatalog
78  * is an XML representation of the SGML Open TR9401:1997 catalog
79  * format. The current proposal supports public identifier maps,
80  * system identifier aliases, and public identifier prefix
81  * delegates. Refer to the XCatalog DTD for the full specification
82  * of this catalog format at
83  * <a HREF="http://www.ccil.org/~cowan/XML/XCatalog.html">http://www.ccil.org/~cowan/XML/XCatalog.html</a>.
84  * <p>
85  * In order to use XCatalogs, you must write the catalog files
86  * with the following restrictions:
87  * <ul>
88  * <li>You must follow the XCatalog grammar.
89  * <li>You must specify the <tt>&lt;!DOCTYPE&gt;</tt> line with
90  * the <tt>PUBLIC</tt> specified as "-//DTD XCatalog//EN" or
91  * make sure that the system identifier is able to locate the
92  * XCatalog 0.2 DTD (which is included in the Jar file containing
93  * the org.enhydra.apache.xerces.readers.XCatalog class).
94  * For example:
95  * <pre>
96  * &lt;!DOCTYPE XCatalog PUBLIC "-//DTD XCatalog//EN" "org.enhydra.apache.xerces/readers/xcatalog.dtd"&gt;
97  * </pre>
98  * <li>The enclosing <tt>&lt;XCatalog&gt;</tt> document root
99  * element is <b>not</b> optional -- it <b>must</b> be specified.
100  * <li>The <tt>Version</tt> attribute of the <tt>&lt;XCatalog&gt;</tt>
101  * has been modified from '<tt><i>#FIXED "1.0"</i></tt>' to
102  * '<tt><i>(0.1|0.2) "0.2"</i></tt>'.
103  * </ul>
104  * <p>
105  * To use this catalog in a parser, set an XCatalog instance as the
106  * parser's <tt>EntityResolver</tt>. For example:
107  * <pre>
108  * XMLParser parser = new AnyParser();
109  * Catalog catalog = <font color="blue">new XCatalog()</font>;
110  * <font color="blue">parser.getEntityHandler().setEntityResolver(catalog);</font>
111  * </pre>
112  * <p>
113  * Once installed, catalog files that conform to the XCatalog grammar
114  * can be appended to the catalog by calling the <tt>loadCatalog</tt>
115  * method on the parser or the catalog instance. The following example
116  * loads the contents of two catalog files:
117  * <pre>
118  * parser.loadCatalog(new InputSource("catalogs/cat1.xml"));
119  * parser.loadCatalog(new InputSource("http://host/catalogs/cat2.xml"));
120  * </pre>
121  * <p>
122  * <b>Limitations:</b> The following are the current limitations
123  * of this XCatalog implementation:
124  * <ul>
125  * <li>No error checking is done to avoid circular <tt>Delegate</tt>
126  * or <tt>Extend</tt> references. Do not specify a combination of
127  * catalog files that reference each other.
128  * </ul>
129  *
130  * @author Andy Clark, IBM
131  * @version $Id: XCatalog.java,v 1.2 2005/01/26 08:28:44 jkjome Exp $
132  */

133 public class XCatalog
134     extends XMLCatalogHandler
135     {
136
137     //
138
// Constants
139
//
140

141     // public
142

143     /** XCatalog public identifier string ("-//DTD XCatalog//EN"). */
144     public static final String JavaDoc XCATALOG_DTD_PUBLICID = "-//DTD XCatalog//EN";
145
146     // "default"
147

148     /** XCatalog DTD resource name ("xcatalog.dtd"). */
149     static final String JavaDoc DTD = "xcatalog.dtd";
150
151     /** XCatalog element name ("XCatalog"). */
152     static final String JavaDoc XCATALOG = "XCatalog";
153
154     /** Map element name ("Map"). */
155     static final String JavaDoc MAP = "Map";
156
157     /** PublicID attribute name ("PublicID"). */
158     static final String JavaDoc PUBLICID = "PublicID";
159
160     /** HRef attribute name ("HRef"). */
161     static final String JavaDoc HREF = "HRef";
162
163     /** Delegate element name ("Delegate"). */
164     static final String JavaDoc DELEGATE = "Delegate";
165
166     /** Extend element name ("Extend"). */
167     static final String JavaDoc EXTEND = "Extend";
168
169     /** Base element name ("Base"). */
170     static final String JavaDoc BASE = "Base";
171
172     /** Remap element name ("Remap"). */
173     static final String JavaDoc REMAP = "Remap";
174
175     /** SystemID attribute name ("SystemID"). */
176     static final String JavaDoc SYSTEMID = "SystemID";
177
178     // private
179

180     /** Set to true and recompile to include debugging code in class. */
181     private static final boolean DEBUG = false;
182
183     //
184
// Data
185
//
186

187     /** Delegates. */
188     private Hashtable JavaDoc delegate = new Hashtable JavaDoc();
189
190     /** Delegates ordering. */
191     private Vector JavaDoc delegateOrder = new Vector JavaDoc();
192
193     //
194
// Constructors
195
//
196

197     /**
198      * Constructs an XCatalog instance.
199      */

200     public XCatalog() {
201     }
202
203     //
204
// Catalog methods
205
//
206

207     /**
208      * Loads the catalog stream specified by the given input source and
209      * appends the contents to the catalog.
210      *
211      * @param source The catalog source.
212      *
213      * @exception org.xml.sax.SAXException Throws exception on SAX error.
214      * @exception java.io.IOException Throws exception on i/o error.
215      */

216     public void loadCatalog(InputSource JavaDoc source)
217         throws SAXException JavaDoc, IOException JavaDoc
218         {
219         
220         new Parser(source);
221
222         /***
223         if (DEBUG) {
224             print("");
225             }
226         /***/

227
228         } // loadCatalog(InputSource)
229

230     /***
231     void print(String indent) {
232         System.out.println(indent+"# "+this);
233         Enumeration maps = getMapKeys();
234         while (maps.hasMoreElements()) {
235             String key = (String)maps.nextElement();
236             String value = getMapValue(key);
237             System.out.println(indent+"MAP \""+key+"\" -> \""+value+"\"");
238             }
239         Enumeration delegates = getDelegateKeys();
240         while (delegates.hasMoreElements()) {
241             String key = (String)delegates.nextElement();
242             XCatalog value = getDelegateValue(key);
243             System.out.println(indent+"DELEGATE \""+key+"\" -> "+value);
244             value.print(indent+" ");
245             }
246         Enumeration remaps = getRemapKeys();
247         while (remaps.hasMoreElements()) {
248             String key = (String)remaps.nextElement();
249             String value = getRemapValue(key);
250             System.out.println(indent+"REMAP \""+key+"\" -> \""+value+"\"");
251             }
252         }
253     /***/

254
255     //
256
// EntityResolver methods
257
//
258

259     /**
260      * Resolves external entities.
261      *
262      * @param publicId The public identifier used for entity resolution.
263      * @param systemId If the publicId is not null, this systemId is
264      * to be considered the default system identifier;
265      * else a system identifier alias mapping is
266      * requested.
267      *
268      * @return Returns the input source of the resolved entity or null
269      * if no resolution is possible.
270      *
271      * @exception org.xml.sax.SAXException Exception thrown on SAX error.
272      * @exception java.io.IOException Exception thrown on i/o error.
273      */

274     public InputSource JavaDoc resolveEntity(String JavaDoc publicId, String JavaDoc systemId)
275         throws SAXException JavaDoc, IOException JavaDoc
276         {
277
278         if (DEBUG) {
279             System.out.println("resolveEntity(\""+publicId+"\", \""+systemId+"\")");
280             }
281
282         // public identifier resolution
283
if (publicId != null) {
284             // direct public id mappings
285
String JavaDoc value = getPublicMapping(publicId);
286             if (DEBUG) {
287                 System.out.println(" map: \""+publicId+"\" -> \""+value+"\"");
288                 }
289             if (value != null) {
290                 InputSource JavaDoc source = resolveEntity(null, value);
291                 if (source == null) {
292                     source = new InputSource JavaDoc(value);
293                     }
294                 source.setPublicId(publicId);
295                 return source;
296                 }
297
298             // delegates
299
Enumeration JavaDoc delegates = getDelegateCatalogKeys();
300             while (delegates.hasMoreElements()) {
301                 String JavaDoc key = (String JavaDoc)delegates.nextElement();
302                 if (DEBUG) {
303                     System.out.println(" delegate: \""+key+"\"");
304                     }
305                 if (publicId.startsWith(key)) {
306                     XMLCatalogHandler catalog = getDelegateCatalog(key);
307                     InputSource JavaDoc source = catalog.resolveEntity(publicId, systemId);
308                     if (source != null) {
309                         return source;
310                         }
311                     }
312                 }
313             }
314
315         // system identifier resolution
316
String JavaDoc value = getSystemMapping(systemId);
317         if (value != null) {
318             if (DEBUG) {
319                 System.out.println(" remap: \""+systemId+"\" -> \""+value+"\"");
320                 }
321             InputSource JavaDoc source = new InputSource JavaDoc(value);
322             source.setPublicId(publicId);
323             return source;
324             }
325
326         // use default behavior
327
if (DEBUG) {
328             System.out.println(" returning null!");
329             }
330         return null;
331
332         } // resolveEntity(String,String):InputSource
333

334     //
335
// Public methods
336
//
337

338     /**
339      * Adds a delegate mapping. If the prefix of a public identifier
340      * matches a delegate prefix, then the delegate catalog is
341      * searched in order to resolve the identifier.
342      * <p>
343      * This method makes sure that prefixes that match each other
344      * are inserted into the delegate list in order of longest prefix
345      * length first.
346      *
347      * @param prefix The delegate prefix.
348      * @param catalog The delegate catalog.
349      */

350     public void addDelegateCatalog(String JavaDoc prefix, XCatalog catalog) {
351
352         synchronized (delegate) {
353             // insert prefix in proper order
354
if (!delegate.containsKey(prefix)) {
355                 int size = delegateOrder.size();
356                 boolean found = false;
357                 for (int i = 0; i < size; i++) {
358                     String JavaDoc element = (String JavaDoc)delegateOrder.elementAt(i);
359                     if (prefix.startsWith(element) || prefix.compareTo(element) < 0) {
360                         delegateOrder.insertElementAt(prefix, i);
361                         found = true;
362                         break;
363                         }
364                     }
365                 if (!found) {
366                     delegateOrder.addElement(prefix);
367                     }
368                 }
369
370             // replace (or add new) prefix mapping
371
delegate.put(prefix, catalog);
372             }
373
374         } // addDelegateCatalog(String,XCatalog)
375

376     /**
377      * Removes a delegate.
378      *
379      * @param prefix The delegate prefix to remove.
380      */

381     public void removeDelegateCatalog(String JavaDoc prefix) {
382
383         synchronized (delegate) {
384             delegate.remove(prefix);
385             delegateOrder.removeElement(prefix);
386             }
387
388         } // removeDelegateCatalog(String)
389

390     /** Returns an enumeration of delegate prefixes. */
391     public Enumeration JavaDoc getDelegateCatalogKeys() {
392         return delegateOrder.elements();
393         }
394
395     /** Returns the catalog for the given delegate prefix. */
396     public XCatalog getDelegateCatalog(String JavaDoc prefix) {
397         return (XCatalog)delegate.get(prefix);
398         }
399
400     //
401
// "default" methods
402
//
403

404     /** Returns true if the string is a valid URL. */
405     boolean isURL(String JavaDoc str) {
406         try {
407             new java.net.URL JavaDoc(str);
408             return true;
409             }
410         catch (java.net.MalformedURLException JavaDoc e) {
411             // assume the worst
412
}
413         return false;
414         }
415
416     //
417
// Classes
418
//
419

420     /** Parser for XCatalog document instances. */
421     class Parser
422         extends SAXParser
423         implements DocumentHandler JavaDoc
424         {
425
426         //
427
// Data
428
//
429

430         /** The base. */
431         private String JavaDoc base;
432
433         //
434
// Constructors
435
//
436

437         /** Parses the specified input source. */
438         public Parser(InputSource JavaDoc source)
439             throws SAXException JavaDoc, IOException JavaDoc
440             {
441
442             // setup parser
443
setEntityResolver(new Resolver JavaDoc());
444             setDocumentHandler((DocumentHandler JavaDoc)this);
445
446             // set base and parse
447
setBase(source.getSystemId());
448             parse(source);
449
450             } // <init>(InputSource)
451

452         //
453
// Protected methods
454
//
455

456         /**
457          * Sets the base from the given system identifier. The base is
458          * the same as the system identifier with the least significant
459          * part (the filename) removed.
460          */

461         protected void setBase(String JavaDoc systemId) throws SAXException JavaDoc {
462
463             // normalize system id
464
if (systemId == null) {
465                 systemId = "";
466                 }
467
468             // expand system id
469
systemId = fEntityHandler.expandSystemId(systemId);
470
471             // cut off the least significant part
472
int index = systemId.lastIndexOf('/');
473             if (index != -1) {
474                 systemId = systemId.substring(0, index + 1);
475                 }
476
477             // save base
478
base = systemId;
479
480             } // setBase(String)
481

482         //
483
// DocumentHandler methods
484
//
485

486         /** Not implemented. */
487         public void processingInstruction(String JavaDoc target, String JavaDoc data) {}
488
489         /** Not implemented. */
490         public void setDocumentLocator(org.xml.sax.Locator JavaDoc locator) {}
491
492         /** Not implemented. */
493         public void startDocument() {}
494
495         /** Not implemented. */
496         public void endElement(String JavaDoc elementName) {}
497
498         /** Not implemented. */
499         public void endDocument() {}
500
501         /** Not implemented. */
502         public void characters(char ch[], int start, int length) {}
503
504         /** Not implemented. */
505         public void ignorableWhitespace(char ch[], int start, int length) {}
506
507         /** The start of an element. */
508         public void startElement(String JavaDoc elementName, AttributeList JavaDoc attrList)
509             throws SAXException JavaDoc
510             {
511
512             try {
513                 // <XCatalog Version="...">
514
if (elementName.equals(XCATALOG)) {
515                     return;
516                     }
517     
518                 // <Map PublicID="..." HRef="..."/>
519
if (elementName.equals(MAP)) {
520                     // get attributes
521
String JavaDoc publicId = attrList.getValue(PUBLICID);
522                     String JavaDoc href = attrList.getValue(HREF);
523                     if (DEBUG) {
524                         System.out.println("MAP \""+publicId+"\" \""+href+"\"");
525                         }
526     
527                     // create mapping
528
if (!isURL(href)) {
529                         href = base + href;
530                         }
531                     addPublicMapping(publicId, href);
532                     }
533     
534                 // <Delegate PublicId="..." HRef="..."/>
535
else if (elementName.equals(DELEGATE)) {
536                     // get attributes
537
String JavaDoc publicId = attrList.getValue(PUBLICID);
538                     String JavaDoc href = attrList.getValue(HREF);
539                     if (DEBUG) {
540                         System.out.println("DELEGATE \""+publicId+"\" \""+href+"\"");
541                         }
542     
543                     // expand system id
544
if (!isURL(href)) {
545                         href = base + href;
546                         }
547                     String JavaDoc systemId = fEntityHandler.expandSystemId(href);
548     
549                     // create delegate
550
XCatalog catalog = new XCatalog();
551                     catalog.loadCatalog(new InputSource JavaDoc(systemId));
552                     addDelegateCatalog(publicId, catalog);
553                     }
554     
555                 // <Extend HRef="..."/>
556
else if (elementName.equals(EXTEND)) {
557                     // get attributes
558
String JavaDoc href = attrList.getValue(HREF);
559                     if (DEBUG) {
560                         System.out.println("EXTEND \""+href+"\"");
561                         }
562     
563                     // expand system id
564
if (!isURL(href)) {
565                         href = base + href;
566                         }
567                     String JavaDoc systemId = fEntityHandler.expandSystemId(href);
568     
569                     // create catalog
570
XCatalog.this.loadCatalog(new InputSource JavaDoc(systemId));
571                     }
572     
573                 // <Base HRef="..."/>
574
else if (elementName.equals(BASE)) {
575                     // get attributes
576
String JavaDoc href = attrList.getValue(HREF);
577     
578                     // set new base
579
setBase(href);
580                     if (DEBUG) {
581                         System.out.println("BASE \""+href+"\" -> \""+base+"\"");
582                         }
583                     }
584                 
585                 // <Remap SystemID="..." HRef="..."/>
586
else if (elementName.equals(REMAP)) {
587                     // get attributes
588
String JavaDoc systemId = attrList.getValue(SYSTEMID);
589                     String JavaDoc href = attrList.getValue(HREF);
590                     if (DEBUG) {
591                         System.out.println("REMAP \""+systemId+"\" \""+href+"\"");
592                         }
593     
594                     // create mapping
595
if (!isURL(href)) {
596                         href = base + href;
597                         }
598                     addSystemMapping(systemId, href);
599                     }
600                 }
601             catch (Exception JavaDoc e) {
602                 throw new SAXException JavaDoc(e);
603                 }
604
605             } // startElement(String,AttributeList)
606

607         //
608
// Classes
609
//
610

611         /** Resolver for locating the XCatalog DTD resource. */
612         class Resolver
613             implements EntityResolver JavaDoc
614             {
615
616             /** Resolves the XCatalog DTD entity. */
617             public InputSource JavaDoc resolveEntity(String JavaDoc publicId, String JavaDoc systemId)
618                 throws SAXException JavaDoc, IOException JavaDoc
619                 {
620
621                 // resolve the XCatalog DTD?
622
if (publicId != null && publicId.equals(XCATALOG_DTD_PUBLICID)) {
623                     InputSource JavaDoc src = new InputSource JavaDoc();
624                     src.setPublicId(publicId);
625                     InputStream JavaDoc is = getClass().getResourceAsStream(DTD);
626                     if (is == null) {
627                         throw new IOException JavaDoc("Can't file xcatalog DTD on classpath: " + DTD);
628                     }
629                     src.setByteStream(is);
630                     src.setCharacterStream(new InputStreamReader JavaDoc(is));
631                     return src;
632                     }
633
634                 // no resolution possible
635
return null;
636
637                 } // resolveEntity(String,String):InputSource
638

639             } // class Resolver
640

641         } // class Parser
642

643     } // class XCatalog
644
Popular Tags