KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > avalon > excalibur > catalog > CatalogEntityResolver


1 /*
2  * Copyright (C) The Apache Software Foundation. All rights reserved.
3  *
4  * This software is published under the terms of the Apache Software License
5  * version 1.1, a copy of which has been included with this distribution in
6  * the LICENSE.txt file.
7  */

8 package org.apache.avalon.excalibur.catalog;
9
10 import java.io.IOException JavaDoc;
11 import java.io.InputStream JavaDoc;
12 import java.lang.Integer JavaDoc;
13 import java.net.MalformedURLException JavaDoc;
14 import java.net.URL JavaDoc;
15 import org.xml.sax.EntityResolver JavaDoc;
16 import org.xml.sax.InputSource JavaDoc;
17
18 /**
19  * <p>Implements SAX entityResolver using OASIS Open Catalogs.</p>
20  *
21  * This class implements the SAX entityResolver interface. It uses OASIS Open
22  * catalog files to provide a facility for mapping public or system identifiers
23  * in source documents to local system identifiers. <p>
24  *
25  * This code interrogates the following non-standard system properties:</p> <dl>
26  * <dt><b>xml.catalog.debug</b></dt> <dd><p>
27  *
28  * Sets the debug level. A value of 0 is assumed if the property is not set or
29  * is not a number.</p></dd></dl>
30  *
31  * @author Arbortext, Inc.
32  * @author <a HREF="mailto:nwalsh@arbortext.com">Norman Walsh</a>
33  * @version 1.0
34  * @see Catalog
35  */

36 public class CatalogEntityResolver implements EntityResolver JavaDoc
37 {
38     /**
39      * <p>
40      *
41      * The debug level</p> <p>
42      *
43      * In general, higher numbers produce more information:</p>
44      * <ul>
45      * <li> 0, no messages
46      * <li> 1, minimal messages (high-level status)
47      * <li> 2, detailed messages
48      * </ul>
49      *
50      */

51     public int debug = 0;
52
53     /**
54      * <p>
55      *
56      * The OASIS Open Catalog used for entity resolution.</p> <p>
57      *
58      * This field is exposed so that the catalog can be updated after creating
59      * the instance of CatalogEntityResolver that will be used by the parser.
60      * </p>
61      */

62     public Catalog catalog = null;
63
64     /**
65      * <p>
66      *
67      * Indicates that unusable system identifiers should be ignored.</p>
68      */

69     private boolean retryBadSystemIds = false;
70
71     /**
72      * <p>
73      *
74      * Constructs a CatalogEntityResolver with an empty catalog.</p>
75      */

76     public CatalogEntityResolver()
77     {
78         String JavaDoc property = System.getProperty( "xml.catalog.debug" );
79
80         if( property != null )
81         {
82             try
83             {
84                 debug = Integer.parseInt( property );
85             }
86             catch( NumberFormatException JavaDoc e )
87             {
88                 debug = 0;
89             }
90         }
91     }
92
93     /**
94      * <p>
95      *
96      * Parse a Catalog file.</p> <p>
97      *
98      * This is really just a convenience method which calls <a HREF="#catalog">
99      * <code>catalog.parseCatalog()</code></a> .</p>
100      *
101      * @param fileName The filename of the catalog file to process
102      * @throws MalformedURLException The fileName cannot be turned into a valid
103      * URL.
104      * @throws IOException Error reading catalog file.
105      */

106     public synchronized void parseCatalog( String JavaDoc fileName )
107         throws MalformedURLException JavaDoc, IOException JavaDoc
108     {
109         catalog.parseCatalog( fileName );
110     }
111
112     /**
113      * <p>
114      *
115      * Implements the <code>resolveEntity</code> method for the SAX interface.
116      * </p><p>
117      *
118      * Presented with an optional public identifier and a system identifier,
119      * this function attempts to locate a mapping in the catalogs.</p> <p>
120      *
121      * If such a mapping is found, the resolver attempts to open the mapped
122      * value as an InputSource and return it. Exceptions are ignored and null is
123      * returned if the mapped value cannot be opened as an input source.</p> If
124      * no mapping is found (or an error occurs attempting to open the mapped
125      * value as an input source), null is returned and the system will use the
126      * specified system identifier as if no entityResolver was specified.</p>
127      *
128      * @param publicId The public identifier for the entity in question. This
129      * may be null.
130      * @param systemId The system identifier for the entity in question. XML
131      * requires a system identifier on all external entities, so this value
132      * is always specified.
133      * @return An InputSource for the mapped identifier, or null.
134      */

135     public InputSource JavaDoc resolveEntity( String JavaDoc publicId, String JavaDoc systemId )
136     {
137         String JavaDoc resolved = null;
138
139         if( systemId != null )
140         {
141             try
142             {
143                 resolved = catalog.resolveSystem( systemId );
144             }
145             catch( MalformedURLException JavaDoc me )
146             {
147                 debug( 1, "Malformed URL exception trying to resolve",
148                     publicId );
149                 resolved = null;
150             }
151             catch( IOException JavaDoc ie )
152             {
153                 debug( 1, "I/O exception trying to resolve", publicId );
154                 resolved = null;
155             }
156         }
157
158         if( resolved == null )
159         {
160             if( publicId != null )
161             {
162                 try
163                 {
164                     resolved = catalog.resolvePublic( publicId, systemId );
165                 }
166                 catch( MalformedURLException JavaDoc me )
167                 {
168                     debug( 1, "Malformed URL exception trying to resolve",
169                         publicId );
170                 }
171                 catch( IOException JavaDoc ie )
172                 {
173                     debug( 1, "I/O exception trying to resolve", publicId );
174                 }
175             }
176
177             if( resolved != null )
178             {
179                 debug( 2, "Resolved", publicId, resolved );
180             }
181         }
182         else
183         {
184             debug( 2, "Resolved", systemId, resolved );
185         }
186
187         if( resolved == null && retryBadSystemIds
188              && publicId != null && systemId != null )
189         {
190             URL JavaDoc systemURL = null;
191             try
192             {
193                 systemURL = new URL JavaDoc( systemId );
194             }
195             catch( MalformedURLException JavaDoc e )
196             {
197                 try
198                 {
199                     systemURL = new URL JavaDoc( "file:///" + systemId );
200                 }
201                 catch( MalformedURLException JavaDoc e2 )
202                 {
203                     systemURL = null;
204                 }
205             }
206
207             if( systemURL != null )
208             {
209                 try
210                 {
211                     InputStream JavaDoc iStream = systemURL.openStream();
212
213                     // The systemId can be opened, so that's the one that
214
// we'll use. There's no point making the caller open
215
// it again though...
216

217                     InputSource JavaDoc iSource = new InputSource JavaDoc( systemId );
218                     iSource.setPublicId( publicId );
219                     iSource.setByteStream( iStream );
220                     return iSource;
221                 }
222                 catch( Exception JavaDoc e )
223                 {
224                     // nop
225
}
226             }
227
228             // we got here, so it must be that the systemId cannot be
229
// opened and the caller wants us to retry...
230
debug( 2, "Failed to open", systemId );
231             debug( 2, "\tAttempting catalog lookup without system identifier." );
232             return resolveEntity( publicId, null );
233         }
234
235         if( resolved != null )
236         {
237             try
238             {
239                 InputSource JavaDoc iSource = new InputSource JavaDoc( resolved );
240                 iSource.setPublicId( publicId );
241
242                 // Ideally this method would not attempt to open the
243
// InputStream, but there is a bug (in Xerces, at least)
244
// that causes the parser to mistakenly open the wrong
245
// system identifier if the returned InputSource does
246
// not have a byteStream.
247
//
248
// It could be argued that we still shouldn't do this here,
249
// but since the purpose of calling the entityResolver is
250
// almost certainly to open the input stream, it seems to
251
// do little harm.
252
//
253
URL JavaDoc url = new URL JavaDoc( resolved );
254                 InputStream JavaDoc iStream = url.openStream();
255                 iSource.setByteStream( iStream );
256
257                 return iSource;
258             }
259             catch( Exception JavaDoc e )
260             {
261                 debug( 1, "Failed to create InputSource", resolved );
262                 return null;
263             }
264         }
265         return null;
266     }
267
268     /**
269      * <p>
270      *
271      * Set the Catalog that will be used to resolve entities.</p> <p>
272      *
273      * This is a convenience method for setting the <a HREF="#catalog"><code>
274      * catalog</code></a> field.</p>
275      *
276      * @param cat The new Catalog value
277      */

278     public void setCatalog( Catalog cat )
279     {
280         catalog = cat;
281     }
282
283     /**
284      * <p>
285      *
286      * Establish whether or not bad system identifiers should be ignored.</p>
287      * <p>
288      *
289      * The semantics of catalog file lookup are such that if a system identifier
290      * is supplied in the instance document, it is possible that it will be used
291      * in preference to alternative system identifiers in the catalog.</p> <p>
292      *
293      * If this variable is <code>true</code> and the system identifier passed to
294      * the entity resolver would be returned, the entity resolver attempts to
295      * open it. If it cannot be opened, the resolver does another catalog
296      * search, ignoring the fact that a system identifier was specified. If this
297      * second search locates a system identifer, it will be returned.</p> <p>
298      *
299      * This setting is initially <code>false</code> meaning that system
300      * identifiers in the document will be used in preference to some entries in
301      * the catalog.</p>
302      *
303      * @param retry If true, the resolver will retry Catalog lookups when the
304      * supplied system identifer cannot be opened.
305      */

306     public void setRetry( boolean retry )
307     {
308         retryBadSystemIds = retry;
309     }
310
311     /**
312      * <p>
313      *
314      * Print debug message (if the debug level is high enough).</p>
315      *
316      * @param level The debug level of this message. This message will only be
317      * displayed if the current debug level is at least equal to this
318      * value.
319      * @param message The text of the message.
320      */

321     private void debug( int level, String JavaDoc message )
322     {
323         if( debug >= level )
324         {
325             System.out.println( message );
326         }
327     }
328
329     /**
330      * <p>
331      *
332      * Print debug message (if the debug level is high enough).</p>
333      *
334      * @param level The debug level of this message. This message will only be
335      * displayed if the current debug level is at least equal to this
336      * value.
337      * @param message The text of the message.
338      * @param spec An argument to the message.
339      */

340     private void debug( int level, String JavaDoc message, String JavaDoc spec )
341     {
342         if( debug >= level )
343         {
344             System.out.println( message + ": " + spec );
345         }
346     }
347
348     /**
349      * <p>
350      *
351      * Print debug message (if the debug level is high enough).</p>
352      *
353      * @param level The debug level of this message. This message will only be
354      * displayed if the current debug level is at least equal to this
355      * value.
356      * @param message The text of the message.
357      * @param spec1 An argument to the message.
358      * @param spec2 DOC: Insert Description of Parameter
359      */

360     private void debug( int level, String JavaDoc message, String JavaDoc spec1, String JavaDoc spec2 )
361     {
362         if( debug >= level )
363         {
364             System.out.println( message + ": " + spec1 );
365             System.out.println( "\t" + spec2 );
366         }
367     }
368 }
369
Popular Tags