Statistics
| Revision:

root / import / ext3D / trunk / install-extension3d / IzPack / src / lib / net / n3 / nanoxml / StdXMLReader.java @ 15280

History | View | Annotate | Download (17.3 KB)

1
/* StdXMLReader.java                                               NanoXML/Java
2
 *
3
 * $Revision: 1.1 $
4
 * $Date: 2006/06/14 07:29:07 $
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

    
32
import java.io.FileInputStream;
33
import java.io.FileNotFoundException;
34
import java.io.IOException;
35
import java.io.InputStream;
36
import java.io.InputStreamReader;
37
import java.io.LineNumberReader;
38
import java.io.PushbackInputStream;
39
import java.io.PushbackReader;
40
import java.io.Reader;
41
import java.io.StringReader;
42
import java.io.UnsupportedEncodingException;
43
import java.net.MalformedURLException;
44
import java.net.URL;
45
import java.util.Stack;
46

    
47

    
48
/**
49
 * StdXMLReader reads the data to be parsed.
50
 *
51
 * @author Marc De Scheemaecker
52
 * @version $Name:  $, $Revision: 1.1 $
53
 */
54
public class StdXMLReader
55
    implements IXMLReader
56
{
57

    
58
    /**
59
     * The stack of push-back readers.
60
     */
61
    private Stack pbreaders;
62
    
63
    
64
    /**
65
     * The stack of line-number readers.
66
     */
67
    private Stack linereaders;
68
    
69
    
70
    /**
71
     * The stack of system ids.
72
     */
73
    private Stack systemIds;
74
    
75
    
76
    /**
77
     * The stack of public ids.
78
     */
79
    private Stack publicIds;
80
    
81
    
82
    /**
83
     * The current push-back reader.
84
     */
85
    private PushbackReader currentPbReader;
86
    
87
    
88
    /**
89
     * The current line-number reader.
90
     */
91
    private LineNumberReader currentLineReader;
92
    
93
    
94
    /**
95
     * The current system ID.
96
     */
97
    private URL currentSystemID;
98
    
99
    
100
    /**
101
     * The current public ID.
102
     */
103
    private String currentPublicID;
104
    
105
    
106
    /**
107
     * Creates a new reader using a string as input.
108
     *
109
     * @param str the string containing the XML data
110
     */
111
    public static IXMLReader stringReader(String str)
112
    {
113
        return new StdXMLReader(new StringReader(str));
114
    }
115
    
116
    
117
    /**
118
     * Creates a new reader using a file as input.
119
     *
120
     * @param filename the name of the file containing the XML data
121
     *
122
     * @throws java.io.FileNotFoundException
123
     *     if the file could not be found
124
     * @throws java.io.IOException
125
     *     if an I/O error occurred
126
     */
127
    public static IXMLReader fileReader(String filename)
128
        throws FileNotFoundException,
129
               IOException
130
    {
131
        IXMLReader reader = new StdXMLReader(new FileInputStream(filename));
132
        reader.setSystemID(filename);
133
        return reader;
134
    }
135
    
136
    
137
    /**
138
     * Initializes the reader from a system and public ID.
139
     *
140
     * @param publicID the public ID which may be null.
141
     * @param systemID the non-null system ID.
142
     *
143
     * @throws MalformedURLException
144
     *     if the system ID does not contain a valid URL
145
     * @throws FileNotFoundException
146
     *     if the system ID refers to a local file which does not exist
147
     * @throws IOException
148
     *     if an error occurred opening the stream
149
     */
150
    public StdXMLReader(String publicID,
151
                        String systemID)
152
        throws MalformedURLException,
153
               FileNotFoundException,
154
               IOException
155
    {
156
        URL systemIDasURL = null;
157
        
158
        try {
159
            systemIDasURL = new URL(systemID);
160
        } catch (MalformedURLException e) {
161
            systemID = "file:" + systemID;
162
            
163
            try {
164
                systemIDasURL = new URL(systemID);
165
            } catch (MalformedURLException e2) {
166
                throw e;
167
            }
168
        }
169
            
170
        Reader reader = this.openStream(publicID, systemIDasURL.toString());
171
        this.currentLineReader = new LineNumberReader(reader);
172
        this.currentPbReader = new PushbackReader(this.currentLineReader, 2);
173
        this.pbreaders = new Stack();
174
        this.linereaders = new Stack();
175
        this.publicIds = new Stack();
176
        this.systemIds = new Stack();
177
        this.currentPublicID = publicID;
178
        this.currentSystemID = systemIDasURL;
179
    }
180

    
181

    
182
    /**
183
     * Initializes the XML reader.
184
     *
185
     * @param reader the input for the XML data.
186
     */
187
    public StdXMLReader(Reader reader)
188
    {
189
        this.currentLineReader = new LineNumberReader(reader);
190
        this.currentPbReader = new PushbackReader(this.currentLineReader, 2);
191
        this.pbreaders = new Stack();
192
        this.linereaders = new Stack();
193
        this.publicIds = new Stack();
194
        this.systemIds = new Stack();
195
        this.currentPublicID = "";
196
        
197
        try {
198
            this.currentSystemID = new URL("file:.");
199
        } catch (MalformedURLException e) {
200
            // never happens
201
        }
202
    }
203
    
204
    
205
    /**
206
     * Cleans up the object when it's destroyed.
207
     */
208
    protected void finalize()
209
        throws Throwable
210
    {
211
        this.currentLineReader = null;
212
        this.currentPbReader = null;
213
        this.pbreaders.clear();
214
        this.pbreaders = null;
215
        this.linereaders.clear();
216
        this.linereaders = null;
217
        this.publicIds.clear();
218
        this.publicIds = null;
219
        this.systemIds.clear();
220
        this.systemIds = null;
221
        this.currentPublicID = null;
222
        super.finalize();
223
    }
224
    
225
    
226
    /**
227
     * Scans the encoding from an <?xml?> tag.
228
     *
229
     * @param str the first tag in the XML data.
230
     *
231
     * @return the encoding, or null if no encoding has been specified.
232
     */
233
    protected String getEncoding(String str)
234
    {
235
        if (! str.startsWith("<?xml")) {
236
            return null;
237
        }
238
        
239
        int index = 5;
240
        
241
        while (index < str.length()) {
242
            StringBuffer key = new StringBuffer();
243
            
244
            while ((index < str.length()) && (str.charAt(index) <= ' ')) {
245
                index++;
246
            }
247
        
248
            while ((index < str.length())
249
                   && (str.charAt(index) >= 'a')
250
                   && (str.charAt(index) <= 'z')) {
251
                key.append(str.charAt(index));
252
                index++;
253
            }
254
        
255
            while ((index < str.length()) && (str.charAt(index) <= ' ')) {
256
                index++;
257
            }
258
        
259
            if ((index >= str.length()) || (str.charAt(index) != '=')) {
260
                break;
261
            }
262
        
263
            while ((index < str.length()) && (str.charAt(index) != '\'')
264
                   && (str.charAt(index) != '"')) {
265
                index++;
266
            }
267
        
268
            if (index >= str.length()) {
269
                break;
270
            }
271
        
272
            char delimiter = str.charAt(index);
273
            index++;
274
            int index2 = str.indexOf(delimiter, index);
275
        
276
            if (index2 < 0) {
277
                break;
278
            }
279
            
280
            if (key.toString().equals("encoding")) {
281
                return str.substring(index, index2);
282
            }
283
            
284
            index = index2 + 1;
285
        }
286
        
287
        return null;
288
    }
289

    
290

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

    
354

    
355
    /**
356
     * Initializes the XML reader.
357
     *
358
     * @param stream the input for the XML data.
359
     *
360
     * @throws java.io.IOException
361
     *                if an I/O error occurred
362
     */
363
    public StdXMLReader(InputStream stream)
364
        throws IOException
365
    {
366
        PushbackInputStream pbstream = new PushbackInputStream(stream);
367
        StringBuffer charsRead = new StringBuffer();
368
        Reader reader = this.stream2reader(stream, charsRead);
369
        this.currentLineReader = new LineNumberReader(reader);
370
        this.currentPbReader = new PushbackReader(this.currentLineReader, 2);
371
        this.pbreaders = new Stack();
372
        this.linereaders = new Stack();
373
        this.publicIds = new Stack();
374
        this.systemIds = new Stack();
375
        this.currentPublicID = "";
376
        
377
        try {
378
            this.currentSystemID = new URL("file:.");
379
        } catch (MalformedURLException e) {
380
            // never happens
381
        }
382
        
383
        this.startNewStream(new StringReader(charsRead.toString()));
384
    }
385
    
386
    
387
    /**
388
     * Reads a character.
389
     *
390
     * @return the character
391
     *
392
     * @throws java.io.IOException
393
     *                if no character could be read
394
     */
395
    public char read()
396
        throws IOException
397
    {
398
        int ch = this.currentPbReader.read();
399
        
400
        while (ch < 0) {
401
            if (this.pbreaders.empty()) {
402
                throw new IOException("Unexpected EOF");
403
            }
404
            
405
            this.currentPbReader.close();
406
            this.currentPbReader = (PushbackReader) this.pbreaders.pop();
407
            this.currentLineReader = (LineNumberReader) this.linereaders.pop();
408
            this.currentSystemID = (URL) this.systemIds.pop();
409
            this.currentPublicID = (String) this.publicIds.pop();
410
            ch = this.currentPbReader.read();
411
        }
412
        
413
        if (ch == 0x0D) { // CR
414
            // using recursion could convert "\r\r\n" to "\n" (wrong),
415
            // newline combo "\r\n" isn't normalized if it spans streams
416
            // next 'read()' will pop pbreaders stack appropriately
417
            ch = this.currentPbReader.read();
418

    
419
            if (ch != 0x0A && ch > 0) { // LF
420
                this.currentPbReader.unread(ch);
421
            }
422
            return (char) 0x0A; // normalized: always LF
423
        }
424

    
425
        return (char) ch;
426
    }
427
        
428
    
429
    /**
430
     * Returns true if the current stream has no more characters left to be
431
     * read.
432
     *
433
     * @throws java.io.IOException
434
     *                if an I/O error occurred
435
     */
436
    public boolean atEOFOfCurrentStream()
437
        throws IOException
438
    {
439
        int ch = this.currentPbReader.read();
440
        
441
        if (ch < 0) {
442
            return true;
443
        } else {
444
            this.currentPbReader.unread(ch);
445
            return false;
446
        }
447
    }
448
    
449
    
450
    /**
451
     * Returns true if there are no more characters left to be read.
452
     *
453
     * @throws java.io.IOException
454
     *                if an I/O error occurred
455
     */
456
    public boolean atEOF()
457
        throws IOException
458
    {
459
        int ch = this.currentPbReader.read();
460
        
461
        while (ch < 0) {
462
            if (this.pbreaders.empty()) {
463
                return true;
464
            }
465
            
466
            this.currentPbReader.close();
467
            this.currentPbReader = (PushbackReader) this.pbreaders.pop();
468
            this.currentLineReader = (LineNumberReader) this.linereaders.pop();
469
            this.currentSystemID = (URL) this.systemIds.pop();
470
            this.currentPublicID = (String) this.publicIds.pop();
471
            ch = this.currentPbReader.read();
472
        }
473

    
474
        this.currentPbReader.unread(ch);
475
        return false;
476
    }
477
        
478
    
479
    /**
480
     * Pushes the last character read back to the stream.
481
     *
482
     * @throws java.io.IOException
483
     *     if an I/O error occurred
484
     */
485
    public void unread(char ch)
486
        throws IOException
487
    {
488
        this.currentPbReader.unread(ch);
489
    }
490

    
491

    
492
    /**
493
     * Opens a stream from a public and system ID.
494
     *
495
     * @param publicID the public ID, which may be null
496
     * @param systemID the system ID, which is never null
497
     *
498
     * @throws java.net.MalformedURLException
499
     *     if the system ID does not contain a valid URL
500
     * @throws java.io.FileNotFoundException
501
     *     if the system ID refers to a local file which does not exist
502
     * @throws java.io.IOException
503
     *     if an error occurred opening the stream
504
     */
505
    public Reader openStream(String publicID,
506
                             String systemID)
507
        throws MalformedURLException,
508
               FileNotFoundException,
509
               IOException
510
    {
511
        URL url = new URL(this.currentSystemID, systemID);
512
        StringBuffer charsRead = new StringBuffer();
513
        Reader reader = this.stream2reader(url.openStream(), charsRead);
514
        
515
        if (charsRead.length() == 0) {
516
            return reader;
517
        }
518
        
519
        String charsReadStr = charsRead.toString();
520
        PushbackReader pbreader = new PushbackReader(reader,
521
                                                     charsReadStr.length());
522
        for (int i = charsReadStr.length() - 1; i >= 0; i--) {
523
            pbreader.unread(charsReadStr.charAt(i));
524
        }
525
        
526
        return pbreader;
527
    }
528
    
529
    
530
    /**
531
     * Starts a new stream from a Java reader. The new stream is used
532
     * temporary to read data from. If that stream is exhausted, control
533
     * returns to the parent stream.
534
     *
535
     * @param reader the non-null reader to read the new data from
536
     */
537
    public void startNewStream(Reader reader)
538
    {
539
        this.pbreaders.push(this.currentPbReader);
540
        this.linereaders.push(this.currentLineReader);
541
        this.systemIds.push(this.currentSystemID);
542
        this.publicIds.push(this.currentPublicID);
543
        this.currentLineReader = new LineNumberReader(reader);
544
        this.currentPbReader = new PushbackReader(this.currentLineReader, 2);
545
    }
546
    
547
    
548
    /**
549
     * Returns the line number of the data in the current stream.
550
     */
551
    public int getLineNr()
552
    {
553
        return this.currentLineReader.getLineNumber() + 1;
554
    }
555
    
556
    
557
    /**
558
     * Sets the system ID of the current stream.
559
     *
560
     * @param systemID the system ID
561
     *
562
     * @throws java.net.MalformedURLException
563
     *     if the system ID does not contain a valid URL
564
     */
565
    public void setSystemID(String systemID)
566
        throws MalformedURLException
567
    {
568
        this.currentSystemID = new URL(this.currentSystemID, systemID);
569
    }
570
    
571
    
572
    /**
573
     * Sets the public ID of the current stream.
574
     *
575
     * @param publicID the public ID
576
     */
577
    public void setPublicID(String publicID)
578
    {
579
        this.currentPublicID = publicID;
580
    }
581
    
582
    
583
    /**
584
     * Returns the current system ID.
585
     */
586
    public String getSystemID()
587
    {
588
        return this.currentSystemID.toString();
589
    }
590
    
591
    
592
    /**
593
     * Returns the current public ID.
594
     */
595
    public String getPublicID()
596
    {
597
        return this.currentPublicID;
598
    }
599

    
600
}