001: /*
002: * @(#)Properties.java 1.70 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.util;
029:
030: import java.io.IOException;
031: import java.io.PrintStream;
032: import java.io.PrintWriter;
033: import java.io.InputStream;
034: import java.io.InputStreamReader;
035: import java.io.BufferedReader;
036: import java.io.OutputStream;
037: import java.io.OutputStreamWriter;
038: import java.io.BufferedWriter;
039: import java.util.Hashtable;
040:
041: /**
042: * The <code>Properties</code> class represents a persistent set of
043: * properties. The <code>Properties</code> can be saved to a stream
044: * or loaded from a stream. Each key and its corresponding value in
045: * the property list is a string.
046: * <p>
047: * A property list can contain another property list as its
048: * "defaults"; this second property list is searched if
049: * the property key is not found in the original property list.
050: * <p>
051: * Because <code>Properties</code> inherits from <code>Hashtable</code>, the
052: * <code>put</code> and <code>putAll</code> methods can be applied to a
053: * <code>Properties</code> object. Their use is strongly discouraged as they
054: * allow the caller to insert entries whose keys or values are not
055: * <code>Strings</code>. The <code>setProperty</code> method should be used
056: * instead. If the <code>store</code> or <code>save</code> method is called
057: * on a "compromised" <code>Properties</code> object that contains a
058: * non-<code>String</code> key or value, the call will fail.
059: * <p>
060: * <a name="encoding"></a>
061: * When saving properties to a stream or loading them from a stream, the
062: * ISO 8859-1 character encoding is used. For characters that cannot be directly
063: * represented in this encoding,
064: * <a href="http://java.sun.com/docs/books/jls/html/3.doc.html#100850">Unicode escapes</a>
065: * are used; however, only a single 'u' character is allowed in an escape sequence.
066: * The native2ascii tool can be used to convert property files to and from
067: * other character encodings.
068: *
069: * @see <a href="../../../tooldocs/solaris/native2ascii.html">native2ascii tool for Solaris</a>
070: * @see <a href="../../../tooldocs/windows/native2ascii.html">native2ascii tool for Windows</a>
071: *
072: * @author Arthur van Hoff
073: * @author Michael McCloskey
074: * @version 1.61, 05/03/00
075: * @since JDK1.0
076: */
077: public class Properties extends Hashtable {
078: /**
079: * use serialVersionUID from JDK 1.1.X for interoperability
080: */
081: private static final long serialVersionUID = 4112578634029874840L;
082:
083: /**
084: * A property list that contains default values for any keys not
085: * found in this property list.
086: *
087: * @serial
088: */
089: protected Properties defaults;
090:
091: /**
092: * Creates an empty property list with no default values.
093: */
094: public Properties() {
095: this (null);
096: }
097:
098: /**
099: * Creates an empty property list with the specified defaults.
100: *
101: * @param defaults the defaults.
102: */
103: public Properties(Properties defaults) {
104: this .defaults = defaults;
105: }
106:
107: /**
108: * Calls the <tt>Hashtable</tt> method <code>put</code>. Provided for
109: * parallelism with the <tt>getProperty</tt> method. Enforces use of
110: * strings for property keys and values. The value returned is the
111: * result of the <tt>Hashtable</tt> call to <code>put</code>.
112: *
113: * @param key the key to be placed into this property list.
114: * @param value the value corresponding to <tt>key</tt>.
115: * @return the previous value of the specified key in this property
116: * list, or <code>null</code> if it did not have one.
117: * @see #getProperty
118: * @since 1.2
119: */
120: public synchronized Object setProperty(String key, String value) {
121: return put(key, value);
122: }
123:
124: private static final String keyValueSeparators = "=: \t\r\n\f";
125:
126: private static final String strictKeyValueSeparators = "=:";
127:
128: private static final String specialSaveChars = "=: \t\r\n\f#!";
129:
130: private static final String whiteSpaceChars = " \t\r\n\f";
131:
132: /**
133: * Reads a property list (key and element pairs) from the input
134: * stream. The stream is assumed to be using the ISO 8859-1
135: * character encoding; that is each byte is one Latin1 character.
136: * Characters not in Latin1, and certain special characters, can
137: * be represented in keys and elements using escape sequences
138: * similar to those used for character and string literals (see <a
139: * href="http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#100850">§3.3</a>
140: * and <a
141: * href="http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#101089">§3.10.6</a>
142: * of the <i>Java Language Specification</i>).
143: *
144: * The differences from the character escape sequences used for
145: * characters and strings are:
146: *
147: * <ul>
148: * <li> Octal escapes are not recognized.
149: *
150: * <li> The character sequence <code>\b</code> does <i>not</i>
151: * represent a backspace character.
152: *
153: * <li> The method does not treat a backslash character,
154: * <code>\</code>, before a non-valid escape character as an
155: * error; the backslash is silently dropped. For example, in a
156: * Java string the sequence <code>"\z"</code> would cause a
157: * compile time error. In contrast, this method silently drops
158: * the backslash. Therefore, this method treats the two character
159: * sequence <code>"\b"</code> as equivalent to the single
160: * character <code>'b'</code>.
161: *
162: * <li> Escapes are not necessary for single and double quotes;
163: * however, by the rule above, single and double quote characters
164: * preceded by a backslash still yield single and double quote
165: * characters, respectively.
166: *
167: * </ul>
168: *
169: * An <code>IllegalArgumentException</code> is thrown if a
170: * malformed Unicode escape appears in the input.
171: *
172: * <p>
173: * This method processes input in terms of lines. A natural line
174: * of input is terminated either by a set of line terminator
175: * characters (<code>\n</code> or <code>\r</code> or
176: * <code>\r\n</code>) or by the end of the file. A natural line
177: * may be either a blank line, a comment line, or hold some part
178: * of a key-element pair. The logical line holding all the data
179: * for a key-element pair may be spread out across several adjacent
180: * natural lines by escaping the line terminator sequence with a
181: * backslash character, <code>\</code>. Note that a comment line
182: * cannot be extended in this manner; every natural line that is a
183: * comment must have its own comment indicator, as described
184: * below. If a logical line is continued over several natural
185: * lines, the continuation lines receive further processing, also
186: * described below. Lines are read from the input stream until
187: * end of file is reached.
188: *
189: * <p>
190: * A natural line that contains only white space characters is
191: * considered blank and is ignored. A comment line has an ASCII
192: * <code>'#'</code> or <code>'!'</code> as its first non-white
193: * space character; comment lines are also ignored and do not
194: * encode key-element information. In addition to line
195: * terminators, this method considers the characters space
196: * (<code>' '</code>, <code>'\u0020'</code>), tab
197: * (<code>'\t'</code>, <code>'\u0009'</code>), and form feed
198: * (<code>'\f'</code>, <code>'\u000C'</code>) to be white
199: * space.
200: *
201: * <p>
202: * If a logical line is spread across several natural lines, the
203: * backslash escaping the line terminator sequence, the line
204: * terminator sequence, and any white space at the start the
205: * following line have no affect on the key or element values.
206: * The remainder of the discussion of key and element parsing will
207: * assume all the characters constituting the key and element
208: * appear on a single natural line after line continuation
209: * characters have been removed. Note that it is <i>not</i>
210: * sufficient to only examine the character preceding a line
211: * terminator sequence to to see if the line terminator is
212: * escaped; there must be an odd number of contiguous backslashes
213: * for the line terminator to be escaped. Since the input is
214: * processed from left to right, a non-zero even number of
215: * 2<i>n</i> contiguous backslashes before a line terminator (or
216: * elsewhere) encodes <i>n</i> backslashes after escape
217: * processing.
218: *
219: * <p>
220: * The key contains all of the characters in the line starting
221: * with the first non-white space character and up to, but not
222: * including, the first unescaped <code>'='</code>,
223: * <code>':'</code>, or white space character other than a line
224: * terminator. All of these key termination characters may be
225: * included in the key by escaping them with a preceding backslash
226: * character; for example,<p>
227: *
228: * <code>\:\=</code><p>
229: *
230: * would be the two-character key <code>":="</code>. Line
231: * terminator characters can be included using <code>\r</code> and
232: * <code>\n</code> escape sequences. Any white space after the
233: * key is skipped; if the first non-white space character after
234: * the key is <code>'='</code> or <code>':'</code>, then it is
235: * ignored and any white space characters after it are also
236: * skipped. All remaining characters on the line become part of
237: * the associated element string; if there are no remaining
238: * characters, the element is the empty string
239: * <code>""</code>. Once the raw character sequences
240: * constituting the key and element are identified, escape
241: * processing is performed as described above.
242: *
243: * <p>
244: * As an example, each of the following three lines specifies the key
245: * <code>"Truth"</code> and the associated element value
246: * <code>"Beauty"</code>:
247: * <p>
248: * <pre>
249: * Truth = Beauty
250: * Truth:Beauty
251: * Truth :Beauty
252: * </pre>
253: * As another example, the following three lines specify a single
254: * property:
255: * <p>
256: * <pre>
257: * fruits apple, banana, pear, \
258: * cantaloupe, watermelon, \
259: * kiwi, mango
260: * </pre>
261: * The key is <code>"fruits"</code> and the associated element is:
262: * <p>
263: * <pre>"apple, banana, pear, cantaloupe, watermelon, kiwi, mango"</pre>
264: * Note that a space appears before each <code>\</code> so that a space
265: * will appear after each comma in the final result; the <code>\</code>,
266: * line terminator, and leading white space on the continuation line are
267: * merely discarded and are <i>not</i> replaced by one or more other
268: * characters.
269: * <p>
270: * As a third example, the line:
271: * <p>
272: * <pre>cheeses
273: * </pre>
274: * specifies that the key is <code>"cheeses"</code> and the associated
275: * element is the empty string <code>""</code>.<p>
276: *
277: * @param inStream the input stream.
278: * @exception IOException if an error occurred when reading from the
279: * input stream.
280: * @throws IllegalArgumentException if the input stream contains a
281: * malformed Unicode escape sequence.
282: */
283: public synchronized void load(InputStream inStream)
284: throws IOException {
285:
286: BufferedReader in = new BufferedReader(new InputStreamReader(
287: inStream, "8859_1"));
288: while (true) {
289: // Get next line
290: String line = in.readLine();
291: if (line == null)
292: return;
293:
294: if (line.length() > 0) {
295:
296: // Find start of key
297: int len = line.length();
298: int keyStart;
299: for (keyStart = 0; keyStart < len; keyStart++)
300: if (whiteSpaceChars.indexOf(line.charAt(keyStart)) == -1)
301: break;
302:
303: // Blank lines are ignored
304: if (keyStart == len)
305: continue;
306:
307: // Continue lines that end in slashes if they are not comments
308: char firstChar = line.charAt(keyStart);
309: if ((firstChar != '#') && (firstChar != '!')) {
310: while (continueLine(line)) {
311: String nextLine = in.readLine();
312: if (nextLine == null)
313: nextLine = "";
314: String loppedLine = line.substring(0, len - 1);
315: // Advance beyond whitespace on new line
316: int startIndex;
317: for (startIndex = 0; startIndex < nextLine
318: .length(); startIndex++)
319: if (whiteSpaceChars.indexOf(nextLine
320: .charAt(startIndex)) == -1)
321: break;
322: nextLine = nextLine.substring(startIndex,
323: nextLine.length());
324: line = new String(loppedLine + nextLine);
325: len = line.length();
326: }
327:
328: // Find separation between key and value
329: int separatorIndex;
330: for (separatorIndex = keyStart; separatorIndex < len; separatorIndex++) {
331: char currentChar = line.charAt(separatorIndex);
332: if (currentChar == '\\')
333: separatorIndex++;
334: else if (keyValueSeparators
335: .indexOf(currentChar) != -1)
336: break;
337: }
338:
339: // Skip over whitespace after key if any
340: int valueIndex;
341: for (valueIndex = separatorIndex; valueIndex < len; valueIndex++)
342: if (whiteSpaceChars.indexOf(line
343: .charAt(valueIndex)) == -1)
344: break;
345:
346: // Skip over one non whitespace key value separators if any
347: if (valueIndex < len)
348: if (strictKeyValueSeparators.indexOf(line
349: .charAt(valueIndex)) != -1)
350: valueIndex++;
351:
352: // Skip over white space after other separators if any
353: while (valueIndex < len) {
354: if (whiteSpaceChars.indexOf(line
355: .charAt(valueIndex)) == -1)
356: break;
357: valueIndex++;
358: }
359: String key = line.substring(keyStart,
360: separatorIndex);
361: String value = (separatorIndex < len) ? line
362: .substring(valueIndex, len) : "";
363:
364: // Convert then store key and value
365: key = loadConvert(key);
366: value = loadConvert(value);
367: put(key, value);
368: }
369: }
370: }
371: }
372:
373: /*
374: * Returns true if the given line is a line that must
375: * be appended to the next line
376: */
377: private boolean continueLine(String line) {
378: int slashCount = 0;
379: int index = line.length() - 1;
380: while ((index >= 0) && (line.charAt(index--) == '\\'))
381: slashCount++;
382: return (slashCount % 2 == 1);
383: }
384:
385: /*
386: * Converts encoded \uxxxx to unicode chars
387: * and changes special saved chars to their original forms
388: */
389: private String loadConvert(String theString) {
390: char aChar;
391: int len = theString.length();
392: StringBuffer outBuffer = new StringBuffer(len);
393:
394: for (int x = 0; x < len;) {
395: aChar = theString.charAt(x++);
396: if (aChar == '\\') {
397: aChar = theString.charAt(x++);
398: if (aChar == 'u') {
399: // Read the xxxx
400: int value = 0;
401: for (int i = 0; i < 4; i++) {
402: aChar = theString.charAt(x++);
403: switch (aChar) {
404: case '0':
405: case '1':
406: case '2':
407: case '3':
408: case '4':
409: case '5':
410: case '6':
411: case '7':
412: case '8':
413: case '9':
414: value = (value << 4) + aChar - '0';
415: break;
416: case 'a':
417: case 'b':
418: case 'c':
419: case 'd':
420: case 'e':
421: case 'f':
422: value = (value << 4) + 10 + aChar - 'a';
423: break;
424: case 'A':
425: case 'B':
426: case 'C':
427: case 'D':
428: case 'E':
429: case 'F':
430: value = (value << 4) + 10 + aChar - 'A';
431: break;
432: default:
433: throw new IllegalArgumentException(
434: "Malformed \\uxxxx encoding.");
435: }
436: }
437: outBuffer.append((char) value);
438: } else {
439: if (aChar == 't')
440: aChar = '\t';
441: else if (aChar == 'r')
442: aChar = '\r';
443: else if (aChar == 'n')
444: aChar = '\n';
445: else if (aChar == 'f')
446: aChar = '\f';
447: outBuffer.append(aChar);
448: }
449: } else
450: outBuffer.append(aChar);
451: }
452: return outBuffer.toString();
453: }
454:
455: /*
456: * Converts unicodes to encoded \uxxxx
457: * and writes out any of the characters in specialSaveChars
458: * with a preceding slash
459: */
460: private String saveConvert(String theString, boolean escapeSpace) {
461: int len = theString.length();
462: StringBuffer outBuffer = new StringBuffer(len * 2);
463:
464: for (int x = 0; x < len; x++) {
465: char aChar = theString.charAt(x);
466: switch (aChar) {
467: case ' ':
468: if (x == 0 || escapeSpace)
469: outBuffer.append('\\');
470:
471: outBuffer.append(' ');
472: break;
473: case '\\':
474: outBuffer.append('\\');
475: outBuffer.append('\\');
476: break;
477: case '\t':
478: outBuffer.append('\\');
479: outBuffer.append('t');
480: break;
481: case '\n':
482: outBuffer.append('\\');
483: outBuffer.append('n');
484: break;
485: case '\r':
486: outBuffer.append('\\');
487: outBuffer.append('r');
488: break;
489: case '\f':
490: outBuffer.append('\\');
491: outBuffer.append('f');
492: break;
493: default:
494: if ((aChar < 0x0020) || (aChar > 0x007e)) {
495: outBuffer.append('\\');
496: outBuffer.append('u');
497: outBuffer.append(toHex((aChar >> 12) & 0xF));
498: outBuffer.append(toHex((aChar >> 8) & 0xF));
499: outBuffer.append(toHex((aChar >> 4) & 0xF));
500: outBuffer.append(toHex(aChar & 0xF));
501: } else {
502: if (specialSaveChars.indexOf(aChar) != -1)
503: outBuffer.append('\\');
504: outBuffer.append(aChar);
505: }
506: }
507: }
508: return outBuffer.toString();
509: }
510:
511: /**
512: * Calls the <code>store(OutputStream out, String header)</code> method
513: * and suppresses IOExceptions that were thrown.
514: *
515: * @deprecated This method does not throw an IOException if an I/O error
516: * occurs while saving the property list. As of the Java 2 platform v1.2, the preferred
517: * way to save a properties list is via the <code>store(OutputStream out,
518: * String header)</code> method.
519: *
520: * @param out an output stream.
521: * @param header a description of the property list.
522: * @exception ClassCastException if this <code>Properties</code> object
523: * contains any keys or values that are not <code>Strings</code>.
524: */
525: public synchronized void save(OutputStream out, String header) {
526: try {
527: store(out, header);
528: } catch (IOException e) {
529: }
530: }
531:
532: /**
533: * Writes this property list (key and element pairs) in this
534: * <code>Properties</code> table to the output stream in a format suitable
535: * for loading into a <code>Properties</code> table using the
536: * {@link #load(InputStream) load} method.
537: * The stream is written using the ISO 8859-1 character encoding.
538: * <p>
539: * Properties from the defaults table of this <code>Properties</code>
540: * table (if any) are <i>not</i> written out by this method.
541: * <p>
542: * If the header argument is not null, then an ASCII <code>#</code>
543: * character, the header string, and a line separator are first written
544: * to the output stream. Thus, the <code>header</code> can serve as an
545: * identifying comment.
546: * <p>
547: * Next, a comment line is always written, consisting of an ASCII
548: * <code>#</code> character, the current date and time (as if produced
549: * by the <code>toString</code> method of <code>Date</code> for the
550: * current time), and a line separator as generated by the Writer.
551: * <p>
552: * Then every entry in this <code>Properties</code> table is
553: * written out, one per line. For each entry the key string is
554: * written, then an ASCII <code>=</code>, then the associated
555: * element string. Each character of the key and element strings
556: * is examined to see whether it should be rendered as an escape
557: * sequence. The ASCII characters <code>\</code>, tab, form feed,
558: * newline, and carriage return are written as <code>\\</code>,
559: * <code>\t</code>, <code>\f</code> <code>\n</code>, and
560: * <code>\r</code>, respectively. Characters less than
561: * <code>\u0020</code> and characters greater than
562: * <code>\u007E</code> are written as
563: * <code>\u</code><i>xxxx</i> for the appropriate hexadecimal
564: * value <i>xxxx</i>. For the key, all space characters are
565: * written with a preceding <code>\</code> character. For the
566: * element, leading space characters, but not embedded or trailing
567: * space characters, are written with a preceding <code>\</code>
568: * character. The key and element characters <code>#</code>,
569: * <code>!</code>, <code>=</code>, and <code>:</code> are written
570: * with a preceding backslash to ensure that they are properly loaded.
571: * <p>
572: * After the entries have been written, the output stream is flushed. The
573: * output stream remains open after this method returns.
574: *
575: * @param out an output stream.
576: * @param header a description of the property list.
577: * @exception IOException if writing this property list to the specified
578: * output stream throws an <tt>IOException</tt>.
579: * @exception ClassCastException if this <code>Properties</code> object
580: * contains any keys or values that are not <code>Strings</code>.
581: * @exception NullPointerException if <code>out</code> is null.
582: * @since 1.2
583: */
584: public synchronized void store(OutputStream out, String header)
585: throws IOException {
586: BufferedWriter awriter;
587: awriter = new BufferedWriter(new OutputStreamWriter(out,
588: "8859_1"));
589: if (header != null)
590: writeln(awriter, "#" + header);
591: writeln(awriter, "#" + new Date().toString());
592: for (Enumeration e = keys(); e.hasMoreElements();) {
593: String key = (String) e.nextElement();
594: String val = (String) get(key);
595: key = saveConvert(key, true);
596:
597: /* No need to escape embedded and trailing spaces for value, hence
598: * pass false to flag.
599: */
600: val = saveConvert(val, false);
601: writeln(awriter, key + "=" + val);
602: }
603: awriter.flush();
604: }
605:
606: private static void writeln(BufferedWriter bw, String s)
607: throws IOException {
608: bw.write(s);
609: bw.newLine();
610: }
611:
612: /**
613: * Searches for the property with the specified key in this property list.
614: * If the key is not found in this property list, the default property list,
615: * and its defaults, recursively, are then checked. The method returns
616: * <code>null</code> if the property is not found.
617: *
618: * @param key the property key.
619: * @return the value in this property list with the specified key value.
620: * @see #setProperty
621: * @see #defaults
622: */
623: public String getProperty(String key) {
624: Object oval = super .get(key);
625: String sval = (oval instanceof String) ? (String) oval : null;
626: return ((sval == null) && (defaults != null)) ? defaults
627: .getProperty(key) : sval;
628: }
629:
630: /**
631: * Searches for the property with the specified key in this property list.
632: * If the key is not found in this property list, the default property list,
633: * and its defaults, recursively, are then checked. The method returns the
634: * default value argument if the property is not found.
635: *
636: * @param key the hashtable key.
637: * @param defaultValue a default value.
638: *
639: * @return the value in this property list with the specified key value.
640: * @see #setProperty
641: * @see #defaults
642: */
643: public String getProperty(String key, String defaultValue) {
644: String val = getProperty(key);
645: return (val == null) ? defaultValue : val;
646: }
647:
648: /**
649: * Returns an enumeration of all the keys in this property list,
650: * including distinct keys in the default property list if a key
651: * of the same name has not already been found from the main
652: * properties list.
653: *
654: * @return an enumeration of all the keys in this property list, including
655: * the keys in the default property list.
656: * @see java.util.Enumeration
657: * @see java.util.Properties#defaults
658: */
659: public Enumeration propertyNames() {
660: Hashtable h = new Hashtable();
661: enumerate(h);
662: return h.keys();
663: }
664:
665: /**
666: * Prints this property list out to the specified output stream.
667: * This method is useful for debugging.
668: *
669: * @param out an output stream.
670: */
671: public void list(PrintStream out) {
672: out.println("-- listing properties --");
673: Hashtable h = new Hashtable();
674: enumerate(h);
675: for (Enumeration e = h.keys(); e.hasMoreElements();) {
676: String key = (String) e.nextElement();
677: String val = (String) h.get(key);
678: if (val.length() > 40) {
679: val = val.substring(0, 37) + "...";
680: }
681: out.println(key + "=" + val);
682: }
683: }
684:
685: /**
686: * Prints this property list out to the specified output stream.
687: * This method is useful for debugging.
688: *
689: * @param out an output stream.
690: * @since JDK1.1
691: */
692: /*
693: * Rather than use an anonymous inner class to share common code, this
694: * method is duplicated in order to ensure that a non-1.1 compiler can
695: * compile this file.
696: */
697: public void list(PrintWriter out) {
698: out.println("-- listing properties --");
699: Hashtable h = new Hashtable();
700: enumerate(h);
701: for (Enumeration e = h.keys(); e.hasMoreElements();) {
702: String key = (String) e.nextElement();
703: String val = (String) h.get(key);
704: if (val.length() > 40) {
705: val = val.substring(0, 37) + "...";
706: }
707: out.println(key + "=" + val);
708: }
709: }
710:
711: /**
712: * Enumerates all key/value pairs in the specified hastable.
713: * @param h the hashtable
714: */
715: private synchronized void enumerate(Hashtable h) {
716: if (defaults != null) {
717: defaults.enumerate(h);
718: }
719: for (Enumeration e = keys(); e.hasMoreElements();) {
720: String key = (String) e.nextElement();
721: h.put(key, get(key));
722: }
723: }
724:
725: /**
726: * Convert a nibble to a hex character
727: * @param nibble the nibble to convert.
728: */
729: private static char toHex(int nibble) {
730: return hexDigit[(nibble & 0xF)];
731: }
732:
733: /** A table of hex digits */
734: private static final char[] hexDigit = { '0', '1', '2', '3', '4',
735: '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
736: }
|