Statistics
| Revision:

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

History | View | Annotate | Download (19.5 KB)

1
/* StdXMLParser.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.Reader;
33
import java.util.Enumeration;
34
import java.util.Properties;
35

    
36

    
37
/**
38
 * StdXMLParser is the core parser of NanoXML.
39
 *
40
 * @author Marc De Scheemaecker
41
 * @version $Name:  $, $Revision: 1.1 $
42
 */
43
public class StdXMLParser
44
    implements IXMLParser
45
{
46

    
47
    /**
48
     * Delimiter for a processing instructions.
49
     */
50
    private static final char[] END_OF_PI = { '>', '?' };
51
    
52
    
53
    /**
54
     * Delimiter for CDATA sections.
55
     */
56
    private static final char[] END_OF_CDATA = { '>', ']', ']' };
57
    
58
    
59
    /**
60
     * Delimiter for PCDATA elements.
61
     */
62
    private static final char[] END_OF_PCDATA = { '<' };
63
    
64
    
65
    /**
66
     * The builder which creates the logical structure of the XML data.
67
     */
68
    private IXMLBuilder builder;
69
    
70
    
71
    /**
72
     * The reader from which the parser retrieves its data.
73
     */
74
    private IXMLReader reader;
75
    
76
    
77
    /**
78
     * The entity resolver.
79
     */
80
    private IXMLEntityResolver entityResolver;
81
    
82
    
83
    /**
84
     * The validator that will process entity references and validate the XML
85
     * data.
86
     */
87
    private IXMLValidator validator;
88
    
89
    
90
    /**
91
     * Creates a new parser.
92
     */
93
    public StdXMLParser()
94
    {
95
        this.builder = null;
96
        this.validator = null;
97
        this.reader = null;
98
        this.entityResolver = new XMLEntityResolver();
99
    }
100
    
101
    
102
    /**
103
     * Cleans up the object when it's destroyed.
104
     */
105
    protected void finalize()
106
        throws Throwable
107
    {
108
        this.builder = null;
109
        this.reader = null;
110
        this.entityResolver = null;
111
        this.validator = null;
112
        super.finalize();
113
    }
114
    
115
    
116
    /**
117
     * Sets the builder which creates the logical structure of the XML data.
118
     *
119
     * @param builder the non-null builder
120
     */
121
    public void setBuilder(IXMLBuilder builder)
122
    {
123
        this.builder = builder;
124
    }
125
    
126
    
127
    /**
128
     * Returns the builder which creates the logical structure of the XML data.
129
     *
130
     * @return the builder
131
     */
132
    public IXMLBuilder getBuilder()
133
    {
134
        return this.builder;
135
    }
136
    
137
    
138
    /**
139
     * Sets the validator that validates the XML data.
140
     *
141
     * @param validator the non-null validator
142
     */
143
    public void setValidator(IXMLValidator validator)
144
    {
145
        this.validator = validator;
146
    }
147
    
148
    
149
    /**
150
     * Returns the validator that validates the XML data.
151
     *
152
     * @return the validator
153
     */
154
    public IXMLValidator getValidator()
155
    {
156
        return this.validator;
157
    }
158
    
159
    
160
    /**
161
     * Sets the entity resolver.
162
     *
163
     * @param resolver the non-null resolver
164
     */
165
    public void setResolver(IXMLEntityResolver resolver)
166
    {
167
        this.entityResolver = resolver;
168
    }
169
    
170
    
171
    /**
172
     * Returns the entity resolver.
173
     *
174
     * @return the non-null resolver
175
     */
176
    public IXMLEntityResolver getResolver()
177
    {
178
        return this.entityResolver;
179
    }
180
    
181
    
182
    /**
183
     * Sets the reader from which the parser retrieves its data.
184
     *
185
     * @param reader the reader
186
     */
187
    public void setReader(IXMLReader reader)
188
    {
189
        this.reader = reader;
190
    }
191
    
192
    
193
    /**
194
     * Returns the reader from which the parser retrieves its data.
195
     *
196
     * @return the reader
197
     */
198
    public IXMLReader getReader()
199
    {
200
        return this.reader;
201
    }
202
    
203
    
204
    /**
205
     * Parses the data and lets the builder create the logical data structure.
206
     *
207
     * @return the logical structure built by the builder
208
     *
209
     * @throws net.n3.nanoxml.XMLException
210
     *                if an error occurred reading or parsing the data
211
     */
212
    public Object parse()
213
        throws XMLException
214
    {
215
        try {
216
            this.builder.startBuilding(this.reader.getSystemID(),
217
                                       this.reader.getLineNr());
218
            this.scanData();
219
            return this.builder.getResult();
220
        } catch (XMLException e) {
221
            throw e;
222
        } catch (Exception e) {
223
            throw new XMLException(e);
224
        }
225
    }
226
    
227
    
228
    /**
229
     * Scans the XML data for elements.
230
     *
231
     * @throws java.lang.Exception
232
     *     if something went wrong
233
     */
234
    protected void scanData()
235
        throws Exception
236
    {
237
        while ((! this.reader.atEOF()) && (this.builder.getResult() == null)) {
238
            char ch = XMLUtil.read(this.reader, null, '&',
239
                                   this.entityResolver);
240
            
241
            switch (ch) {
242
                case '<':
243
                    this.scanSomeTag(false /*don't allow CDATA*/);
244
                    break;
245
                    
246
                case ' ':
247
                case '\t':
248
                case '\r':
249
                case '\n':
250
                    // skip whitespace
251
                    break;
252
                    
253
                default:
254
                    XMLUtil.errorInvalidInput(reader.getSystemID(),
255
                                              reader.getLineNr(),
256
                                              "`" + ch + "' (0x"
257
                                              + Integer.toHexString((int) ch)
258
                                              + ')');
259
            }
260
        }
261
    }
262
    
263
    
264
    /**
265
     * Scans an XML tag.
266
     *
267
     * @param allowCDATA true if CDATA sections are allowed at this point
268
     *
269
     * @throws java.lang.Exception
270
     *     if something went wrong
271
     */
272
    protected void scanSomeTag(boolean allowCDATA)
273
        throws Exception
274
    {
275
        char ch = XMLUtil.read(this.reader, null, '&', this.entityResolver);
276
        
277
        switch (ch) {
278
            case '?':
279
                this.processPI();
280
                break;
281
                
282
            case '!':
283
                this.processSpecialTag(allowCDATA);
284
                break;
285
                
286
            default:
287
                this.reader.unread(ch);
288
                this.processElement();
289
        }
290
    }
291
   
292
    
293
    /**
294
     * Processes a "processing instruction".
295
     *
296
     * @throws java.lang.Exception
297
     *     if something went wrong
298
     */
299
    protected void processPI()
300
        throws Exception
301
    {
302
        XMLUtil.skipWhitespace(this.reader, '&', null, null);
303
        String target = XMLUtil.scanIdentifier(this.reader, '&',
304
                                               this.entityResolver);
305
        XMLUtil.skipWhitespace(this.reader, '&', null, null);
306
        Reader reader = new ContentReader(this.reader, this.entityResolver,
307
                                          '&', StdXMLParser.END_OF_PI, true,
308
                                          "");
309

    
310
        if (! target.equalsIgnoreCase("xml")) {
311
            this.builder.newProcessingInstruction(target, reader);
312
        }
313
        
314
        reader.close();
315
    }
316
    
317
    
318
    /**
319
     * Processes a tag that starts with a bang
320
     * (&lt;&#x21;&#x2e;&#x2e;&#x2e;&gt;).
321
     *
322
     * @param allowCDATA true if CDATA sections are allowed at this point
323
     *
324
     * @throws java.lang.Exception
325
     *     if something went wrong
326
     */
327
    protected void processSpecialTag(boolean allowCDATA)
328
        throws Exception
329
    {
330
        char ch = XMLUtil.read(this.reader, null, '&', this.entityResolver);
331
        
332
        switch (ch) {
333
            case '[':
334
                if (allowCDATA) {
335
                    this.processCDATA();
336
                } else {
337
                    XMLUtil.skipTag(this.reader, '&', this.entityResolver);
338
                }
339
                
340
                return;
341
                
342
            case 'D':
343
                this.processDocType();
344
                return;
345
                
346
            case '-':
347
                XMLUtil.skipComment(this.reader, this.entityResolver);
348
                return;
349
        }        
350
    }
351
    
352
    
353
    /**
354
     * Processes a CDATA section.
355
     *
356
     * @throws java.lang.Exception
357
     *     if something went wrong
358
     */
359
    protected void processCDATA()
360
        throws Exception
361
    {
362
        if (! XMLUtil.checkLiteral(this.reader, '&', this.entityResolver,
363
                                   "CDATA[")) {
364
            XMLUtil.skipTag(this.reader, '&', this.entityResolver);
365
            return;
366
        }
367
        
368
        this.validator.PCDataAdded(this.reader.getSystemID(),
369
                                   this.reader.getLineNr());
370
        Reader reader = new ContentReader(this.reader, this.entityResolver,
371
                                          '&', StdXMLParser.END_OF_CDATA,
372
                                          true, "");
373

    
374
        this.builder.addPCData(reader, this.reader.getSystemID(),
375
                               this.reader.getLineNr());
376
        reader.close();
377
    }
378
        
379

    
380
    /**
381
     * Processes a document type declaration.
382
     *
383
     * @throws java.lang.Exception
384
     *                if an error occurred reading or parsing the data
385
     */
386
    protected void processDocType()
387
        throws Exception
388
    {
389
        if (! XMLUtil.checkLiteral(this.reader, '&', this.entityResolver,
390
                                   "OCTYPE")) {
391
            XMLUtil.skipTag(this.reader, '&', this.entityResolver);
392
            return;
393
        }
394
        
395
        XMLUtil.skipWhitespace(this.reader, '&', null, null);
396
        String systemID = null;
397
        StringBuffer publicID = new StringBuffer();
398
        String rootElement = XMLUtil.scanIdentifier(this.reader, '&',
399
                                                    this.entityResolver);
400
        XMLUtil.skipWhitespace(this.reader, '&', null, null);
401
        char ch = XMLUtil.read(this.reader, null, '&', this.entityResolver);
402
        
403
        if (ch == 'P') {
404
            systemID = XMLUtil.scanPublicID(publicID, reader, '&',
405
                                            this.entityResolver);
406
            XMLUtil.skipWhitespace(this.reader, '&', null, null);
407
            ch = XMLUtil.read(this.reader, null, '&', this.entityResolver);
408
        } else if (ch == 'S') {
409
            systemID = XMLUtil.scanSystemID(reader, '&', this.entityResolver);
410
            XMLUtil.skipWhitespace(this.reader, '&', null, null);
411
            ch = XMLUtil.read(this.reader, null, '&', this.entityResolver);
412
        }
413
        
414
        if (ch == '[') {
415
            this.validator.parseDTD(publicID.toString(),
416
                                    this.reader,
417
                                    this.entityResolver,
418
                                    false);
419
            XMLUtil.skipWhitespace(this.reader, '&', null, null);
420
            ch = XMLUtil.read(this.reader, null, '&', this.entityResolver);
421
        }
422
        
423
        if (ch != '>') {
424
            XMLUtil.errorExpectedInput(reader.getSystemID(),
425
                                       reader.getLineNr(),
426
                                       "`>'");
427
        }
428
        
429
        if (systemID != null) {
430
            Reader reader
431
                    = this.reader.openStream(publicID.toString(), systemID);
432
            this.reader.startNewStream(reader);
433
            this.reader.setSystemID(systemID);
434
            this.reader.setPublicID(publicID.toString());
435
            this.validator.parseDTD(publicID.toString(),
436
                                    this.reader,
437
                                    this.entityResolver,
438
                                    true);
439
        }
440
    }
441
    
442

    
443
    /**
444
     * Processes a regular element.
445
     *
446
     * @throws java.lang.Exception
447
     *     if something went wrong
448
     */
449
    protected void processElement()
450
        throws Exception
451
    {
452
        String name = XMLUtil.scanIdentifier(this.reader, '&',
453
                                             this.entityResolver);
454
        XMLUtil.skipWhitespace(this.reader, '&', null, null);
455
        String prefix = null;
456
        int colonIndex = name.indexOf(':');
457
        
458
        if (colonIndex > 0) {
459
            prefix = name.substring(0, colonIndex);
460
            name = name.substring(colonIndex + 1);
461
        }
462
        
463
        this.validator.elementStarted(name, prefix, null,
464
                                      this.reader.getSystemID(),
465
                                      this.reader.getLineNr());
466
        this.builder.startElement(name, prefix, null,
467
                                  this.reader.getSystemID(),
468
                                  this.reader.getLineNr());
469
        char ch;
470
        
471
        for (;;) {
472
            ch = XMLUtil.read(this.reader, null, '&', this.entityResolver);
473
            
474
            if ((ch == '/') || (ch == '>')) {
475
                break;
476
            }
477
            
478
            this.reader.unread(ch);
479
            this.processAttribute();
480
            XMLUtil.skipWhitespace(this.reader, '&', null, null);
481
        }
482
        
483
        Properties extraAttributes = new Properties();
484
        this.validator.elementAttributesProcessed(name, prefix, null,
485
                                                  extraAttributes,
486
                                                  this.reader.getSystemID(),
487
                                                  this.reader.getLineNr());
488
        Enumeration enum = extraAttributes.keys();
489
        
490
        while (enum.hasMoreElements()) {
491
            String key = (String) enum.nextElement();
492
            String value = extraAttributes.getProperty(key);
493
            String attPrefix = null;
494
            colonIndex = key.indexOf(':');
495
            
496
            if (colonIndex > 0) {
497
                attPrefix = key.substring(0, colonIndex);
498
                key = key.substring(colonIndex + 1);
499
            }
500
            
501
            this.builder.addAttribute(key, attPrefix, null, value, "CDATA");
502
        }
503
        
504
        this.builder.elementAttributesProcessed(name, prefix, null);
505
        
506
        if (ch == '/') {
507
            if (XMLUtil.read(this.reader, null, '&',
508
                             this.entityResolver) != '>') {
509
                XMLUtil.errorExpectedInput(reader.getSystemID(),
510
                                           reader.getLineNr(),
511
                                           "`>'");
512
            }
513
            
514
            this.validator.elementEnded(name, prefix, null,
515
                                        this.reader.getSystemID(),
516
                                        this.reader.getLineNr());
517
            this.builder.endElement(name, prefix, null);
518
            return;
519
        }
520
        
521
        StringBuffer whitespaceBuffer = new StringBuffer(16);
522
        
523
        for (;;) {
524
            whitespaceBuffer.setLength(0);
525
            boolean fromEntity[] = new boolean[1];
526
            XMLUtil.skipWhitespace(this.reader, '&', whitespaceBuffer,
527
                                   fromEntity);
528
            ch = XMLUtil.read(this.reader, null, '&', this.entityResolver);
529
            
530
            if ((ch == '<') && (! fromEntity[0])) {
531
                ch = reader.read();
532
                
533
                if (ch == '/') {
534
                    XMLUtil.skipWhitespace(this.reader, '&', null, null);
535
                    String str = XMLUtil.scanIdentifier(this.reader, '&',
536
                                                        this.entityResolver);
537
                    
538
                    if (! str.equals(name)) {
539
                        XMLUtil.errorWrongClosingTag(reader.getSystemID(),
540
                                                     reader.getLineNr(),
541
                                                     name, str);
542
                    }
543
                    
544
                    XMLUtil.skipWhitespace(this.reader, '&', null, null);
545
                    
546
                    if (XMLUtil.read(this.reader, null, '&',
547
                                     this.entityResolver) != '>') {
548
                        XMLUtil.errorClosingTagNotEmpty(reader.getSystemID(),
549
                                                        reader.getLineNr());
550
                    }
551
                    
552
                    this.validator.elementEnded(name, prefix, null,
553
                                                this.reader.getSystemID(),
554
                                                this.reader.getLineNr());
555
                    this.builder.endElement(name, prefix, null);
556
                    break;
557
                } else {
558
                    this.reader.unread(ch);
559
                    this.scanSomeTag(true /*CDATA allowed*/);
560
                }
561
            } else {
562
                this.validator.PCDataAdded(this.reader.getSystemID(),
563
                                           this.reader.getLineNr());
564
                this.reader.unread(ch);
565
                Reader reader = new ContentReader(this.reader,
566
                                                  this.entityResolver,
567
                                                  '&',
568
                                                  StdXMLParser.END_OF_PCDATA,
569
                                                  false,
570
                                                  whitespaceBuffer.toString());
571
                this.builder.addPCData(reader, this.reader.getSystemID(),
572
                                       this.reader.getLineNr());
573
                reader.close();
574
                this.reader.unread('<');
575
            }
576
        }
577
    }
578
    
579

    
580
    /**
581
     * Processes an attribute of an element.
582
     *
583
     * @throws java.lang.Exception
584
     *     if something went wrong
585
     */
586
    protected void processAttribute()
587
        throws Exception
588
    {
589
        String key = XMLUtil.scanIdentifier(this.reader, '&',
590
                                            this.entityResolver);
591
        XMLUtil.skipWhitespace(this.reader, '&', null, null);
592
        
593
        if (XMLUtil.read(this.reader, null, '&', this.entityResolver) != '=') {
594
            XMLUtil.errorExpectedInput(reader.getSystemID(),
595
                                       reader.getLineNr(),
596
                                       "`='");
597
        }
598
        
599
        String value = XMLUtil.scanString(this.reader, '&', true,
600
                                          this.entityResolver);
601
        this.validator.attributeAdded(key, null, null, value,
602
                                      this.reader.getSystemID(),
603
                                      this.reader.getLineNr());
604
        this.builder.addAttribute(key, null, null, value, "CDATA");
605
    }
606
    
607
}