KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xerces > util > XMLCatalogResolver


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

16  
17 package org.apache.xerces.util;
18
19 import java.io.IOException JavaDoc;
20
21 import org.xml.sax.InputSource JavaDoc;
22 import org.xml.sax.SAXException JavaDoc;
23 import org.xml.sax.ext.EntityResolver2 JavaDoc;
24
25 import org.w3c.dom.ls.LSInput JavaDoc;
26 import org.w3c.dom.ls.LSResourceResolver JavaDoc;
27
28 import javax.xml.parsers.SAXParserFactory JavaDoc;
29
30 import org.apache.xerces.dom.DOMInputImpl;
31 import org.apache.xerces.jaxp.SAXParserFactoryImpl;
32
33 import org.apache.xerces.xni.XNIException;
34 import org.apache.xerces.xni.XMLResourceIdentifier;
35
36 import org.apache.xerces.xni.parser.XMLEntityResolver;
37 import org.apache.xerces.xni.parser.XMLInputSource;
38
39 import org.apache.xml.resolver.Catalog;
40 import org.apache.xml.resolver.CatalogManager;
41 import org.apache.xml.resolver.readers.OASISXMLCatalogReader;
42 import org.apache.xml.resolver.readers.SAXCatalogReader;
43
44 /**
45  * <p>The catalog resolver handles the resolution of external
46  * identifiers and URI references through XML catalogs. This
47  * component supports XML catalogs defined by the
48  * <a HREF="http://www.oasis-open.org/committees/entity/spec.html">
49  * OASIS XML Catalogs Specification</a>. It encapsulates the
50  * <a HREF="http://xml.apache.org/commons/">XML Commons</a> resolver.
51  * An instance of this class may be registered on the parser
52  * as a SAX entity resolver, as a DOM LSResourceResolver or
53  * as an XNI entity resolver by setting the property
54  * (http://apache.org/xml/properties/internal/entity-resolver).</p>
55  *
56  * <p>It is intended that this class may be used standalone to perform
57  * catalog resolution outside of a parsing context. It may be shared
58  * between several parsers and the application.</p>
59  *
60  * @author Michael Glavassevich, IBM
61  *
62  * @version $Id: XMLCatalogResolver.java,v 1.8 2005/05/02 21:44:37 mrglavas Exp $
63  */

64 public class XMLCatalogResolver
65     implements XMLEntityResolver, EntityResolver2 JavaDoc, LSResourceResolver JavaDoc {
66     
67     /** Internal catalog manager for Apache catalogs. **/
68     private CatalogManager fResolverCatalogManager = null;
69     
70     /** Internal catalog structure. **/
71     private Catalog fCatalog = null;
72     
73     /** An array of catalog URIs. **/
74     private String JavaDoc [] fCatalogsList = null;
75
76     /**
77      * Indicates whether the list of catalogs has
78      * changed since it was processed.
79      */

80     private boolean fCatalogsChanged = true;
81     
82     /** Application specified prefer public setting. **/
83     private boolean fPreferPublic = true;
84     
85     /**
86      * Indicates whether the application desires that
87      * the parser or some other component performing catalog
88      * resolution should use the literal system identifier
89      * instead of the expanded system identifier.
90      */

91     private boolean fUseLiteralSystemId = true;
92     
93     /**
94      * <p>Constructs a catalog resolver with a default configuration.</p>
95      */

96     public XMLCatalogResolver () {
97         this(null, true);
98     }
99     
100     /**
101      * <p>Constructs a catalog resolver with the given
102      * list of entry files.</p>
103      *
104      * @param catalogs an ordered array list of absolute URIs
105      */

106     public XMLCatalogResolver (String JavaDoc [] catalogs) {
107         this(catalogs, true);
108     }
109     
110     /**
111      * <p>Constructs a catalog resolver with the given
112      * list of entry files and the preference for whether
113      * system or public matches are preferred.</p>
114      *
115      * @param catalogs an ordered array list of absolute URIs
116      * @param preferPublic the prefer public setting
117      */

118     public XMLCatalogResolver (String JavaDoc [] catalogs, boolean preferPublic) {
119         init(catalogs, preferPublic);
120     }
121     
122     /**
123      * <p>Returns the initial list of catalog entry files.</p>
124      *
125      * @return the initial list of catalog entry files
126      */

127     public final synchronized String JavaDoc [] getCatalogList () {
128         return (fCatalogsList != null)
129             ? (String JavaDoc[]) fCatalogsList.clone() : null;
130     }
131
132     /**
133      * <p>Sets the initial list of catalog entry files.
134      * If there were any catalog mappings cached from
135      * the previous list they will be replaced by catalog
136      * mappings from the new list the next time the catalog
137      * is queried.</p>
138      *
139      * @param catalogs an ordered array list of absolute URIs
140      */

141     public final synchronized void setCatalogList (String JavaDoc [] catalogs) {
142         fCatalogsChanged = true;
143         fCatalogsList = (catalogs != null)
144             ? (String JavaDoc[]) catalogs.clone() : null;
145     }
146     
147     /**
148      * <p>Forces the cache of catalog mappings to be cleared.</p>
149      */

150     public final synchronized void clear () {
151         fCatalog = null;
152     }
153     
154     /**
155      * <p>Returns the preference for whether system or public
156      * matches are preferred. This is used in the absence
157      * of any occurence of the <code>prefer</code> attribute
158      * on the <code>catalog</code> entry of a catalog. If this
159      * property has not yet been explicitly set its value is
160      * <code>true</code>.</p>
161      *
162      * @return the prefer public setting
163      */

164     public final boolean getPreferPublic () {
165         return fPreferPublic;
166     }
167     
168     /**
169      * <p>Sets the preference for whether system or public
170      * matches are preferred. This is used in the absence
171      * of any occurence of the <code>prefer</code> attribute
172      * on the <code>catalog</code> entry of a catalog.</p>
173      *
174      * @param preferPublic the prefer public setting
175      */

176     public final void setPreferPublic (boolean preferPublic) {
177         fPreferPublic = preferPublic;
178         fResolverCatalogManager.setPreferPublic(preferPublic);
179     }
180     
181     /**
182      * <p>Returns the preference for whether the literal system
183      * identifier should be used when resolving system
184      * identifiers when both it and the expanded system
185      * identifier are available. If this property has not yet
186      * been explicitly set its value is <code>true</code>.</p>
187      *
188      * @return the preference for using literal system identifers
189      * for catalog resolution
190      *
191      * @see #setUseLiteralSystemId
192      */

193     public final boolean getUseLiteralSystemId () {
194         return fUseLiteralSystemId;
195     }
196     
197     /**
198      * <p>Sets the preference for whether the literal system
199      * identifier should be used when resolving system
200      * identifiers when both it and the expanded system
201      * identifier are available.</p>
202      *
203      * <p>The literal system identifier is the URI as it was
204      * provided before absolutization. It may be embedded within
205      * an entity. It may be provided externally or it may be the
206      * result of redirection. For example, redirection may
207      * have come from the protocol level through HTTP or from
208      * an application's entity resolver.</p>
209      *
210      * <p>The expanded system identifier is an absolute URI
211      * which is the result of resolving the literal system
212      * identifier against a base URI.</p>
213      *
214      * @param useLiteralSystemId the preference for using
215      * literal system identifers for catalog resolution
216      */

217     public final void setUseLiteralSystemId (boolean useLiteralSystemId) {
218         fUseLiteralSystemId = useLiteralSystemId;
219     }
220     
221     /**
222      * <p>Resolves an external entity. If the entity cannot be
223      * resolved, this method should return <code>null</code>. This
224      * method returns an input source if an entry was found in the
225      * catalog for the given external identifier. It should be
226      * overrided if other behaviour is required.</p>
227      *
228      * @param publicId the public identifier, or <code>null</code> if none was supplied
229      * @param systemId the system identifier
230      *
231      * @throws SAXException any SAX exception, possibly wrapping another exception
232      * @throws IOException thrown if some i/o error occurs
233      */

234     public InputSource JavaDoc resolveEntity(String JavaDoc publicId, String JavaDoc systemId)
235          throws SAXException JavaDoc, IOException JavaDoc {
236         
237         String JavaDoc resolvedId = null;
238         if (publicId != null && systemId != null) {
239             resolvedId = resolvePublic(publicId, systemId);
240         }
241         else if (systemId != null) {
242             resolvedId = resolveSystem(systemId);
243         }
244         
245         if (resolvedId != null) {
246             InputSource JavaDoc source = new InputSource JavaDoc(resolvedId);
247             source.setPublicId(publicId);
248             return source;
249         }
250         return null;
251     }
252     
253      /**
254       * <p>Resolves an external entity. If the entity cannot be
255       * resolved, this method should return <code>null</code>. This
256       * method returns an input source if an entry was found in the
257       * catalog for the given external identifier. It should be
258       * overrided if other behaviour is required.</p>
259       *
260       * @param name the identifier of the external entity
261       * @param publicId the public identifier, or <code>null</code> if none was supplied
262       * @param baseURI the URI with respect to which relative systemIDs are interpreted.
263       * @param systemId the system identifier
264       *
265       * @throws SAXException any SAX exception, possibly wrapping another exception
266       * @throws IOException thrown if some i/o error occurs
267       */

268      public InputSource JavaDoc resolveEntity(String JavaDoc name, String JavaDoc publicId,
269          String JavaDoc baseURI, String JavaDoc systemId) throws SAXException JavaDoc, IOException JavaDoc {
270         
271          String JavaDoc resolvedId = null;
272          
273          if (!getUseLiteralSystemId() && baseURI != null) {
274              // Attempt to resolve the system identifier against the base URI.
275
try {
276                  URI uri = new URI(new URI(baseURI), systemId);
277                  systemId = uri.toString();
278              }
279              // Ignore the exception. Fallback to the literal system identifier.
280
catch (URI.MalformedURIException ex) {}
281          }
282          
283          if (publicId != null && systemId != null) {
284              resolvedId = resolvePublic(publicId, systemId);
285          }
286          else if (systemId != null) {
287              resolvedId = resolveSystem(systemId);
288          }
289         
290          if (resolvedId != null) {
291              InputSource JavaDoc source = new InputSource JavaDoc(resolvedId);
292              source.setPublicId(publicId);
293              return source;
294          }
295          return null;
296     }
297
298      /**
299       * <p>Locates an external subset for documents which do not explicitly
300       * provide one. This method always returns <code>null</code>. It
301       * should be overrided if other behaviour is required.</p>
302       *
303       * @param name the identifier of the document root element
304       * @param baseURI the document's base URI
305       *
306       * @throws SAXException any SAX exception, possibly wrapping another exception
307       * @throws IOException thrown if some i/o error occurs
308       */

309      public InputSource JavaDoc getExternalSubset(String JavaDoc name, String JavaDoc baseURI)
310          throws SAXException JavaDoc, IOException JavaDoc {
311          return null;
312      }
313
314     /**
315      * <p>Resolves a resource using the catalog. This method interprets that
316      * the namespace URI corresponds to uri entries in the catalog.
317      * Where both a namespace and an external identifier exist, the namespace
318      * takes precedence.</p>
319      *
320      * @param type the type of the resource being resolved
321      * @param namespaceURI the namespace of the resource being resolved,
322      * or <code>null</code> if none was supplied
323      * @param publicId the public identifier of the resource being resolved,
324      * or <code>null</code> if none was supplied
325      * @param systemId the system identifier of the resource being resolved,
326      * or <code>null</code> if none was supplied
327      * @param baseURI the absolute base URI of the resource being parsed,
328      * or <code>null</code> if there is no base URI
329      */

330     public LSInput JavaDoc resolveResource(String JavaDoc type, String JavaDoc namespaceURI,
331         String JavaDoc publicId, String JavaDoc systemId, String JavaDoc baseURI) {
332
333         String JavaDoc resolvedId = null;
334         
335         try {
336             // The namespace is useful for resolving namespace aware
337
// grammars such as XML schema. Let it take precedence over
338
// the external identifier if one exists.
339
if (namespaceURI != null) {
340                 resolvedId = resolveURI(namespaceURI);
341             }
342             
343             if (!getUseLiteralSystemId() && baseURI != null) {
344                 // Attempt to resolve the system identifier against the base URI.
345
try {
346                     URI uri = new URI(new URI(baseURI), systemId);
347                     systemId = uri.toString();
348                 }
349                 // Ignore the exception. Fallback to the literal system identifier.
350
catch (URI.MalformedURIException ex) {}
351             }
352         
353             // Resolve against an external identifier if one exists. This
354
// is useful for resolving DTD external subsets and other
355
// external entities. For XML schemas if there was no namespace
356
// mapping we might be able to resolve a system identifier
357
// specified as a location hint.
358
if (resolvedId == null) {
359                 if (publicId != null && systemId != null) {
360                     resolvedId = resolvePublic(publicId, systemId);
361                 }
362                 else if (systemId != null) {
363                     resolvedId = resolveSystem(systemId);
364                 }
365             }
366         }
367         // Ignore IOException. It cannot be thrown from this method.
368
catch (IOException JavaDoc ex) {}
369         
370         if (resolvedId != null) {
371             return new DOMInputImpl(publicId, resolvedId, baseURI);
372         }
373         return null;
374     }
375     
376     
377     /**
378      * <p>Resolves an external entity. If the entity cannot be
379      * resolved, this method should return <code>null</code>. This
380      * method only calls <code>resolveIdentifier</code> and returns
381      * an input source if an entry was found in the catalog. It
382      * should be overrided if other behaviour is required.</p>
383      *
384      * @param resourceIdentifier location of the XML resource to resolve
385      *
386      * @throws XNIException thrown on general error
387      * @throws IOException thrown if some i/o error occurs
388      */

389     public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier)
390         throws XNIException, IOException JavaDoc {
391
392         String JavaDoc resolvedId = resolveIdentifier(resourceIdentifier);
393         if (resolvedId != null) {
394             return new XMLInputSource(resourceIdentifier.getPublicId(),
395                                       resolvedId,
396                                       resourceIdentifier.getBaseSystemId());
397         }
398         return null;
399     }
400     
401     /**
402      * <p>Resolves an identifier using the catalog. This method interprets that
403      * the namespace of the identifier corresponds to uri entries in the catalog.
404      * Where both a namespace and an external identifier exist, the namespace
405      * takes precedence.</p>
406      *
407      * @param resourceIdentifier the identifier to resolve
408      *
409      * @throws XNIException thrown on general error
410      * @throws IOException thrown if some i/o error occurs
411      */

412     public String JavaDoc resolveIdentifier(XMLResourceIdentifier resourceIdentifier)
413         throws IOException JavaDoc, XNIException {
414         
415         String JavaDoc resolvedId = null;
416
417         // The namespace is useful for resolving namespace aware
418
// grammars such as XML schema. Let it take precedence over
419
// the external identifier if one exists.
420
String JavaDoc namespace = resourceIdentifier.getNamespace();
421         if (namespace != null) {
422             resolvedId = resolveURI(namespace);
423         }
424         
425         // Resolve against an external identifier if one exists. This
426
// is useful for resolving DTD external subsets and other
427
// external entities. For XML schemas if there was no namespace
428
// mapping we might be able to resolve a system identifier
429
// specified as a location hint.
430
if (resolvedId == null) {
431             String JavaDoc publicId = resourceIdentifier.getPublicId();
432             String JavaDoc systemId = getUseLiteralSystemId()
433                 ? resourceIdentifier.getLiteralSystemId()
434                 : resourceIdentifier.getExpandedSystemId();
435             if (publicId != null && systemId != null) {
436                 resolvedId = resolvePublic(publicId, systemId);
437             }
438             else if (systemId != null) {
439                 resolvedId = resolveSystem(systemId);
440             }
441         }
442         return resolvedId;
443     }
444     
445     /**
446      * <p>Returns the URI mapping in the catalog for the given
447      * external identifier or <code>null</code> if no mapping
448      * exists. If the system identifier is an URN in the
449      * <code>publicid</code> namespace it is converted into
450      * a public identifier by URN "unwrapping" as specified
451      * in the XML Catalogs specification.</p>
452      *
453      * @param systemId the system identifier to locate in the catalog
454      *
455      * @return the mapped URI or <code>null</code> if no mapping
456      * was found in the catalog
457      *
458      * @throws IOException if an i/o error occurred while reading
459      * the catalog
460      */

461     public final synchronized String JavaDoc resolveSystem (String JavaDoc systemId)
462         throws IOException JavaDoc {
463
464         if (fCatalogsChanged) {
465             parseCatalogs();
466             fCatalogsChanged = false;
467         }
468         return (fCatalog != null)
469             ? fCatalog.resolveSystem(systemId) : null;
470     }
471     
472     /**
473      * <p>Returns the URI mapping in the catalog for the given
474      * external identifier or <code>null</code> if no mapping
475      * exists. Public identifiers are normalized before
476      * comparison.</p>
477      *
478      * @param publicId the public identifier to locate in the catalog
479      * @param systemId the system identifier to locate in the catalog
480      *
481      * @return the mapped URI or <code>null</code> if no mapping
482      * was found in the catalog
483      *
484      * @throws IOException if an i/o error occurred while reading
485      * the catalog
486      */

487     public final synchronized String JavaDoc resolvePublic (String JavaDoc publicId, String JavaDoc systemId)
488         throws IOException JavaDoc {
489
490         if (fCatalogsChanged) {
491             parseCatalogs();
492             fCatalogsChanged = false;
493         }
494         return (fCatalog != null)
495             ? fCatalog.resolvePublic(publicId, systemId) : null;
496     }
497     
498     /**
499      * <p>Returns the URI mapping in the catalog for the given URI
500      * reference or <code>null</code> if no mapping exists.
501      * URI comparison is case sensitive. If the URI reference
502      * is an URN in the <code>publicid</code> namespace
503      * it is converted into a public identifier by URN "unwrapping"
504      * as specified in the XML Catalogs specification and then
505      * resolution is performed following the semantics of
506      * external identifier resolution.</p>
507      *
508      * @param uri the URI to locate in the catalog
509      *
510      * @return the mapped URI or <code>null</code> if no mapping
511      * was found in the catalog
512      *
513      * @throws IOException if an i/o error occurred while reading
514      * the catalog
515      */

516     public final synchronized String JavaDoc resolveURI (String JavaDoc uri)
517         throws IOException JavaDoc {
518
519         if (fCatalogsChanged) {
520             parseCatalogs();
521             fCatalogsChanged = false;
522         }
523         return (fCatalog != null)
524             ? fCatalog.resolveURI(uri) : null;
525     }
526     
527     /**
528      * Initialization. Create a CatalogManager and set all
529      * the properties upfront. This prevents JVM wide system properties
530      * or a property file somewhere in the environment from affecting
531      * the behaviour of this catalog resolver.
532      */

533     private void init (String JavaDoc [] catalogs, boolean preferPublic) {
534         fCatalogsList = (catalogs != null) ? (String JavaDoc[]) catalogs.clone() : null;
535         fPreferPublic = preferPublic;
536         fResolverCatalogManager = new CatalogManager();
537         fResolverCatalogManager.setAllowOasisXMLCatalogPI(false);
538         fResolverCatalogManager.setCatalogClassName("org.apache.xml.resolver.Catalog");
539         fResolverCatalogManager.setCatalogFiles("");
540         fResolverCatalogManager.setIgnoreMissingProperties(true);
541         fResolverCatalogManager.setPreferPublic(fPreferPublic);
542         fResolverCatalogManager.setRelativeCatalogs(false);
543         fResolverCatalogManager.setUseStaticCatalog(false);
544         fResolverCatalogManager.setVerbosity(0);
545     }
546     
547     /**
548      * Instruct the <code>Catalog</code> to parse each of the
549      * catalogs in the list. Only the first catalog will actually be
550      * parsed immediately. The others will be queued and read if
551      * they are needed later.
552      */

553     private void parseCatalogs () throws IOException JavaDoc {
554         if (fCatalogsList != null) {
555             fCatalog = new Catalog(fResolverCatalogManager);
556             attachReaderToCatalog(fCatalog);
557             for (int i = 0; i < fCatalogsList.length; ++i) {
558                 String JavaDoc catalog = fCatalogsList[i];
559                 if (catalog != null && catalog.length() > 0) {
560                     fCatalog.parseCatalog(catalog);
561                 }
562             }
563         }
564         else {
565             fCatalog = null;
566         }
567     }
568     
569     /**
570      * Attaches the reader to the catalog.
571      */

572     private void attachReaderToCatalog (Catalog catalog) {
573
574         SAXParserFactory JavaDoc spf = new SAXParserFactoryImpl();
575         spf.setNamespaceAware(true);
576         spf.setValidating(false);
577
578         SAXCatalogReader saxReader = new SAXCatalogReader(spf);
579         saxReader.setCatalogParser(OASISXMLCatalogReader.namespaceName, "catalog",
580             "org.apache.xml.resolver.readers.OASISXMLCatalogReader");
581         catalog.addReader("application/xml", saxReader);
582     }
583 }
584
Popular Tags