KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > n3 > nanoxml > StdXMLReader


1 /* StdXMLReader.java NanoXML/Java
2  *
3  * $Revision: 1422 $
4  * $Date: 2006-03-21 19:14:56 +0100 (Tue, 21 Mar 2006) $
5  * $Name$
6  *
7  * This file is part of NanoXML 2 for Java.
8  * Copyright (C) 2001 Marc De Scheemaecker, All Rights Reserved.
9  *
10  * This software is provided 'as-is', without any express or implied warranty.
11  * In no event will the authors be held liable for any damages arising from the
12  * use of this software.
13  *
14  * Permission is granted to anyone to use this software for any purpose,
15  * including commercial applications, and to alter it and redistribute it
16  * freely, subject to the following restrictions:
17  *
18  * 1. The origin of this software must not be misrepresented; you must not
19  * claim that you wrote the original software. If you use this software in
20  * a product, an acknowledgment in the product documentation would be
21  * appreciated but is not required.
22  *
23  * 2. Altered source versions must be plainly marked as such, and must not be
24  * misrepresented as being the original software.
25  *
26  * 3. This notice may not be removed or altered from any source distribution.
27  */

28
29 package net.n3.nanoxml;
30
31 import java.io.FileInputStream JavaDoc;
32 import java.io.FileNotFoundException JavaDoc;
33 import java.io.IOException JavaDoc;
34 import java.io.InputStream JavaDoc;
35 import java.io.InputStreamReader JavaDoc;
36 import java.io.LineNumberReader JavaDoc;
37 import java.io.PushbackInputStream JavaDoc;
38 import java.io.PushbackReader JavaDoc;
39 import java.io.Reader JavaDoc;
40 import java.io.StringReader JavaDoc;
41 import java.io.UnsupportedEncodingException JavaDoc;
42 import java.net.MalformedURLException JavaDoc;
43 import java.net.URL JavaDoc;
44 import java.util.Stack JavaDoc;
45
46 /**
47  * StdXMLReader reads the data to be parsed.
48  *
49  * @author Marc De Scheemaecker
50  * @version $Name$, $Revision: 1422 $
51  */

52 public class StdXMLReader implements IXMLReader
53 {
54
55     /**
56      * The stack of push-back readers.
57      */

58     private Stack JavaDoc pbreaders;
59
60     /**
61      * The stack of line-number readers.
62      */

63     private Stack JavaDoc linereaders;
64
65     /**
66      * The stack of system ids.
67      */

68     private Stack JavaDoc systemIds;
69
70     /**
71      * The stack of public ids.
72      */

73     private Stack JavaDoc publicIds;
74
75     /**
76      * The current push-back reader.
77      */

78     private PushbackReader JavaDoc currentPbReader;
79
80     /**
81      * The current line-number reader.
82      */

83     private LineNumberReader JavaDoc currentLineReader;
84
85     /**
86      * The current system ID.
87      */

88     private URL JavaDoc currentSystemID;
89
90     /**
91      * The current public ID.
92      */

93     private String JavaDoc currentPublicID;
94
95     /**
96      * Creates a new reader using a string as input.
97      *
98      * @param str the string containing the XML data
99      */

100     public static IXMLReader stringReader(String JavaDoc str)
101     {
102         return new StdXMLReader(new StringReader JavaDoc(str));
103     }
104
105     /**
106      * Creates a new reader using a file as input.
107      *
108      * @param filename the name of the file containing the XML data
109      *
110      * @throws java.io.FileNotFoundException if the file could not be found
111      * @throws java.io.IOException if an I/O error occurred
112      */

113     public static IXMLReader fileReader(String JavaDoc filename) throws FileNotFoundException JavaDoc, IOException JavaDoc
114     {
115         IXMLReader reader = new StdXMLReader(new FileInputStream JavaDoc(filename));
116         reader.setSystemID(filename);
117         return reader;
118     }
119
120     /**
121      * Initializes the reader from a system and public ID.
122      *
123      * @param publicID the public ID which may be null.
124      * @param systemID the non-null system ID.
125      *
126      * @throws MalformedURLException if the system ID does not contain a valid URL
127      * @throws FileNotFoundException if the system ID refers to a local file which does not exist
128      * @throws IOException if an error occurred opening the stream
129      */

130     public StdXMLReader(String JavaDoc publicID, String JavaDoc systemID) throws MalformedURLException JavaDoc,
131             FileNotFoundException JavaDoc, IOException JavaDoc
132     {
133         URL JavaDoc systemIDasURL = null;
134
135         try
136         {
137             systemIDasURL = new URL JavaDoc(systemID);
138         }
139         catch (MalformedURLException JavaDoc e)
140         {
141             systemID = "file:" + systemID;
142
143             try
144             {
145                 systemIDasURL = new URL JavaDoc(systemID);
146             }
147             catch (MalformedURLException JavaDoc e2)
148             {
149                 throw e;
150             }
151         }
152
153         Reader JavaDoc reader = this.openStream(publicID, systemIDasURL.toString());
154         this.currentLineReader = new LineNumberReader JavaDoc(reader);
155         this.currentPbReader = new PushbackReader JavaDoc(this.currentLineReader, 2);
156         this.pbreaders = new Stack JavaDoc();
157         this.linereaders = new Stack JavaDoc();
158         this.publicIds = new Stack JavaDoc();
159         this.systemIds = new Stack JavaDoc();
160         this.currentPublicID = publicID;
161         this.currentSystemID = systemIDasURL;
162     }
163
164     /**
165      * Initializes the XML reader.
166      *
167      * @param reader the input for the XML data.
168      */

169     public StdXMLReader(Reader JavaDoc reader)
170     {
171         this.currentLineReader = new LineNumberReader JavaDoc(reader);
172         this.currentPbReader = new PushbackReader JavaDoc(this.currentLineReader, 2);
173         this.pbreaders = new Stack JavaDoc();
174         this.linereaders = new Stack JavaDoc();
175         this.publicIds = new Stack JavaDoc();
176         this.systemIds = new Stack JavaDoc();
177         this.currentPublicID = "";
178
179         try
180         {
181             this.currentSystemID = new URL JavaDoc("file:.");
182         }
183         catch (MalformedURLException JavaDoc e)
184         {
185             // never happens
186
}
187     }
188
189     /**
190      * Cleans up the object when it's destroyed.
191      */

192     protected void finalize() throws Throwable JavaDoc
193     {
194         this.currentLineReader = null;
195         this.currentPbReader = null;
196         this.pbreaders.clear();
197         this.pbreaders = null;
198         this.linereaders.clear();
199         this.linereaders = null;
200         this.publicIds.clear();
201         this.publicIds = null;
202         this.systemIds.clear();
203         this.systemIds = null;
204         this.currentPublicID = null;
205         super.finalize();
206     }
207
208     /**
209      * Scans the encoding from an <?xml?> tag.
210      *
211      * @param str the first tag in the XML data.
212      *
213      * @return the encoding, or null if no encoding has been specified.
214      */

215     protected String JavaDoc getEncoding(String JavaDoc str)
216     {
217         if (!str.startsWith("<?xml")) { return null; }
218
219         int index = 5;
220
221         while (index < str.length())
222         {
223             StringBuffer JavaDoc key = new StringBuffer JavaDoc();
224
225             while ((index < str.length()) && (str.charAt(index) <= ' '))
226             {
227                 index++;
228             }
229
230             while ((index < str.length()) && (str.charAt(index) >= 'a')
231                     && (str.charAt(index) <= 'z'))
232             {
233                 key.append(str.charAt(index));
234                 index++;
235             }
236
237             while ((index < str.length()) && (str.charAt(index) <= ' '))
238             {
239                 index++;
240             }
241
242             if ((index >= str.length()) || (str.charAt(index) != '='))
243             {
244                 break;
245             }
246
247             while ((index < str.length()) && (str.charAt(index) != '\'')
248                     && (str.charAt(index) != '"'))
249             {
250                 index++;
251             }
252
253             if (index >= str.length())
254             {
255                 break;
256             }
257
258             char delimiter = str.charAt(index);
259             index++;
260             int index2 = str.indexOf(delimiter, index);
261
262             if (index2 < 0)
263             {
264                 break;
265             }
266
267             if ("encoding".equals(key.toString())) { return str.substring(index, index2); }
268
269             index = index2 + 1;
270         }
271
272         return null;
273     }
274
275     /**
276      * Converts a stream to a reader while detecting the encoding.
277      *
278      * @param stream the input for the XML data.
279      * @param charsRead buffer where to put characters that have been read
280      *
281      * @throws java.io.IOException if an I/O error occurred
282      */

283     protected Reader JavaDoc stream2reader(InputStream JavaDoc stream, StringBuffer JavaDoc charsRead) throws IOException JavaDoc
284     {
285         PushbackInputStream JavaDoc pbstream = new PushbackInputStream JavaDoc(stream);
286         int b = pbstream.read();
287
288         switch (b)
289         {
290         case 0x00:
291         case 0xFE:
292         case 0xFF:
293             pbstream.unread(b);
294             return new InputStreamReader JavaDoc(pbstream, "UTF-16");
295
296         case 0xEF:
297             for (int i = 0; i < 2; i++)
298             {
299                 pbstream.read();
300             }
301
302             return new InputStreamReader JavaDoc(pbstream, "UTF-8");
303
304         case 0x3C:
305             b = pbstream.read();
306             charsRead.append('<');
307
308             while ((b > 0) && (b != 0x3E))
309             {
310                 charsRead.append((char) b);
311                 b = pbstream.read();
312             }
313
314             if (b > 0)
315             {
316                 charsRead.append((char) b);
317             }
318
319             String JavaDoc encoding = this.getEncoding(charsRead.toString());
320
321             if (encoding == null) { return new InputStreamReader JavaDoc(pbstream, "UTF-8"); }
322
323             charsRead.setLength(0);
324
325             try
326             {
327                 return new InputStreamReader JavaDoc(pbstream, encoding);
328             }
329             catch (UnsupportedEncodingException JavaDoc e)
330             {
331                 return new InputStreamReader JavaDoc(pbstream, "UTF-8");
332             }
333
334         default:
335             charsRead.append((char) b);
336             return new InputStreamReader JavaDoc(pbstream, "UTF-8");
337         }
338     }
339
340     /**
341      * Initializes the XML reader.
342      *
343      * @param stream the input for the XML data.
344      *
345      * @throws java.io.IOException if an I/O error occurred
346      */

347     public StdXMLReader(InputStream JavaDoc stream) throws IOException JavaDoc
348     {
349         StringBuffer JavaDoc charsRead = new StringBuffer JavaDoc();
350         Reader JavaDoc reader = this.stream2reader(stream, charsRead);
351         this.currentLineReader = new LineNumberReader JavaDoc(reader);
352         this.currentPbReader = new PushbackReader JavaDoc(this.currentLineReader, 2);
353         this.pbreaders = new Stack JavaDoc();
354         this.linereaders = new Stack JavaDoc();
355         this.publicIds = new Stack JavaDoc();
356         this.systemIds = new Stack JavaDoc();
357         this.currentPublicID = "";
358
359         try
360         {
361             this.currentSystemID = new URL JavaDoc("file:.");
362         }
363         catch (MalformedURLException JavaDoc e)
364         {
365             // never happens
366
}
367
368         this.startNewStream(new StringReader JavaDoc(charsRead.toString()));
369     }
370
371     /**
372      * Reads a character.
373      *
374      * @return the character
375      *
376      * @throws java.io.IOException if no character could be read
377      */

378     public char read() throws IOException JavaDoc
379     {
380         int ch = this.currentPbReader.read();
381
382         while (ch < 0)
383         {
384             if (this.pbreaders.empty()) { throw new IOException JavaDoc("Unexpected EOF"); }
385
386             this.currentPbReader.close();
387             this.currentPbReader = (PushbackReader JavaDoc) this.pbreaders.pop();
388             this.currentLineReader = (LineNumberReader JavaDoc) this.linereaders.pop();
389             this.currentSystemID = (URL JavaDoc) this.systemIds.pop();
390             this.currentPublicID = (String JavaDoc) this.publicIds.pop();
391             ch = this.currentPbReader.read();
392         }
393
394         if (ch == 0x0D)
395         { // CR
396
// using recursion could convert "\r\r\n" to "\n" (wrong),
397
// newline combo "\r\n" isn't normalized if it spans streams
398
// next 'read()' will pop pbreaders stack appropriately
399
ch = this.currentPbReader.read();
400
401             if (ch != 0x0A && ch > 0)
402             { // LF
403
this.currentPbReader.unread(ch);
404             }
405             return (char) 0x0A; // normalized: always LF
406
}
407
408         return (char) ch;
409     }
410
411     /**
412      * Returns true if the current stream has no more characters left to be read.
413      *
414      * @throws java.io.IOException if an I/O error occurred
415      */

416     public boolean atEOFOfCurrentStream() throws IOException JavaDoc
417     {
418         int ch = this.currentPbReader.read();
419
420         if (ch < 0)
421         {
422             return true;
423         }
424         else
425         {
426             this.currentPbReader.unread(ch);
427             return false;
428         }
429     }
430
431     /**
432      * Returns true if there are no more characters left to be read.
433      *
434      * @throws java.io.IOException if an I/O error occurred
435      */

436     public boolean atEOF() throws IOException JavaDoc
437     {
438         int ch = this.currentPbReader.read();
439
440         while (ch < 0)
441         {
442             if (this.pbreaders.empty()) { return true; }
443
444             this.currentPbReader.close();
445             this.currentPbReader = (PushbackReader JavaDoc) this.pbreaders.pop();
446             this.currentLineReader = (LineNumberReader JavaDoc) this.linereaders.pop();
447             this.currentSystemID = (URL JavaDoc) this.systemIds.pop();
448             this.currentPublicID = (String JavaDoc) this.publicIds.pop();
449             ch = this.currentPbReader.read();
450         }
451
452         this.currentPbReader.unread(ch);
453         return false;
454     }
455
456     /**
457      * Pushes the last character read back to the stream.
458      *
459      * @throws java.io.IOException if an I/O error occurred
460      */

461     public void unread(char ch) throws IOException JavaDoc
462     {
463         this.currentPbReader.unread(ch);
464     }
465
466     /**
467      * Opens a stream from a public and system ID.
468      *
469      * @param publicID the public ID, which may be null
470      * @param systemID the system ID, which is never null
471      *
472      * @throws java.net.MalformedURLException if the system ID does not contain a valid URL
473      * @throws java.io.FileNotFoundException if the system ID refers to a local file which does not
474      * exist
475      * @throws java.io.IOException if an error occurred opening the stream
476      */

477     public Reader JavaDoc openStream(String JavaDoc publicID, String JavaDoc systemID) throws MalformedURLException JavaDoc,
478             FileNotFoundException JavaDoc, IOException JavaDoc
479     {
480         URL JavaDoc url = new URL JavaDoc(this.currentSystemID, systemID);
481         StringBuffer JavaDoc charsRead = new StringBuffer JavaDoc();
482         Reader JavaDoc reader = this.stream2reader(url.openStream(), charsRead);
483
484         if (charsRead.length() == 0) { return reader; }
485
486         String JavaDoc charsReadStr = charsRead.toString();
487         PushbackReader JavaDoc pbreader = new PushbackReader JavaDoc(reader, charsReadStr.length());
488         for (int i = charsReadStr.length() - 1; i >= 0; i--)
489         {
490             pbreader.unread(charsReadStr.charAt(i));
491         }
492
493         return pbreader;
494     }
495
496     /**
497      * Starts a new stream from a Java reader. The new stream is used temporary to read data from.
498      * If that stream is exhausted, control returns to the parent stream.
499      *
500      * @param reader the non-null reader to read the new data from
501      */

502     public void startNewStream(Reader JavaDoc reader)
503     {
504         this.pbreaders.push(this.currentPbReader);
505         this.linereaders.push(this.currentLineReader);
506         this.systemIds.push(this.currentSystemID);
507         this.publicIds.push(this.currentPublicID);
508         this.currentLineReader = new LineNumberReader JavaDoc(reader);
509         this.currentPbReader = new PushbackReader JavaDoc(this.currentLineReader, 2);
510     }
511
512     /**
513      * Returns the line number of the data in the current stream.
514      */

515     public int getLineNr()
516     {
517         return this.currentLineReader.getLineNumber() + 1;
518     }
519
520     /**
521      * Sets the system ID of the current stream.
522      *
523      * @param systemID the system ID
524      *
525      * @throws java.net.MalformedURLException if the system ID does not contain a valid URL
526      */

527     public void setSystemID(String JavaDoc systemID) throws MalformedURLException JavaDoc
528     {
529         this.currentSystemID = new URL JavaDoc(this.currentSystemID, systemID);
530     }
531
532     /**
533      * Sets the public ID of the current stream.
534      *
535      * @param publicID the public ID
536      */

537     public void setPublicID(String JavaDoc publicID)
538     {
539         this.currentPublicID = publicID;
540     }
541
542     /**
543      * Returns the current system ID.
544      */

545     public String JavaDoc getSystemID()
546     {
547         return this.currentSystemID.toString();
548     }
549
550     /**
551      * Returns the current public ID.
552      */

553     public String JavaDoc getPublicID()
554     {
555         return this.currentPublicID;
556     }
557
558 }
559
Popular Tags