KickJava   Java API By Example, From Geeks To Geeks.

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


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.DataInputStream JavaDoc;
11 import java.io.FileNotFoundException JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.lang.Integer JavaDoc;
14 import java.net.MalformedURLException JavaDoc;
15 import java.net.URL JavaDoc;
16
17 /**
18  * <p>Parses OASIS Open Catalog files.</p>
19  *
20  * This class reads OASIS Open Catalog files, returning a stream of tokens.</p>
21  * <p>
22  *
23  * This code interrogates the following non-standard system properties:</p> <dl>
24  * <dt><b>xml.catalog.debug</b></dt> <dd><p>
25  *
26  * Sets the debug level. A value of 0 is assumed if the property is not set or
27  * is not a number.</p></dd></dl>
28  *
29  * @author Arbortext, Inc.
30  * @author <a HREF="mailto:nwalsh@arbortext.com">Norman Walsh</a>
31  * @version 1.0
32  * @see Catalog
33  */

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

49     public int debug = 0;
50
51     /**
52      * The filename (URL) of the catalog being read
53      */

54     private String JavaDoc catfilename = null;
55
56     /**
57      * The input stream used to read the catalog
58      */

59     private DataInputStream JavaDoc catfile = null;
60
61     /**
62      * Lookahead stack. Reading a catalog sometimes requires up to two
63      * characters of lookahead.
64      */

65     private int[] stack = new int[3];
66
67     /**
68      * The current position on the lookahead stack
69      */

70     private int top = -1;
71
72     /**
73      * <p>
74      *
75      * Construct a CatalogReader object.</p>
76      */

77     public CatalogReader()
78     {
79         String JavaDoc property = System.getProperty( "xml.catalog.debug" );
80
81         if( property != null )
82         {
83             try
84             {
85                 debug = Integer.parseInt( property );
86             }
87             catch( NumberFormatException JavaDoc e )
88             {
89                 debug = 0;
90             }
91         }
92     }
93
94     // -----------------------------------------------------------------
95

96     /**
97      * <p>
98      *
99      * Normalize a public identifier.</p> <p>
100      *
101      * Public identifiers must be normalized according to the following rules
102      * before comparisons between them can be made:</p>
103      * <ul>
104      * <li> Whitespace characters are normalized to spaces (e.g., line feeds,
105      * tabs, etc. become spaces).</li>
106      * <li> Leading and trailing whitespace is removed.</li>
107      * <li> Multiple internal whitespaces are normalized to a single space.
108      * </li>
109      * </ul>
110      * <p>
111      *
112      * This method is declared static so that other classes can use it directly.
113      * </p>
114      *
115      * @param publicId The unnormalized public identifier.
116      * @return The normalized identifier.
117      */

118     public static String JavaDoc normalize( String JavaDoc publicId )
119     {
120         String JavaDoc normal = publicId.replace( '\t', ' ' );
121         normal = normal.replace( '\r', ' ' );
122         normal = normal.replace( '\n', ' ' );
123         normal = normal.trim();
124
125         int pos;
126
127         while( ( pos = normal.indexOf( " " ) ) >= 0 )
128         {
129             normal = normal.substring( 0, pos ) + normal.substring( pos + 1 );
130         }
131
132         return normal;
133     }
134
135     /**
136      * <p>
137      *
138      * Start parsing an OASIS Open Catalog file. The file is actually read and
139      * parsed as needed by <code>nextEntry</code> .</p>
140      *
141      * @param fileUrl The URL or filename of the catalog file to process
142      * @throws MalformedURLException Improper fileUrl
143      * @throws IOException Error reading catalog file
144      */

145     public void parseCatalog( String JavaDoc fileUrl )
146         throws MalformedURLException JavaDoc, IOException JavaDoc
147     {
148         catfilename = fileUrl;
149         URL JavaDoc catalog;
150
151         try
152         {
153             catalog = new URL JavaDoc( fileUrl );
154         }
155         catch( MalformedURLException JavaDoc e )
156         {
157             catalog = new URL JavaDoc( "file:///" + fileUrl );
158         }
159
160         try
161         {
162             catfile = new DataInputStream JavaDoc( catalog.openStream() );
163         }
164         catch( FileNotFoundException JavaDoc e )
165         {
166             debug( 1, "Failed to load catalog, file not found", catalog.toString() );
167         }
168     }
169
170     /**
171      * <p>
172      *
173      * Get the next entry from the file</p>
174      *
175      * @return A CatalogEntry object for the next entry in the catalog
176      * @throws IOException Error reading catalog file
177      */

178     public CatalogEntry nextEntry()
179         throws IOException JavaDoc
180     {
181         if( catfile == null )
182         {
183             return null;
184         }
185
186         boolean confused = false;
187
188         while( true )
189         {
190             String JavaDoc token = nextToken();
191
192             if( token == null )
193             {
194                 catfile.close();
195                 catfile = null;
196                 return null;
197             }
198
199             if( token.equalsIgnoreCase( "BASE" )
200                  || token.equalsIgnoreCase( "CATALOG" )
201                  || token.equalsIgnoreCase( "DOCUMENT" )
202                  || token.equalsIgnoreCase( "OVERRIDE" )
203                  || token.equalsIgnoreCase( "SGMLDECL" ) )
204             {
205                 String JavaDoc spec = nextToken();
206                 confused = false;
207
208                 try
209                 {
210                     if( token.equalsIgnoreCase( "BASE" ) )
211                     {
212                         return new CatalogEntry( CatalogEntry.BASE, spec );
213                     }
214                     if( token.equalsIgnoreCase( "CATALOG" ) )
215                     {
216                         return new CatalogEntry( CatalogEntry.CATALOG, spec );
217                     }
218                     if( token.equalsIgnoreCase( "DOCUMENT" ) )
219                     {
220                         return new CatalogEntry( CatalogEntry.DOCUMENT, spec );
221                     }
222                     if( token.equalsIgnoreCase( "OVERRIDE" ) )
223                     {
224                         return new CatalogEntry( CatalogEntry.OVERRIDE, spec );
225                     }
226                     if( token.equalsIgnoreCase( "SGMLDECL" ) )
227                     {
228                         return new CatalogEntry( CatalogEntry.SGMLDECL, spec );
229                     }
230                 }
231                 catch( InvalidCatalogEntryTypeException icete )
232                 {
233                     debug( 1, "Invalid catalog entry type", token );
234                     confused = true;
235                 }
236                 catch( InvalidCatalogEntryException icete )
237                 {
238                     debug( 1, "Invalid catalog entry", token, spec );
239                     confused = true;
240                 }
241             }
242
243             if( token.equalsIgnoreCase( "DELEGATE" )
244                  || token.equalsIgnoreCase( "DOCTYPE" )
245                  || token.equalsIgnoreCase( "DTDDECL" )
246                  || token.equalsIgnoreCase( "ENTITY" )
247                  || token.equalsIgnoreCase( "LINKTYPE" )
248                  || token.equalsIgnoreCase( "NOTATION" )
249                  || token.equalsIgnoreCase( "PUBLIC" )
250                  || token.equalsIgnoreCase( "SYSTEM" ) )
251             {
252                 String JavaDoc spec1 = nextToken();
253                 String JavaDoc spec2 = nextToken();
254                 confused = false;
255                 try
256                 {
257                     if( token.equalsIgnoreCase( "DELEGATE" ) )
258                     {
259                         return new CatalogEntry( CatalogEntry.DELEGATE,
260                             normalize( spec1 ), spec2 );
261                     }
262                     if( token.equalsIgnoreCase( "DOCTYPE" ) )
263                     {
264                         return new CatalogEntry( CatalogEntry.DOCTYPE,
265                             spec1, spec2 );
266                     }
267                     if( token.equalsIgnoreCase( "DTDDECL" ) )
268                     {
269                         return new CatalogEntry( CatalogEntry.DTDDECL,
270                             normalize( spec1 ), spec2 );
271                     }
272                     if( token.equalsIgnoreCase( "ENTITY" ) )
273                     {
274                         return new CatalogEntry( CatalogEntry.ENTITY,
275                             spec1, spec2 );
276                     }
277                     if( token.equalsIgnoreCase( "LINKTYPE" ) )
278                     {
279                         return new CatalogEntry( CatalogEntry.LINKTYPE,
280                             spec1, spec2 );
281                     }
282                     if( token.equalsIgnoreCase( "NOTATION" ) )
283                     {
284                         return new CatalogEntry( CatalogEntry.NOTATION,
285                             spec1, spec2 );
286                     }
287                     if( token.equalsIgnoreCase( "PUBLIC" ) )
288                     {
289                         return new CatalogEntry( CatalogEntry.PUBLIC,
290                             normalize( spec1 ), spec2 );
291                     }
292                     if( token.equalsIgnoreCase( "SYSTEM" ) )
293                     {
294                         return new CatalogEntry( CatalogEntry.SYSTEM,
295                             spec1, spec2 );
296                     }
297                 }
298                 catch( InvalidCatalogEntryTypeException icete )
299                 {
300                     debug( 1, "Invalid catalog entry type", token );
301                     confused = true;
302                 }
303                 catch( InvalidCatalogEntryException icete )
304                 {
305                     debug( 1, "Invalid catalog entry", token, spec1, spec2 );
306                     confused = true;
307                 }
308             }
309
310             if( !confused )
311             {
312                 if( debug > 1 )
313                 {
314                     System.out.println( "Unrecognized token parsing catalog: '"
315                          + catfilename
316                          + "': "
317                          + token );
318                     System.out.println( "\tSkipping to next recognized token." );
319                 }
320                 confused = true;
321             }
322         }
323     }
324
325     /**
326      * <p>
327      *
328      * The destructor.</p> <p>
329      *
330      * Makes sure the catalog file is closed.</p>
331      *
332      * @exception IOException DOC: Insert Description of Exception
333      */

334     protected void finalize()
335         throws IOException JavaDoc
336     {
337         if( catfile != null )
338         {
339             catfile.close();
340         }
341         catfile = null;
342     }
343
344     // -----------------------------------------------------------------
345

346     /**
347      * <p>
348      *
349      * Return the next token in the catalog file.</p>
350      *
351      * @return The Catalog file token from the input stream.
352      * @throws IOException If an error occurs reading from the stream.
353      */

354     private String JavaDoc nextToken()
355         throws IOException JavaDoc
356     {
357         String JavaDoc token = "";
358         int ch;
359         int nextch;
360
361         // Skip over leading whitespace and comments
362
while( true )
363         {
364             // skip leading whitespace
365
ch = catfile.read();
366             while( ch <= ' ' )
367             {
368                 // all ctrls are whitespace
369
ch = catfile.read();
370                 if( ch < 0 )
371                 {
372                     return null;
373                 }
374             }
375
376             // now 'ch' is the current char from the file
377
nextch = catfile.read();
378             if( nextch < 0 )
379             {
380                 return null;
381             }
382
383             if( ch == '-' && nextch == '-' )
384             {
385                 // we've found a comment, skip it...
386
ch = ' ';
387                 nextch = nextChar();
388                 while( ch != '-' || nextch != '-' )
389                 {
390                     ch = nextch;
391                     nextch = nextChar();
392                 }
393
394                 // Ok, we've found the end of the comment,
395
// loop back to the top and start again...
396
}
397             else
398             {
399                 stack[++top] = nextch;
400                 stack[++top] = ch;
401                 break;
402             }
403         }
404
405         ch = nextChar();
406         if( ch == '"' || ch == '\'' )
407         {
408             int quote = ch;
409             while( ( ch = nextChar() ) != quote )
410             {
411                 char[] chararr = new char[1];
412                 chararr[0] = (char)ch;
413                 String JavaDoc s = new String JavaDoc( chararr );
414                 token = token.concat( s );
415             }
416             return token;
417         }
418         else
419         {
420             // return the next whitespace or comment delimited
421
// string
422
while( ch > ' ' )
423             {
424                 nextch = nextChar();
425                 if( ch == '-' && nextch == '-' )
426                 {
427                     stack[++top] = ch;
428                     stack[++top] = nextch;
429                     return token;
430                 }
431                 else
432                 {
433                     char[] chararr = new char[1];
434                     chararr[0] = (char)ch;
435                     String JavaDoc s = new String JavaDoc( chararr );
436                     token = token.concat( s );
437                     ch = nextch;
438                 }
439             }
440             return token;
441         }
442     }
443
444     /**
445      * <p>
446      *
447      * Return the next logical character from the input stream.</p>
448      *
449      * @return The next (logical) character from the input stream. The character
450      * may be buffered from a previous lookahead.
451      * @throws IOException If an error occurs reading from the stream.
452      */

453     private int nextChar()
454         throws IOException JavaDoc
455     {
456         if( top < 0 )
457         {
458             return catfile.read();
459         }
460         else
461         {
462             return stack[top--];
463         }
464     }
465
466     /**
467      * <p>
468      *
469      * Print debug message (if the debug level is high enough).</p>
470      *
471      * @param level The debug level of this message. This message will only be
472      * displayed if the current debug level is at least equal to this
473      * value.
474      * @param message The text of the message.
475      * @param token The catalog file token being processed.
476      */

477     private void debug( int level, String JavaDoc message, String JavaDoc token )
478     {
479         if( debug >= level )
480         {
481             System.out.println( message + ": " + token );
482         }
483     }
484
485     /**
486      * <p>
487      *
488      * Print debug message (if the debug level is high enough).</p>
489      *
490      * @param level The debug level of this message. This message will only be
491      * displayed if the current debug level is at least equal to this
492      * value.
493      * @param message The text of the message.
494      * @param token The catalog file token being processed.
495      * @param spec The argument to the token.
496      */

497     private void debug( int level, String JavaDoc message, String JavaDoc token, String JavaDoc spec )
498     {
499         if( debug >= level )
500         {
501             System.out.println( message + ": " + token + " " + spec );
502         }
503     }
504
505     /**
506      * <p>Print debug message (if the debug level is high enough).</p>
507      *
508      * @param level The debug level of this message. This message will only be
509      * displayed if the current debug level is at least equal to this
510      * value.
511      * @param message The text of the message.
512      * @param token The catalog file token being processed.
513      * @param spec1 The first argument to the token.
514      * @param spec2 The second argument to the token.
515      */

516     private void debug( int level, String JavaDoc message,
517                         String JavaDoc token, String JavaDoc spec1, String JavaDoc spec2 )
518     {
519         if( debug >= level )
520         {
521             System.out.println( message + ": " + token + " " + spec1 );
522             System.out.println( "\t" + spec2 );
523         }
524     }
525 }
526
Popular Tags