OpenCores
URL https://opencores.org/ocsvn/scarts/scarts/trunk

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [libjava/] [classpath/] [java/] [util/] [Properties.java] - Blame information for rev 14

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 14 jlechner
/* Properties.java -- a set of persistent properties
2
   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005  Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
 
39
package java.util;
40
 
41
import java.io.BufferedReader;
42
import java.io.IOException;
43
import java.io.InputStream;
44
import java.io.InputStreamReader;
45
import java.io.OutputStream;
46
import java.io.OutputStreamWriter;
47
import java.io.PrintStream;
48
import java.io.PrintWriter;
49
 
50
import javax.xml.parsers.ParserConfigurationException;
51
import javax.xml.parsers.SAXParser;
52
import javax.xml.parsers.SAXParserFactory;
53
 
54
import org.xml.sax.Attributes;
55
import org.xml.sax.InputSource;
56
import org.xml.sax.SAXException;
57
import org.xml.sax.XMLReader;
58
import org.xml.sax.ext.DefaultHandler2;
59
 
60
import org.w3c.dom.Document;
61
import org.w3c.dom.DocumentType;
62
import org.w3c.dom.DOMImplementation;
63
import org.w3c.dom.Element;
64
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
65
import org.w3c.dom.ls.DOMImplementationLS;
66
import org.w3c.dom.ls.LSOutput;
67
import org.w3c.dom.ls.LSSerializer;
68
 
69
/**
70
 * A set of persistent properties, which can be saved or loaded from a stream.
71
 * A property list may also contain defaults, searched if the main list
72
 * does not contain a property for a given key.
73
 *
74
 * An example of a properties file for the german language is given
75
 * here.  This extends the example given in ListResourceBundle.
76
 * Create a file MyResource_de.properties with the following contents
77
 * and put it in the CLASSPATH.  (The character
78
 * <code>\</code><code>u00e4</code> is the german umlaut)
79
 *
80
 *
81
<pre>s1=3
82
s2=MeineDisk
83
s3=3. M\<code></code>u00e4rz 96
84
s4=Die Diskette ''{1}'' enth\<code></code>u00e4lt {0} in {2}.
85
s5=0
86
s6=keine Dateien
87
s7=1
88
s8=eine Datei
89
s9=2
90
s10={0,number} Dateien
91
s11=Das Formatieren schlug fehl mit folgender Exception: {0}
92
s12=FEHLER
93
s13=Ergebnis
94
s14=Dialog
95
s15=Auswahlkriterium
96
s16=1,3</pre>
97
 *
98
 * <p>Although this is a sub class of a hash table, you should never
99
 * insert anything other than strings to this property, or several
100
 * methods, that need string keys and values, will fail.  To ensure
101
 * this, you should use the <code>get/setProperty</code> method instead
102
 * of <code>get/put</code>.
103
 *
104
 * Properties are saved in ISO 8859-1 encoding, using Unicode escapes with
105
 * a single <code>u</code> for any character which cannot be represented.
106
 *
107
 * @author Jochen Hoenicke
108
 * @author Eric Blake (ebb9@email.byu.edu)
109
 * @see PropertyResourceBundle
110
 * @status updated to 1.4
111
 */
112
public class Properties extends Hashtable
113
{
114
  // WARNING: Properties is a CORE class in the bootstrap cycle. See the
115
  // comments in vm/reference/java/lang/Runtime for implications of this fact.
116
 
117
  /**
118
   * The property list that contains default values for any keys not
119
   * in this property list.
120
   *
121
   * @serial the default properties
122
   */
123
  protected Properties defaults;
124
 
125
  /**
126
   * Compatible with JDK 1.0+.
127
   */
128
  private static final long serialVersionUID = 4112578634029874840L;
129
 
130
  /**
131
   * Creates a new empty property list with no default values.
132
   */
133
  public Properties()
134
  {
135
  }
136
 
137
  /**
138
   * Create a new empty property list with the specified default values.
139
   *
140
   * @param defaults a Properties object containing the default values
141
   */
142
  public Properties(Properties defaults)
143
  {
144
    this.defaults = defaults;
145
  }
146
 
147
  /**
148
   * Adds the given key/value pair to this properties.  This calls
149
   * the hashtable method put.
150
   *
151
   * @param key the key for this property
152
   * @param value the value for this property
153
   * @return The old value for the given key
154
   * @see #getProperty(String)
155
   * @since 1.2
156
   */
157
  public Object setProperty(String key, String value)
158
  {
159
    return put(key, value);
160
  }
161
 
162
  /**
163
   * Reads a property list from an input stream.  The stream should
164
   * have the following format: <br>
165
   *
166
   * An empty line or a line starting with <code>#</code> or
167
   * <code>!</code> is ignored.  An backslash (<code>\</code>) at the
168
   * end of the line makes the line continueing on the next line
169
   * (but make sure there is no whitespace after the backslash).
170
   * Otherwise, each line describes a key/value pair. <br>
171
   *
172
   * The chars up to the first whitespace, = or : are the key.  You
173
   * can include this caracters in the key, if you precede them with
174
   * a backslash (<code>\</code>). The key is followed by optional
175
   * whitespaces, optionally one <code>=</code> or <code>:</code>,
176
   * and optionally some more whitespaces.  The rest of the line is
177
   * the resource belonging to the key. <br>
178
   *
179
   * Escape sequences <code>\t, \n, \r, \\, \", \', \!, \#, \ </code>(a
180
   * space), and unicode characters with the
181
   * <code>\\u</code><em>xxxx</em> notation are detected, and
182
   * converted to the corresponding single character. <br>
183
   *
184
   *
185
<pre># This is a comment
186
key     = value
187
k\:5      \ a string starting with space and ending with newline\n
188
# This is a multiline specification; note that the value contains
189
# no white space.
190
weekdays: Sunday,Monday,Tuesday,Wednesday,\\
191
          Thursday,Friday,Saturday
192
# The safest way to include a space at the end of a value:
193
label   = Name:\\u0020</pre>
194
   *
195
   * @param inStream the input stream
196
   * @throws IOException if an error occurred when reading the input
197
   * @throws NullPointerException if in is null
198
   */
199
  public void load(InputStream inStream) throws IOException
200
  {
201
    // The spec says that the file must be encoded using ISO-8859-1.
202
    BufferedReader reader =
203
      new BufferedReader(new InputStreamReader(inStream, "ISO-8859-1"));
204
    String line;
205
 
206
    while ((line = reader.readLine()) != null)
207
      {
208
        char c = 0;
209
        int pos = 0;
210
        // Leading whitespaces must be deleted first.
211
        while (pos < line.length()
212
               && Character.isWhitespace(c = line.charAt(pos)))
213
          pos++;
214
 
215
        // If empty line or begins with a comment character, skip this line.
216
        if ((line.length() - pos) == 0
217
            || line.charAt(pos) == '#' || line.charAt(pos) == '!')
218
          continue;
219
 
220
        // The characters up to the next Whitespace, ':', or '='
221
        // describe the key.  But look for escape sequences.
222
        // Try to short-circuit when there is no escape char.
223
        int start = pos;
224
        boolean needsEscape = line.indexOf('\\', pos) != -1;
225
        StringBuilder key = needsEscape ? new StringBuilder() : null;
226
        while (pos < line.length()
227
               && ! Character.isWhitespace(c = line.charAt(pos++))
228
               && c != '=' && c != ':')
229
          {
230
            if (needsEscape && c == '\\')
231
              {
232
                if (pos == line.length())
233
                  {
234
                    // The line continues on the next line.  If there
235
                    // is no next line, just treat it as a key with an
236
                    // empty value.
237
                    line = reader.readLine();
238
                    if (line == null)
239
                      line = "";
240
                    pos = 0;
241
                    while (pos < line.length()
242
                           && Character.isWhitespace(c = line.charAt(pos)))
243
                      pos++;
244
                  }
245
                else
246
                  {
247
                    c = line.charAt(pos++);
248
                    switch (c)
249
                      {
250
                      case 'n':
251
                        key.append('\n');
252
                        break;
253
                      case 't':
254
                        key.append('\t');
255
                        break;
256
                      case 'r':
257
                        key.append('\r');
258
                        break;
259
                      case 'u':
260
                        if (pos + 4 <= line.length())
261
                          {
262
                            char uni = (char) Integer.parseInt
263
                              (line.substring(pos, pos + 4), 16);
264
                            key.append(uni);
265
                            pos += 4;
266
                          }        // else throw exception?
267
                        break;
268
                      default:
269
                        key.append(c);
270
                        break;
271
                      }
272
                  }
273
              }
274
            else if (needsEscape)
275
              key.append(c);
276
          }
277
 
278
        boolean isDelim = (c == ':' || c == '=');
279
 
280
        String keyString;
281
        if (needsEscape)
282
          keyString = key.toString();
283
        else if (isDelim || Character.isWhitespace(c))
284
          keyString = line.substring(start, pos - 1);
285
        else
286
          keyString = line.substring(start, pos);
287
 
288
        while (pos < line.length()
289
               && Character.isWhitespace(c = line.charAt(pos)))
290
          pos++;
291
 
292
        if (! isDelim && (c == ':' || c == '='))
293
          {
294
            pos++;
295
            while (pos < line.length()
296
                   && Character.isWhitespace(c = line.charAt(pos)))
297
              pos++;
298
          }
299
 
300
        // Short-circuit if no escape chars found.
301
        if (!needsEscape)
302
          {
303
            put(keyString, line.substring(pos));
304
            continue;
305
          }
306
 
307
        // Escape char found so iterate through the rest of the line.
308
        StringBuilder element = new StringBuilder(line.length() - pos);
309
        while (pos < line.length())
310
          {
311
            c = line.charAt(pos++);
312
            if (c == '\\')
313
              {
314
                if (pos == line.length())
315
                  {
316
                    // The line continues on the next line.
317
                    line = reader.readLine();
318
 
319
                    // We might have seen a backslash at the end of
320
                    // the file.  The JDK ignores the backslash in
321
                    // this case, so we follow for compatibility.
322
                    if (line == null)
323
                      break;
324
 
325
                    pos = 0;
326
                    while (pos < line.length()
327
                           && Character.isWhitespace(c = line.charAt(pos)))
328
                      pos++;
329
                    element.ensureCapacity(line.length() - pos +
330
                                           element.length());
331
                  }
332
                else
333
                  {
334
                    c = line.charAt(pos++);
335
                    switch (c)
336
                      {
337
                      case 'n':
338
                        element.append('\n');
339
                        break;
340
                      case 't':
341
                        element.append('\t');
342
                        break;
343
                      case 'r':
344
                        element.append('\r');
345
                        break;
346
                      case 'u':
347
                        if (pos + 4 <= line.length())
348
                          {
349
                            char uni = (char) Integer.parseInt
350
                              (line.substring(pos, pos + 4), 16);
351
                            element.append(uni);
352
                            pos += 4;
353
                          }        // else throw exception?
354
                        break;
355
                      default:
356
                        element.append(c);
357
                        break;
358
                      }
359
                  }
360
              }
361
            else
362
              element.append(c);
363
          }
364
        put(keyString, element.toString());
365
      }
366
  }
367
 
368
  /**
369
   * Calls <code>store(OutputStream out, String header)</code> and
370
   * ignores the IOException that may be thrown.
371
   *
372
   * @param out the stream to write to
373
   * @param header a description of the property list
374
   * @throws ClassCastException if this property contains any key or
375
   *         value that are not strings
376
   * @deprecated use {@link #store(OutputStream, String)} instead
377
   */
378
  public void save(OutputStream out, String header)
379
  {
380
    try
381
      {
382
        store(out, header);
383
      }
384
    catch (IOException ex)
385
      {
386
      }
387
  }
388
 
389
  /**
390
   * Writes the key/value pairs to the given output stream, in a format
391
   * suitable for <code>load</code>.<br>
392
   *
393
   * If header is not null, this method writes a comment containing
394
   * the header as first line to the stream.  The next line (or first
395
   * line if header is null) contains a comment with the current date.
396
   * Afterwards the key/value pairs are written to the stream in the
397
   * following format.<br>
398
   *
399
   * Each line has the form <code>key = value</code>.  Newlines,
400
   * Returns and tabs are written as <code>\n,\t,\r</code> resp.
401
   * The characters <code>\, !, #, =</code> and <code>:</code> are
402
   * preceeded by a backslash.  Spaces are preceded with a backslash,
403
   * if and only if they are at the beginning of the key.  Characters
404
   * that are not in the ascii range 33 to 127 are written in the
405
   * <code>\</code><code>u</code>xxxx Form.<br>
406
   *
407
   * Following the listing, the output stream is flushed but left open.
408
   *
409
   * @param out the output stream
410
   * @param header the header written in the first line, may be null
411
   * @throws ClassCastException if this property contains any key or
412
   *         value that isn't a string
413
   * @throws IOException if writing to the stream fails
414
   * @throws NullPointerException if out is null
415
   * @since 1.2
416
   */
417
  public void store(OutputStream out, String header) throws IOException
418
  {
419
    // The spec says that the file must be encoded using ISO-8859-1.
420
    PrintWriter writer
421
      = new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1"));
422
    if (header != null)
423
      writer.println("#" + header);
424
    writer.println ("#" + Calendar.getInstance ().getTime ());
425
 
426
    Iterator iter = entrySet ().iterator ();
427
    int i = size ();
428
    StringBuilder s = new StringBuilder (); // Reuse the same buffer.
429
    while (--i >= 0)
430
      {
431
        Map.Entry entry = (Map.Entry) iter.next ();
432
        formatForOutput ((String) entry.getKey (), s, true);
433
        s.append ('=');
434
        formatForOutput ((String) entry.getValue (), s, false);
435
        writer.println (s);
436
      }
437
 
438
    writer.flush ();
439
  }
440
 
441
  /**
442
   * Gets the property with the specified key in this property list.
443
   * If the key is not found, the default property list is searched.
444
   * If the property is not found in the default, null is returned.
445
   *
446
   * @param key The key for this property
447
   * @return the value for the given key, or null if not found
448
   * @throws ClassCastException if this property contains any key or
449
   *         value that isn't a string
450
   * @see #defaults
451
   * @see #setProperty(String, String)
452
   * @see #getProperty(String, String)
453
   */
454
  public String getProperty(String key)
455
  {
456
    Properties prop = this;
457
    // Eliminate tail recursion.
458
    do
459
      {
460
        String value = (String) prop.get(key);
461
        if (value != null)
462
          return value;
463
        prop = prop.defaults;
464
      }
465
    while (prop != null);
466
    return null;
467
  }
468
 
469
  /**
470
   * Gets the property with the specified key in this property list.  If
471
   * the key is not found, the default property list is searched.  If the
472
   * property is not found in the default, the specified defaultValue is
473
   * returned.
474
   *
475
   * @param key The key for this property
476
   * @param defaultValue A default value
477
   * @return The value for the given key
478
   * @throws ClassCastException if this property contains any key or
479
   *         value that isn't a string
480
   * @see #defaults
481
   * @see #setProperty(String, String)
482
   */
483
  public String getProperty(String key, String defaultValue)
484
  {
485
    String prop = getProperty(key);
486
    if (prop == null)
487
      prop = defaultValue;
488
    return prop;
489
  }
490
 
491
  /**
492
   * Returns an enumeration of all keys in this property list, including
493
   * the keys in the default property list.
494
   *
495
   * @return an Enumeration of all defined keys
496
   */
497
  public Enumeration propertyNames()
498
  {
499
    // We make a new Set that holds all the keys, then return an enumeration
500
    // for that. This prevents modifications from ruining the enumeration,
501
    // as well as ignoring duplicates.
502
    Properties prop = this;
503
    Set s = new HashSet();
504
    // Eliminate tail recursion.
505
    do
506
      {
507
        s.addAll(prop.keySet());
508
        prop = prop.defaults;
509
      }
510
    while (prop != null);
511
    return Collections.enumeration(s);
512
  }
513
 
514
  /**
515
   * Prints the key/value pairs to the given print stream.  This is
516
   * mainly useful for debugging purposes.
517
   *
518
   * @param out the print stream, where the key/value pairs are written to
519
   * @throws ClassCastException if this property contains a key or a
520
   *         value that isn't a string
521
   * @see #list(PrintWriter)
522
   */
523
  public void list(PrintStream out)
524
  {
525
    PrintWriter writer = new PrintWriter (out);
526
    list (writer);
527
  }
528
 
529
  /**
530
   * Prints the key/value pairs to the given print writer.  This is
531
   * mainly useful for debugging purposes.
532
   *
533
   * @param out the print writer where the key/value pairs are written to
534
   * @throws ClassCastException if this property contains a key or a
535
   *         value that isn't a string
536
   * @see #list(PrintStream)
537
   * @since 1.1
538
   */
539
  public void list(PrintWriter out)
540
  {
541
    out.println ("-- listing properties --");
542
 
543
    Iterator iter = entrySet ().iterator ();
544
    int i = size ();
545
    while (--i >= 0)
546
      {
547
        Map.Entry entry = (Map.Entry) iter.next ();
548
        out.print ((String) entry.getKey () + "=");
549
 
550
        // JDK 1.3/1.4 restrict the printed value, but not the key,
551
        // to 40 characters, including the truncating ellipsis.
552
        String s = (String ) entry.getValue ();
553
        if (s != null && s.length () > 40)
554
          out.println (s.substring (0, 37) + "...");
555
        else
556
          out.println (s);
557
      }
558
    out.flush ();
559
  }
560
 
561
  /**
562
   * Formats a key or value for output in a properties file.
563
   * See store for a description of the format.
564
   *
565
   * @param str the string to format
566
   * @param buffer the buffer to add it to
567
   * @param key true if all ' ' must be escaped for the key, false if only
568
   *        leading spaces must be escaped for the value
569
   * @see #store(OutputStream, String)
570
   */
571
  private void formatForOutput(String str, StringBuilder buffer, boolean key)
572
  {
573
    if (key)
574
      {
575
        buffer.setLength(0);
576
        buffer.ensureCapacity(str.length());
577
      }
578
    else
579
      buffer.ensureCapacity(buffer.length() + str.length());
580
    boolean head = true;
581
    int size = str.length();
582
    for (int i = 0; i < size; i++)
583
      {
584
        char c = str.charAt(i);
585
        switch (c)
586
          {
587
          case '\n':
588
            buffer.append("\\n");
589
            break;
590
          case '\r':
591
            buffer.append("\\r");
592
            break;
593
          case '\t':
594
            buffer.append("\\t");
595
            break;
596
          case ' ':
597
            buffer.append(head ? "\\ " : " ");
598
            break;
599
          case '\\':
600
          case '!':
601
          case '#':
602
          case '=':
603
          case ':':
604
            buffer.append('\\').append(c);
605
            break;
606
          default:
607
            if (c < ' ' || c > '~')
608
              {
609
                String hex = Integer.toHexString(c);
610
                buffer.append("\\u0000".substring(0, 6 - hex.length()));
611
                buffer.append(hex);
612
              }
613
            else
614
              buffer.append(c);
615
          }
616
        if (c != ' ')
617
          head = key;
618
      }
619
  }
620
 
621
  /**
622
   * <p>
623
   * Encodes the properties as an XML file using the UTF-8 encoding.
624
   * The format of the XML file matches the DTD
625
   * <a href="http://java.sun.com/dtd/properties.dtd">
626
   * http://java.sun.com/dtd/properties.dtd</a>.
627
   * </p>
628
   * <p>
629
   * Invoking this method provides the same behaviour as invoking
630
   * <code>storeToXML(os, comment, "UTF-8")</code>.
631
   * </p>
632
   *
633
   * @param os the stream to output to.
634
   * @param comment a comment to include at the top of the XML file, or
635
   *                <code>null</code> if one is not required.
636
   * @throws IOException if the serialization fails.
637
   * @throws NullPointerException if <code>os</code> is null.
638
   * @since 1.5
639
   */
640
  public void storeToXML(OutputStream os, String comment)
641
    throws IOException
642
  {
643
    storeToXML(os, comment, "UTF-8");
644
  }
645
 
646
  /**
647
   * <p>
648
   * Encodes the properties as an XML file using the supplied encoding.
649
   * The format of the XML file matches the DTD
650
   * <a href="http://java.sun.com/dtd/properties.dtd">
651
   * http://java.sun.com/dtd/properties.dtd</a>.
652
   * </p>
653
   *
654
   * @param os the stream to output to.
655
   * @param comment a comment to include at the top of the XML file, or
656
   *                <code>null</code> if one is not required.
657
   * @param encoding the encoding to use for the XML output.
658
   * @throws IOException if the serialization fails.
659
   * @throws NullPointerException if <code>os</code> or <code>encoding</code>
660
   *                              is null.
661
   * @since 1.5
662
   */
663
  public void storeToXML(OutputStream os, String comment, String encoding)
664
    throws IOException
665
  {
666
    if (os == null)
667
      throw new NullPointerException("Null output stream supplied.");
668
    if (encoding == null)
669
      throw new NullPointerException("Null encoding supplied.");
670
    try
671
      {
672
        DOMImplementationRegistry registry =
673
          DOMImplementationRegistry.newInstance();
674
        DOMImplementation domImpl = registry.getDOMImplementation("LS 3.0");
675
        DocumentType doctype =
676
          domImpl.createDocumentType("properties", null,
677
                                     "http://java.sun.com/dtd/properties.dtd");
678
        Document doc = domImpl.createDocument(null, "properties", doctype);
679
        Element root = doc.getDocumentElement();
680
        if (comment != null)
681
          {
682
            Element commentElement = doc.createElement("comment");
683
            commentElement.appendChild(doc.createTextNode(comment));
684
            root.appendChild(commentElement);
685
          }
686
        Iterator iterator = entrySet().iterator();
687
        while (iterator.hasNext())
688
          {
689
            Map.Entry entry = (Map.Entry) iterator.next();
690
            Element entryElement = doc.createElement("entry");
691
            entryElement.setAttribute("key", (String) entry.getKey());
692
            entryElement.appendChild(doc.createTextNode((String)
693
                                                        entry.getValue()));
694
            root.appendChild(entryElement);
695
          }
696
        DOMImplementationLS loadAndSave = (DOMImplementationLS) domImpl;
697
        LSSerializer serializer = loadAndSave.createLSSerializer();
698
        LSOutput output = loadAndSave.createLSOutput();
699
        output.setByteStream(os);
700
        output.setEncoding(encoding);
701
        serializer.write(doc, output);
702
      }
703
    catch (ClassNotFoundException e)
704
      {
705
        throw (IOException)
706
          new IOException("The XML classes could not be found.").initCause(e);
707
      }
708
    catch (InstantiationException e)
709
      {
710
        throw (IOException)
711
          new IOException("The XML classes could not be instantiated.")
712
          .initCause(e);
713
      }
714
    catch (IllegalAccessException e)
715
      {
716
        throw (IOException)
717
          new IOException("The XML classes could not be accessed.")
718
          .initCause(e);
719
      }
720
  }
721
 
722
  /**
723
   * <p>
724
   * Decodes the contents of the supplied <code>InputStream</code> as
725
   * an XML file, which represents a set of properties.  The format of
726
   * the XML file must match the DTD
727
   * <a href="http://java.sun.com/dtd/properties.dtd">
728
   * http://java.sun.com/dtd/properties.dtd</a>.
729
   * </p>
730
   *
731
   * @param in the input stream from which to receive the XML data.
732
   * @throws IOException if an I/O error occurs in reading the input data.
733
   * @throws InvalidPropertiesFormatException if the input data does not
734
   *                                          constitute an XML properties
735
   *                                          file.
736
   * @throws NullPointerException if <code>in</code> is null.
737
   * @since 1.5
738
   */
739
  public void loadFromXML(InputStream in)
740
    throws IOException, InvalidPropertiesFormatException
741
  {
742
    if (in == null)
743
      throw new NullPointerException("Null input stream supplied.");
744
    try
745
      {
746
        SAXParserFactory factory = SAXParserFactory.newInstance();
747
        factory.setValidating(false); /* Don't use the URI */
748
        XMLReader parser = factory.newSAXParser().getXMLReader();
749
        PropertiesHandler handler = new PropertiesHandler();
750
        parser.setContentHandler(handler);
751
        parser.setProperty("http://xml.org/sax/properties/lexical-handler",
752
                           handler);
753
        parser.parse(new InputSource(in));
754
      }
755
    catch (SAXException e)
756
      {
757
        throw (InvalidPropertiesFormatException)
758
          new InvalidPropertiesFormatException("Error in parsing XML.").
759
          initCause(e);
760
      }
761
    catch (ParserConfigurationException e)
762
      {
763
        throw (IOException)
764
          new IOException("An XML parser could not be found.").
765
          initCause(e);
766
      }
767
  }
768
 
769
  /**
770
   * This class deals with the parsing of XML using
771
   * <a href="http://java.sun.com/dtd/properties.dtd">
772
   * http://java.sun.com/dtd/properties.dtd</a>.
773
   *
774
   * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
775
   * @since 1.5
776
   */
777
  private class PropertiesHandler
778
    extends DefaultHandler2
779
  {
780
 
781
    /**
782
     * The current key.
783
     */
784
    private String key;
785
 
786
    /**
787
     * The current value.
788
     */
789
    private String value;
790
 
791
    /**
792
     * A flag to check whether a valid DTD declaration has been seen.
793
     */
794
    private boolean dtdDeclSeen;
795
 
796
    /**
797
     * Constructs a new Properties handler.
798
     */
799
    public PropertiesHandler()
800
    {
801
      key = null;
802
      value = null;
803
      dtdDeclSeen = false;
804
    }
805
 
806
    /**
807
     * <p>
808
     * Captures the start of the DTD declarations, if they exist.
809
     * A valid properties file must declare the following doctype:
810
     * </p>
811
     * <p>
812
     * <code>!DOCTYPE properties SYSTEM
813
     * "http://java.sun.com/dtd/properties.dtd"</code>
814
     * </p>
815
     *
816
     * @param name the name of the document type.
817
     * @param publicId the public identifier that was declared, or
818
     *                 null if there wasn't one.
819
     * @param systemId the system identifier that was declared, or
820
     *                 null if there wasn't one.
821
     * @throws SAXException if some error occurs in parsing.
822
     */
823
    public void startDTD(String name, String publicId, String systemId)
824
      throws SAXException
825
    {
826
      if (name.equals("properties") &&
827
          publicId == null &&
828
          systemId.equals("http://java.sun.com/dtd/properties.dtd"))
829
        {
830
          dtdDeclSeen = true;
831
        }
832
      else
833
        throw new SAXException("Invalid DTD declaration: " + name);
834
    }
835
 
836
    /**
837
     * Captures the start of an XML element.
838
     *
839
     * @param uri the namespace URI.
840
     * @param localName the local name of the element inside the namespace.
841
     * @param qName the local name qualified with the namespace URI.
842
     * @param attributes the attributes of this element.
843
     * @throws SAXException if some error occurs in parsing.
844
     */
845
    public void startElement(String uri, String localName,
846
                             String qName, Attributes attributes)
847
      throws SAXException
848
    {
849
      if (qName.equals("entry"))
850
        {
851
          int index = attributes.getIndex("key");
852
          if (index != -1)
853
            key = attributes.getValue(index);
854
        }
855
      else if (qName.equals("comment") || qName.equals("properties"))
856
        {
857
          /* Ignore it */
858
        }
859
      else
860
        throw new SAXException("Invalid tag: " + qName);
861
    }
862
 
863
    /**
864
     * Captures characters within an XML element.
865
     *
866
     * @param ch the array of characters.
867
     * @param start the start index of the characters to use.
868
     * @param length the number of characters to use from the start index on.
869
     * @throws SAXException if some error occurs in parsing.
870
     */
871
    public void characters(char[] ch, int start, int length)
872
      throws SAXException
873
    {
874
      if (key != null)
875
        value = new String(ch,start,length);
876
    }
877
 
878
    /**
879
     * Captures the end of an XML element.
880
     *
881
     * @param uri the namespace URI.
882
     * @param localName the local name of the element inside the namespace.
883
     * @param qName the local name qualified with the namespace URI.
884
     * @throws SAXException if some error occurs in parsing.
885
     */
886
    public void endElement(String uri, String localName,
887
                           String qName)
888
      throws SAXException
889
    {
890
      if (qName.equals("entry"))
891
        {
892
          if (value == null)
893
            value = "";
894
          setProperty(key, value);
895
          key = null;
896
          value = null;
897
        }
898
    }
899
 
900
    /**
901
     * Captures the end of the XML document.  If a DTD declaration has
902
     * not been seen, the document is erroneous and an exception is thrown.
903
     *
904
     * @throws SAXException if the correct DTD declaration didn't appear.
905
     */
906
    public void endDocument()
907
      throws SAXException
908
    {
909
      if (!dtdDeclSeen)
910
        throw new SAXException("No appropriate DTD declaration was seen.");
911
    }
912
 
913
  } // class PropertiesHandler
914
 
915
} // class Properties

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.