17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util;
27
28 import java.io.IOException;
29 import java.io.PrintStream;
30 import java.io.PrintWriter;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.io.Reader;
34 import java.io.Writer;
35 import java.io.OutputStreamWriter;
36 import java.io.BufferedWriter;
37
38 import jdk.internal.util.xml.PropertiesDefaultHandler;
39
40 /**
41 * The {@code Properties} class represents a persistent set of
42 * properties. The {@code Properties} can be saved to a stream
43 * or loaded from a stream. Each key and its corresponding value in
44 * the property list is a string.
45 * <p>
46 * A property list can contain another property list as its
47 * "defaults"; this second property list is searched if
48 * the property key is not found in the original property list.
49 * <p>
50 * Because {@code Properties} inherits from {@code Hashtable}, the
51 * {@code put} and {@code putAll} methods can be applied to a
52 * {@code Properties} object. Their use is strongly discouraged as they
53 * allow the caller to insert entries whose keys or values are not
54 * {@code Strings}. The {@code setProperty} method should be used
55 * instead. If the {@code store} or {@code save} method is called
56 * on a "compromised" {@code Properties} object that contains a
127 * A property list that contains default values for any keys not
128 * found in this property list.
129 *
130 * @serial
131 */
132 protected Properties defaults;
133
134 /**
135 * Creates an empty property list with no default values.
136 */
137 public Properties() {
138 this(null);
139 }
140
141 /**
142 * Creates an empty property list with the specified defaults.
143 *
144 * @param defaults the defaults.
145 */
146 public Properties(Properties defaults) {
147 this.defaults = defaults;
148 }
149
150 /**
151 * Calls the <tt>Hashtable</tt> method {@code put}. Provided for
152 * parallelism with the <tt>getProperty</tt> method. Enforces use of
153 * strings for property keys and values. The value returned is the
154 * result of the <tt>Hashtable</tt> call to {@code put}.
155 *
156 * @param key the key to be placed into this property list.
157 * @param value the value corresponding to <tt>key</tt>.
158 * @return the previous value of the specified key in this property
159 * list, or {@code null} if it did not have one.
160 * @see #getProperty
161 * @since 1.2
162 */
163 public synchronized Object setProperty(String key, String value) {
164 return put(key, value);
165 }
166
167
168 /**
169 * Reads a property list (key and element pairs) from the input
170 * character stream in a simple line-oriented format.
171 * <p>
172 * Properties are processed in terms of lines. There are two
173 * kinds of line, <i>natural lines</i> and <i>logical lines</i>.
174 * A natural line is defined as a line of
175 * characters that is terminated either by a set of line terminator
176 * characters ({@code \n} or {@code \r} or {@code \r\n})
177 * or by the end of the stream. A natural line may be either a blank line,
178 * a comment line, or hold all or some of a key-element pair. A logical
179 * line holds all the data of a key-element pair, which may be spread
180 * out across several adjacent natural lines by escaping
181 * the line terminator sequence with a backslash character
182 * {@code \}. Note that a comment line cannot be extended
183 * in this manner; every natural line that is a comment must have
295 * <li> Escapes are not necessary for single and double quotes;
296 * however, by the rule above, single and double quote characters
297 * preceded by a backslash still yield single and double quote
298 * characters, respectively.
299 *
300 * <li> Only a single 'u' character is allowed in a Unicode escape
301 * sequence.
302 *
303 * </ul>
304 * <p>
305 * The specified stream remains open after this method returns.
306 *
307 * @param reader the input character stream.
308 * @throws IOException if an error occurred when reading from the
309 * input stream.
310 * @throws IllegalArgumentException if a malformed Unicode escape
311 * appears in the input.
312 * @throws NullPointerException if {@code reader} is null.
313 * @since 1.6
314 */
315 public synchronized void load(Reader reader) throws IOException {
316 Objects.requireNonNull(reader, "reader parameter is null");
317 load0(new LineReader(reader));
318 }
319
320 /**
321 * Reads a property list (key and element pairs) from the input
322 * byte stream. The input stream is in a simple line-oriented
323 * format as specified in
324 * {@link #load(java.io.Reader) load(Reader)} and is assumed to use
325 * the ISO 8859-1 character encoding; that is each byte is one Latin1
326 * character. Characters not in Latin1, and certain special characters,
327 * are represented in keys and elements using Unicode escapes as defined in
328 * section 3.3 of
329 * <cite>The Java™ Language Specification</cite>.
330 * <p>
331 * The specified stream remains open after this method returns.
332 *
333 * @param inStream the input stream.
334 * @exception IOException if an error occurred when reading from the
335 * input stream.
336 * @throws IllegalArgumentException if the input stream contains a
337 * malformed Unicode escape sequence.
338 * @throws NullPointerException if {@code inStream} is null.
339 * @since 1.2
340 */
341 public synchronized void load(InputStream inStream) throws IOException {
342 Objects.requireNonNull(inStream, "inStream parameter is null");
343 load0(new LineReader(inStream));
344 }
345
346 private void load0 (LineReader lr) throws IOException {
347 char[] convtBuf = new char[1024];
348 int limit;
349 int keyLen;
350 int valueStart;
351 char c;
352 boolean hasSep;
353 boolean precedingBackslash;
354
355 while ((limit = lr.readLine()) >= 0) {
356 c = 0;
357 keyLen = 0;
358 valueStart = limit;
359 hasSep = false;
360
361 //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
812 * contains any keys or values that are not {@code Strings}.
813 * @exception NullPointerException if {@code out} is null.
814 * @since 1.2
815 */
816 public void store(OutputStream out, String comments)
817 throws IOException
818 {
819 store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")),
820 comments,
821 true);
822 }
823
824 private void store0(BufferedWriter bw, String comments, boolean escUnicode)
825 throws IOException
826 {
827 if (comments != null) {
828 writeComments(bw, comments);
829 }
830 bw.write("#" + new Date().toString());
831 bw.newLine();
832 synchronized (this) {
833 for (Enumeration<?> e = keys(); e.hasMoreElements();) {
834 String key = (String)e.nextElement();
835 String val = (String)get(key);
836 key = saveConvert(key, true, escUnicode);
837 /* No need to escape embedded and trailing spaces for value, hence
838 * pass false to flag.
839 */
840 val = saveConvert(val, false, escUnicode);
841 bw.write(key + "=" + val);
842 bw.newLine();
843 }
844 }
845 bw.flush();
846 }
847
848 /**
849 * Loads all of the properties represented by the XML document on the
850 * specified input stream into this properties table.
851 *
852 * <p>The XML document must have the following DOCTYPE declaration:
853 * <pre>
854 * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
855 * </pre>
856 * Furthermore, the document must satisfy the properties DTD described
857 * above.
858 *
859 * <p> An implementation is required to read XML documents that use the
860 * "{@code UTF-8}" or "{@code UTF-16}" encoding. An implementation may
861 * support additional encodings.
862 *
863 * <p>The specified stream is closed after this method returns.
864 *
865 * @param in the input stream from which to read the XML document.
866 * @throws IOException if reading from the specified input stream
867 * results in an <tt>IOException</tt>.
868 * @throws java.io.UnsupportedEncodingException if the document's encoding
869 * declaration can be read and it specifies an encoding that is not
870 * supported
871 * @throws InvalidPropertiesFormatException Data on input stream does not
872 * constitute a valid XML document with the mandated document type.
873 * @throws NullPointerException if {@code in} is null.
874 * @see #storeToXML(OutputStream, String, String)
875 * @see <a href="http://www.w3.org/TR/REC-xml/#charencoding">Character
876 * Encoding in Entities</a>
877 * @since 1.5
878 */
879 public synchronized void loadFromXML(InputStream in)
880 throws IOException, InvalidPropertiesFormatException
881 {
882 Objects.requireNonNull(in);
883 PropertiesDefaultHandler handler = new PropertiesDefaultHandler();
884 handler.load(this, in);
885 in.close();
886 }
887
888 /**
889 * Emits an XML document representing all of the properties contained
890 * in this table.
891 *
892 * <p> An invocation of this method of the form <tt>props.storeToXML(os,
893 * comment)</tt> behaves in exactly the same way as the invocation
894 * <tt>props.storeToXML(os, comment, "UTF-8");</tt>.
895 *
896 * @param os the output stream on which to emit the XML document.
897 * @param comment a description of the property list, or {@code null}
898 * if no comment is desired.
899 * @throws IOException if writing to the specified output stream
954 throws IOException
955 {
956 Objects.requireNonNull(os);
957 Objects.requireNonNull(encoding);
958 PropertiesDefaultHandler handler = new PropertiesDefaultHandler();
959 handler.store(this, os, comment, encoding);
960 }
961
962 /**
963 * Searches for the property with the specified key in this property list.
964 * If the key is not found in this property list, the default property list,
965 * and its defaults, recursively, are then checked. The method returns
966 * {@code null} if the property is not found.
967 *
968 * @param key the property key.
969 * @return the value in this property list with the specified key value.
970 * @see #setProperty
971 * @see #defaults
972 */
973 public String getProperty(String key) {
974 Object oval = super.get(key);
975 String sval = (oval instanceof String) ? (String)oval : null;
976 return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
977 }
978
979 /**
980 * Searches for the property with the specified key in this property list.
981 * If the key is not found in this property list, the default property list,
982 * and its defaults, recursively, are then checked. The method returns the
983 * default value argument if the property is not found.
984 *
985 * @param key the hashtable key.
986 * @param defaultValue a default value.
987 *
988 * @return the value in this property list with the specified key value.
989 * @see #setProperty
990 * @see #defaults
991 */
992 public String getProperty(String key, String defaultValue) {
993 String val = getProperty(key);
994 return (val == null) ? defaultValue : val;
995 }
996
997 /**
998 * Returns an enumeration of all the keys in this property list,
999 * including distinct keys in the default property list if a key
1000 * of the same name has not already been found from the main
1001 * properties list.
1002 *
1003 * @return an enumeration of all the keys in this property list, including
1004 * the keys in the default property list.
1005 * @throws ClassCastException if any key in this property list
1006 * is not a string.
1007 * @see java.util.Enumeration
1008 * @see java.util.Properties#defaults
1009 * @see #stringPropertyNames
1010 */
1011 public Enumeration<?> propertyNames() {
1012 Hashtable<String,Object> h = new Hashtable<>();
1013 enumerate(h);
1014 return h.keys();
1015 }
1016
1017 /**
1018 * Returns a set of keys in this property list where
1019 * the key and its corresponding value are strings,
1020 * including distinct keys in the default property list if a key
1021 * of the same name has not already been found from the main
1022 * properties list. Properties whose key or value is not
1023 * of type <tt>String</tt> are omitted.
1024 * <p>
1025 * The returned set is not backed by the <tt>Properties</tt> object.
1026 * Changes to this <tt>Properties</tt> are not reflected in the set,
1027 * or vice versa.
1028 *
1029 * @return a set of keys in this property list where
1030 * the key and its corresponding value are strings,
1031 * including the keys in the default property list.
1032 * @see java.util.Properties#defaults
1033 * @since 1.6
1034 */
1035 public Set<String> stringPropertyNames() {
1036 Hashtable<String, String> h = new Hashtable<>();
1037 enumerateStringProperties(h);
1038 return h.keySet();
1039 }
1040
1041 /**
1042 * Prints this property list out to the specified output stream.
1043 * This method is useful for debugging.
1044 *
1045 * @param out an output stream.
1046 * @throws ClassCastException if any key in this property list
1047 * is not a string.
1048 */
1049 public void list(PrintStream out) {
1050 out.println("-- listing properties --");
1051 Hashtable<String,Object> h = new Hashtable<>();
1052 enumerate(h);
1053 for (Enumeration<String> e = h.keys() ; e.hasMoreElements() ;) {
1054 String key = e.nextElement();
1055 String val = (String)h.get(key);
1056 if (val.length() > 40) {
1057 val = val.substring(0, 37) + "...";
1058 }
1059 out.println(key + "=" + val);
1060 }
1061 }
1062
1063 /**
1064 * Prints this property list out to the specified output stream.
1065 * This method is useful for debugging.
1066 *
1067 * @param out an output stream.
1068 * @throws ClassCastException if any key in this property list
1069 * is not a string.
1070 * @since 1.1
1071 */
1072 /*
1073 * Rather than use an anonymous inner class to share common code, this
1074 * method is duplicated in order to ensure that a non-1.1 compiler can
1075 * compile this file.
1076 */
1077 public void list(PrintWriter out) {
1078 out.println("-- listing properties --");
1079 Hashtable<String,Object> h = new Hashtable<>();
1080 enumerate(h);
1081 for (Enumeration<String> e = h.keys() ; e.hasMoreElements() ;) {
1082 String key = e.nextElement();
1083 String val = (String)h.get(key);
1084 if (val.length() > 40) {
1085 val = val.substring(0, 37) + "...";
1086 }
1087 out.println(key + "=" + val);
1088 }
1089 }
1090
1091 /**
1092 * Enumerates all key/value pairs in the specified hashtable.
1093 * @param h the hashtable
1094 * @throws ClassCastException if any of the property keys
1095 * is not of String type.
1096 */
1097 private synchronized void enumerate(Hashtable<String,Object> h) {
1098 if (defaults != null) {
1099 defaults.enumerate(h);
1100 }
1101 for (Enumeration<?> e = keys() ; e.hasMoreElements() ;) {
1102 String key = (String)e.nextElement();
1103 h.put(key, get(key));
1104 }
1105 }
1106
1107 /**
1108 * Enumerates all key/value pairs in the specified hashtable
1109 * and omits the property if the key or value is not a string.
1110 * @param h the hashtable
1111 */
1112 private synchronized void enumerateStringProperties(Hashtable<String, String> h) {
1113 if (defaults != null) {
1114 defaults.enumerateStringProperties(h);
1115 }
1116 for (Enumeration<?> e = keys() ; e.hasMoreElements() ;) {
1117 Object k = e.nextElement();
1118 Object v = get(k);
1119 if (k instanceof String && v instanceof String) {
1120 h.put((String) k, (String) v);
1121 }
1122 }
1123 }
1124
1125 /**
1126 * Convert a nibble to a hex character
1127 * @param nibble the nibble to convert.
1128 */
1129 private static char toHex(int nibble) {
1130 return hexDigit[(nibble & 0xF)];
1131 }
1132
1133 /** A table of hex digits */
1134 private static final char[] hexDigit = {
1135 '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
1136 };
1137 }
|
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util;
27
28 import java.io.IOException;
29 import java.io.PrintStream;
30 import java.io.PrintWriter;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.io.Reader;
34 import java.io.Writer;
35 import java.io.OutputStreamWriter;
36 import java.io.BufferedWriter;
37 import java.io.ObjectInputStream;
38 import java.io.ObjectOutputStream;
39 import java.util.concurrent.ConcurrentHashMap;
40 import java.util.function.BiConsumer;
41 import java.util.function.BiFunction;
42 import java.util.function.Function;
43
44 import jdk.internal.util.xml.PropertiesDefaultHandler;
45
46 /**
47 * The {@code Properties} class represents a persistent set of
48 * properties. The {@code Properties} can be saved to a stream
49 * or loaded from a stream. Each key and its corresponding value in
50 * the property list is a string.
51 * <p>
52 * A property list can contain another property list as its
53 * "defaults"; this second property list is searched if
54 * the property key is not found in the original property list.
55 * <p>
56 * Because {@code Properties} inherits from {@code Hashtable}, the
57 * {@code put} and {@code putAll} methods can be applied to a
58 * {@code Properties} object. Their use is strongly discouraged as they
59 * allow the caller to insert entries whose keys or values are not
60 * {@code Strings}. The {@code setProperty} method should be used
61 * instead. If the {@code store} or {@code save} method is called
62 * on a "compromised" {@code Properties} object that contains a
133 * A property list that contains default values for any keys not
134 * found in this property list.
135 *
136 * @serial
137 */
138 protected Properties defaults;
139
140 /**
141 * Creates an empty property list with no default values.
142 */
143 public Properties() {
144 this(null);
145 }
146
147 /**
148 * Creates an empty property list with the specified defaults.
149 *
150 * @param defaults the defaults.
151 */
152 public Properties(Properties defaults) {
153 // use package-private constructor to
154 // initialize unused fields with dummy values
155 super((Void) null);
156 this.defaults = defaults;
157 }
158
159 /**
160 * Calls the <tt>Hashtable</tt> method {@code put}. Provided for
161 * parallelism with the <tt>getProperty</tt> method. Enforces use of
162 * strings for property keys and values. The value returned is the
163 * result of the <tt>Hashtable</tt> call to {@code put}.
164 *
165 * @param key the key to be placed into this property list.
166 * @param value the value corresponding to <tt>key</tt>.
167 * @return the previous value of the specified key in this property
168 * list, or {@code null} if it did not have one.
169 * @see #getProperty
170 * @since 1.2
171 */
172 public Object setProperty(String key, String value) {
173 return put(key, value);
174 }
175
176
177 /**
178 * Reads a property list (key and element pairs) from the input
179 * character stream in a simple line-oriented format.
180 * <p>
181 * Properties are processed in terms of lines. There are two
182 * kinds of line, <i>natural lines</i> and <i>logical lines</i>.
183 * A natural line is defined as a line of
184 * characters that is terminated either by a set of line terminator
185 * characters ({@code \n} or {@code \r} or {@code \r\n})
186 * or by the end of the stream. A natural line may be either a blank line,
187 * a comment line, or hold all or some of a key-element pair. A logical
188 * line holds all the data of a key-element pair, which may be spread
189 * out across several adjacent natural lines by escaping
190 * the line terminator sequence with a backslash character
191 * {@code \}. Note that a comment line cannot be extended
192 * in this manner; every natural line that is a comment must have
304 * <li> Escapes are not necessary for single and double quotes;
305 * however, by the rule above, single and double quote characters
306 * preceded by a backslash still yield single and double quote
307 * characters, respectively.
308 *
309 * <li> Only a single 'u' character is allowed in a Unicode escape
310 * sequence.
311 *
312 * </ul>
313 * <p>
314 * The specified stream remains open after this method returns.
315 *
316 * @param reader the input character stream.
317 * @throws IOException if an error occurred when reading from the
318 * input stream.
319 * @throws IllegalArgumentException if a malformed Unicode escape
320 * appears in the input.
321 * @throws NullPointerException if {@code reader} is null.
322 * @since 1.6
323 */
324 public void load(Reader reader) throws IOException {
325 Objects.requireNonNull(reader, "reader parameter is null");
326 load0(new LineReader(reader));
327 }
328
329 /**
330 * Reads a property list (key and element pairs) from the input
331 * byte stream. The input stream is in a simple line-oriented
332 * format as specified in
333 * {@link #load(java.io.Reader) load(Reader)} and is assumed to use
334 * the ISO 8859-1 character encoding; that is each byte is one Latin1
335 * character. Characters not in Latin1, and certain special characters,
336 * are represented in keys and elements using Unicode escapes as defined in
337 * section 3.3 of
338 * <cite>The Java™ Language Specification</cite>.
339 * <p>
340 * The specified stream remains open after this method returns.
341 *
342 * @param inStream the input stream.
343 * @exception IOException if an error occurred when reading from the
344 * input stream.
345 * @throws IllegalArgumentException if the input stream contains a
346 * malformed Unicode escape sequence.
347 * @throws NullPointerException if {@code inStream} is null.
348 * @since 1.2
349 */
350 public void load(InputStream inStream) throws IOException {
351 Objects.requireNonNull(inStream, "inStream parameter is null");
352 load0(new LineReader(inStream));
353 }
354
355 private void load0 (LineReader lr) throws IOException {
356 char[] convtBuf = new char[1024];
357 int limit;
358 int keyLen;
359 int valueStart;
360 char c;
361 boolean hasSep;
362 boolean precedingBackslash;
363
364 while ((limit = lr.readLine()) >= 0) {
365 c = 0;
366 keyLen = 0;
367 valueStart = limit;
368 hasSep = false;
369
370 //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
821 * contains any keys or values that are not {@code Strings}.
822 * @exception NullPointerException if {@code out} is null.
823 * @since 1.2
824 */
825 public void store(OutputStream out, String comments)
826 throws IOException
827 {
828 store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")),
829 comments,
830 true);
831 }
832
833 private void store0(BufferedWriter bw, String comments, boolean escUnicode)
834 throws IOException
835 {
836 if (comments != null) {
837 writeComments(bw, comments);
838 }
839 bw.write("#" + new Date().toString());
840 bw.newLine();
841 for (Map.Entry<Object, Object> e : entrySet()) {
842 String key = (String)e.getKey();
843 String val = (String)e.getValue();
844 key = saveConvert(key, true, escUnicode);
845 /* No need to escape embedded and trailing spaces for value, hence
846 * pass false to flag.
847 */
848 val = saveConvert(val, false, escUnicode);
849 bw.write(key + "=" + val);
850 bw.newLine();
851 }
852 bw.flush();
853 }
854
855 /**
856 * Loads all of the properties represented by the XML document on the
857 * specified input stream into this properties table.
858 *
859 * <p>The XML document must have the following DOCTYPE declaration:
860 * <pre>
861 * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
862 * </pre>
863 * Furthermore, the document must satisfy the properties DTD described
864 * above.
865 *
866 * <p> An implementation is required to read XML documents that use the
867 * "{@code UTF-8}" or "{@code UTF-16}" encoding. An implementation may
868 * support additional encodings.
869 *
870 * <p>The specified stream is closed after this method returns.
871 *
872 * @param in the input stream from which to read the XML document.
873 * @throws IOException if reading from the specified input stream
874 * results in an <tt>IOException</tt>.
875 * @throws java.io.UnsupportedEncodingException if the document's encoding
876 * declaration can be read and it specifies an encoding that is not
877 * supported
878 * @throws InvalidPropertiesFormatException Data on input stream does not
879 * constitute a valid XML document with the mandated document type.
880 * @throws NullPointerException if {@code in} is null.
881 * @see #storeToXML(OutputStream, String, String)
882 * @see <a href="http://www.w3.org/TR/REC-xml/#charencoding">Character
883 * Encoding in Entities</a>
884 * @since 1.5
885 */
886 public void loadFromXML(InputStream in)
887 throws IOException, InvalidPropertiesFormatException
888 {
889 Objects.requireNonNull(in);
890 PropertiesDefaultHandler handler = new PropertiesDefaultHandler();
891 handler.load(this, in);
892 in.close();
893 }
894
895 /**
896 * Emits an XML document representing all of the properties contained
897 * in this table.
898 *
899 * <p> An invocation of this method of the form <tt>props.storeToXML(os,
900 * comment)</tt> behaves in exactly the same way as the invocation
901 * <tt>props.storeToXML(os, comment, "UTF-8");</tt>.
902 *
903 * @param os the output stream on which to emit the XML document.
904 * @param comment a description of the property list, or {@code null}
905 * if no comment is desired.
906 * @throws IOException if writing to the specified output stream
961 throws IOException
962 {
963 Objects.requireNonNull(os);
964 Objects.requireNonNull(encoding);
965 PropertiesDefaultHandler handler = new PropertiesDefaultHandler();
966 handler.store(this, os, comment, encoding);
967 }
968
969 /**
970 * Searches for the property with the specified key in this property list.
971 * If the key is not found in this property list, the default property list,
972 * and its defaults, recursively, are then checked. The method returns
973 * {@code null} if the property is not found.
974 *
975 * @param key the property key.
976 * @return the value in this property list with the specified key value.
977 * @see #setProperty
978 * @see #defaults
979 */
980 public String getProperty(String key) {
981 Object oval = map.get(key);
982 String sval = (oval instanceof String) ? (String)oval : null;
983 return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
984 }
985
986 /**
987 * Searches for the property with the specified key in this property list.
988 * If the key is not found in this property list, the default property list,
989 * and its defaults, recursively, are then checked. The method returns the
990 * default value argument if the property is not found.
991 *
992 * @param key the hashtable key.
993 * @param defaultValue a default value.
994 *
995 * @return the value in this property list with the specified key value.
996 * @see #setProperty
997 * @see #defaults
998 */
999 public String getProperty(String key, String defaultValue) {
1000 String val = getProperty(key);
1001 return (val == null) ? defaultValue : val;
1002 }
1003
1004 /**
1005 * Returns an enumeration of all the keys in this property list,
1006 * including distinct keys in the default property list if a key
1007 * of the same name has not already been found from the main
1008 * properties list.
1009 *
1010 * @return an enumeration of all the keys in this property list, including
1011 * the keys in the default property list.
1012 * @throws ClassCastException if any key in this property list
1013 * is not a string.
1014 * @see java.util.Enumeration
1015 * @see java.util.Properties#defaults
1016 * @see #stringPropertyNames
1017 */
1018 public Enumeration<?> propertyNames() {
1019 Hashtable<String, Object> h = new Hashtable<>();
1020 enumerate(h);
1021 return h.keys();
1022 }
1023
1024 /**
1025 * Returns a set of keys in this property list where
1026 * the key and its corresponding value are strings,
1027 * including distinct keys in the default property list if a key
1028 * of the same name has not already been found from the main
1029 * properties list. Properties whose key or value is not
1030 * of type <tt>String</tt> are omitted.
1031 * <p>
1032 * The returned set is not backed by the <tt>Properties</tt> object.
1033 * Changes to this <tt>Properties</tt> are not reflected in the set,
1034 * or vice versa.
1035 *
1036 * @return a set of keys in this property list where
1037 * the key and its corresponding value are strings,
1038 * including the keys in the default property list.
1039 * @see java.util.Properties#defaults
1040 * @since 1.6
1041 */
1042 public Set<String> stringPropertyNames() {
1043 Map<String, String> h = new HashMap<>();
1044 enumerateStringProperties(h);
1045 return h.keySet();
1046 }
1047
1048 /**
1049 * Prints this property list out to the specified output stream.
1050 * This method is useful for debugging.
1051 *
1052 * @param out an output stream.
1053 * @throws ClassCastException if any key in this property list
1054 * is not a string.
1055 */
1056 public void list(PrintStream out) {
1057 out.println("-- listing properties --");
1058 Map<String, Object> h = new HashMap<>();
1059 enumerate(h);
1060 for (Map.Entry<String, Object> e : h.entrySet()) {
1061 String key = e.getKey();
1062 String val = (String)e.getValue();
1063 if (val.length() > 40) {
1064 val = val.substring(0, 37) + "...";
1065 }
1066 out.println(key + "=" + val);
1067 }
1068 }
1069
1070 /**
1071 * Prints this property list out to the specified output stream.
1072 * This method is useful for debugging.
1073 *
1074 * @param out an output stream.
1075 * @throws ClassCastException if any key in this property list
1076 * is not a string.
1077 * @since 1.1
1078 */
1079 /*
1080 * Rather than use an anonymous inner class to share common code, this
1081 * method is duplicated in order to ensure that a non-1.1 compiler can
1082 * compile this file.
1083 */
1084 public void list(PrintWriter out) {
1085 out.println("-- listing properties --");
1086 Map<String, Object> h = new HashMap<>();
1087 enumerate(h);
1088 for (Map.Entry<String, Object> e : h.entrySet()) {
1089 String key = e.getKey();
1090 String val = (String)e.getValue();
1091 if (val.length() > 40) {
1092 val = val.substring(0, 37) + "...";
1093 }
1094 out.println(key + "=" + val);
1095 }
1096 }
1097
1098 /**
1099 * Enumerates all key/value pairs in the specified Map.
1100 * @param h the Map
1101 * @throws ClassCastException if any of the property keys
1102 * is not of String type.
1103 */
1104 private void enumerate(Map<String, Object> h) {
1105 if (defaults != null) {
1106 defaults.enumerate(h);
1107 }
1108 for (Map.Entry<Object, Object> e : entrySet()) {
1109 String key = (String)e.getKey();
1110 h.put(key, e.getValue());
1111 }
1112 }
1113
1114 /**
1115 * Enumerates all key/value pairs in the specified Map
1116 * and omits the property if the key or value is not a string.
1117 * @param h the Map
1118 */
1119 private void enumerateStringProperties(Map<String, String> h) {
1120 if (defaults != null) {
1121 defaults.enumerateStringProperties(h);
1122 }
1123 for (Map.Entry<Object, Object> e : entrySet()) {
1124 Object k = e.getKey();
1125 Object v = e.getValue();
1126 if (k instanceof String && v instanceof String) {
1127 h.put((String) k, (String) v);
1128 }
1129 }
1130 }
1131
1132 /**
1133 * Convert a nibble to a hex character
1134 * @param nibble the nibble to convert.
1135 */
1136 private static char toHex(int nibble) {
1137 return hexDigit[(nibble & 0xF)];
1138 }
1139
1140 /** A table of hex digits */
1141 private static final char[] hexDigit = {
1142 '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
1143 };
1144
1145 //
1146 // Hashtable methods overridden and delegated to a ConcurrentHashMap instance
1147
1148 private transient ConcurrentHashMap<Object, Object> map =
1149 new ConcurrentHashMap<>(8);
1150
1151 @Override
1152 public int size() {
1153 return map.size();
1154 }
1155
1156 @Override
1157 public boolean isEmpty() {
1158 return map.isEmpty();
1159 }
1160
1161 @Override
1162 public Enumeration<Object> keys() {
1163 return map.keys();
1164 }
1165
1166 @Override
1167 public Enumeration<Object> elements() {
1168 return map.elements();
1169 }
1170
1171 @Override
1172 public boolean contains(Object value) {
1173 return map.contains(value);
1174 }
1175
1176 @Override
1177 public boolean containsValue(Object value) {
1178 return map.containsValue(value);
1179 }
1180
1181 @Override
1182 public boolean containsKey(Object key) {
1183 return map.containsKey(key);
1184 }
1185
1186 @Override
1187 public Object get(Object key) {
1188 return map.get(key);
1189 }
1190
1191 @Override
1192 public Object put(Object key, Object value) {
1193 return map.put(key, value);
1194 }
1195
1196 @Override
1197 public Object remove(Object key) {
1198 return map.remove(key);
1199 }
1200
1201 @Override
1202 public void putAll(Map<?, ?> t) {
1203 map.putAll(t);
1204 }
1205
1206 @Override
1207 public void clear() {
1208 map.clear();
1209 }
1210
1211 @Override
1212 public String toString() {
1213 return map.toString();
1214 }
1215
1216 @Override
1217 public Set<Object> keySet() {
1218 return map.keySet();
1219 }
1220
1221 @Override
1222 public Set<Map.Entry<Object, Object>> entrySet() {
1223 return map.entrySet();
1224 }
1225
1226 @Override
1227 public Collection<Object> values() {
1228 return map.values();
1229 }
1230
1231 @Override
1232 public boolean equals(Object o) {
1233 return map.equals(o);
1234 }
1235
1236 @Override
1237 public int hashCode() {
1238 return map.hashCode();
1239 }
1240
1241 @Override
1242 public Object getOrDefault(Object key, Object defaultValue) {
1243 return map.getOrDefault(key, defaultValue);
1244 }
1245
1246 @Override
1247 public void forEach(BiConsumer<? super Object, ? super Object> action) {
1248 map.forEach(action);
1249 }
1250
1251 @Override
1252 public void replaceAll(BiFunction<? super Object, ? super Object, ?> function) {
1253 map.replaceAll(function);
1254 }
1255
1256 @Override
1257 public Object putIfAbsent(Object key, Object value) {
1258 return map.putIfAbsent(key, value);
1259 }
1260
1261 @Override
1262 public boolean remove(Object key, Object value) {
1263 return map.remove(key, value);
1264 }
1265
1266 @Override
1267 public boolean replace(Object key, Object oldValue, Object newValue) {
1268 return map.replace(key, oldValue, newValue);
1269 }
1270
1271 @Override
1272 public Object replace(Object key, Object value) {
1273 return map.replace(key, value);
1274 }
1275
1276 @Override
1277 public Object computeIfAbsent(Object key,
1278 Function<? super Object, ?> mappingFunction) {
1279 return map.computeIfAbsent(key, mappingFunction);
1280 }
1281
1282 @Override
1283 public Object computeIfPresent(Object key,
1284 BiFunction<? super Object, ? super Object, ?> remappingFunction) {
1285 return map.computeIfPresent(key, remappingFunction);
1286 }
1287
1288 @Override
1289 public Object compute(Object key,
1290 BiFunction<? super Object, ? super Object, ?> remappingFunction) {
1291 return map.compute(key, remappingFunction);
1292 }
1293
1294 @Override
1295 public Object merge(Object key, Object value,
1296 BiFunction<? super Object, ? super Object, ?> remappingFunction) {
1297 return map.merge(key, value, remappingFunction);
1298 }
1299
1300 //
1301 // Special Hashtable methods
1302
1303 @Override
1304 protected void rehash() {
1305 // no-op
1306 }
1307
1308 @Override
1309 public Object clone() {
1310 Properties clone = (Properties) cloneHashtable();
1311 clone.map = new ConcurrentHashMap<>(map);
1312 return clone;
1313 }
1314
1315 //
1316 // Hashtable serialization overrides
1317 // (these should emit and consume Hashtable-compatible stream)
1318
1319 @Override
1320 void writeHashtable(ObjectOutputStream s) throws IOException {
1321 List<Object> entryStack = new ArrayList<>(map.size() * 2); // an estimate
1322
1323 for (Map.Entry<Object, Object> entry : map.entrySet()) {
1324 entryStack.add(entry.getValue());
1325 entryStack.add(entry.getKey());
1326 }
1327
1328 // Write out the simulated threshold, loadfactor
1329 float loadFactor = 0.75f;
1330 int count = entryStack.size() / 2;
1331 int length = (int)(count / loadFactor) + (count / 20) + 3;
1332 if (length > count && (length & 1) == 0) {
1333 length--;
1334 }
1335 synchronized (map) { // in case of multiple concurrent serializations
1336 defaultWriteHashtable(s, length, loadFactor);
1337 }
1338
1339 // Write out simulated length and real count of elements
1340 s.writeInt(length);
1341 s.writeInt(count);
1342
1343 // Write out the key/value objects from the stacked entries
1344 for (int i = entryStack.size() - 1; i >= 0; i--) {
1345 s.writeObject(entryStack.get(i));
1346 }
1347 }
1348
1349 @Override
1350 void readHashtable(ObjectInputStream s) throws IOException,
1351 ClassNotFoundException {
1352 // Read in the threshold and loadfactor
1353 s.defaultReadObject();
1354
1355 // Read the original length of the array and number of elements
1356 int origlength = s.readInt();
1357 int elements = s.readInt();
1358
1359 // create CHM of appropriate capacity
1360 map = new ConcurrentHashMap<>(elements);
1361
1362 // Read all the key/value objects
1363 for (; elements > 0; elements--) {
1364 Object key = s.readObject();
1365 Object value = s.readObject();
1366 map.put(key, value);
1367 }
1368 }
1369 }
|