1 /* 2 * $Id$ 3 * 4 * Copyright (c) 2006, 2009, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.interview; 28 29 import java.io.IOException; 30 import java.io.PrintWriter; 31 import java.io.Reader; 32 import java.io.Writer; 33 import java.util.Date; 34 import java.util.Enumeration; 35 import java.util.Hashtable; 36 37 /** 38 * The <code>Properties</code> class represents a persistent set of 39 * properties. The <code>Properties</code> can be saved to a stream 40 * or loaded from a stream. Each key and its corresponding value in 41 * the property list is a string. 42 * <p> 43 * A property list can contain another property list as its 44 * "defaults"; this second property list is searched if 45 * the property key is not found in the original property list. 46 * 47 * This class is similar to java.util.Properties, but has upgraded 48 * capabilities. 49 */ 50 51 public class Properties2 extends Hashtable<String, Object> { 52 /** 53 * A property list that contains default values for any keys not 54 * found in this property list. 55 */ 56 protected Properties2 defaults; 57 58 /** 59 * Creates an empty property list with no default values. 60 */ 61 public Properties2() { 62 this(null); 63 } 64 65 /** 66 * Creates an empty property list with the specified defaults. 67 * @param defaults the defaults. 68 */ 69 public Properties2(Properties2 defaults) { 70 this.defaults = defaults; 71 } 72 73 public void load(java.util.Properties source) { 74 Enumeration e = source.propertyNames(); 75 while(e.hasMoreElements()) { 76 Object next = e.nextElement(); 77 put( ((String)next), source.get(next) ); 78 } // while 79 } 80 81 82 /** 83 * Reads a property list from an input stream. 84 * 85 * @param in the input stream. 86 * @exception IOException if an error occurred when reading from the 87 * input stream. 88 */ 89 public synchronized void load(Reader in) throws IOException { 90 int ch = in.read(); 91 while (true) { 92 switch (ch) { 93 case -1: 94 return; 95 96 case '#': 97 case '!': 98 do { 99 ch = in.read(); 100 } while ((ch >= 0) && (ch != '\n') && (ch != '\r')); 101 continue; 102 103 case '\n': 104 case '\r': 105 case ' ': 106 case '\t': 107 ch = in.read(); 108 continue; 109 } 110 111 // Read the key 112 StringBuffer key = new StringBuffer(); 113 while ((ch >= 0) && (ch != '=') && (ch != ':') && 114 (ch != ' ') && (ch != '\t') && (ch != '\n') && (ch != '\r')) { 115 key.append((char)ch); 116 ch = in.read(); 117 } 118 while ((ch == ' ') || (ch == '\t')) { 119 ch = in.read(); 120 } 121 if ((ch == '=') || (ch == ':')) { 122 ch = in.read(); 123 } 124 while ((ch == ' ') || (ch == '\t')) { 125 ch = in.read(); 126 } 127 128 // Read the value 129 StringBuffer val = new StringBuffer(); 130 while ((ch >= 0) && (ch != '\n') && (ch != '\r')) { 131 int next = 0; 132 if (ch == '\\') { 133 switch (ch = in.read()) { 134 case '\r': 135 if (((ch = in.read()) == '\n') || 136 (ch == ' ') || (ch == '\t')) { 137 // fall thru to '\n' case 138 } else continue; 139 case '\n': 140 while (((ch = in.read()) == ' ') || (ch == '\t')); 141 continue; 142 case 't': ch = '\t'; next = in.read(); break; 143 case 'n': ch = '\n'; next = in.read(); break; 144 case 'r': ch = '\r'; next = in.read(); break; 145 case 'u': { 146 while ((ch = in.read()) == 'u'); 147 int d = 0; 148 loop: 149 for (int i = 0 ; i < 4 ; i++) { 150 next = in.read(); 151 switch (ch) { 152 case '0': case '1': case '2': case '3': case '4': 153 case '5': case '6': case '7': case '8': case '9': 154 d = (d << 4) + ch - '0'; 155 break; 156 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 157 d = (d << 4) + 10 + ch - 'a'; 158 break; 159 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 160 d = (d << 4) + 10 + ch - 'A'; 161 break; 162 default: 163 break loop; 164 } 165 ch = next; 166 } 167 ch = d; 168 break; 169 } 170 default: next = in.read(); break; 171 } 172 } else { 173 next = in.read(); 174 } 175 val.append((char)ch); 176 ch = next; 177 } 178 179 //System.out.println(key + " = '" + val + "'"); 180 put(key.toString(), val.toString()); 181 } 182 } 183 184 /** 185 * Stores this property list to the specified output stream. The 186 * string header is printed as a comment at the beginning of the stream. 187 * 188 * @param out an output stream. 189 * @param header a description of the property list. 190 */ 191 public synchronized void save(Writer out, String header) { 192 save(out, header, false); 193 } 194 195 private void save(Writer out, String header, boolean localize) { 196 PrintWriter prnt = (out instanceof PrintWriter ? (PrintWriter)out : 197 new PrintWriter(out, false)); 198 199 if (header != null) { 200 prnt.write('#'); 201 prnt.println(header); 202 } 203 prnt.write('#'); 204 prnt.println(new Date()); 205 206 for (Enumeration e = keys() ; e.hasMoreElements() ;) { 207 String key = (String)e.nextElement(); 208 prnt.print(key); 209 prnt.write('='); 210 211 String val = (String)get(key); 212 int len = val.length(); 213 boolean empty = false; 214 215 for (int i = 0 ; i < len ; i++) { 216 int ch = val.charAt(i); 217 218 switch (ch) { 219 case '\\': prnt.write('\\'); prnt.write('\\'); break; 220 case '\t': prnt.write('\\'); prnt.write('t'); break; 221 case '\n': prnt.write('\\'); prnt.write('n'); break; 222 case '\r': prnt.write('\\'); prnt.write('r'); break; 223 224 default: 225 if ((ch < ' ') || (ch >= 127) || (empty && (ch == ' '))) { 226 if ((ch > 255) && localize) { 227 prnt.write(ch); 228 } else { 229 prnt.write('\\'); 230 prnt.write('u'); 231 prnt.write(toHex((ch >> 12) & 0xF)); 232 prnt.write(toHex((ch >> 8) & 0xF)); 233 prnt.write(toHex((ch >> 4) & 0xF)); 234 prnt.write(toHex((ch >> 0) & 0xF)); 235 } 236 } else { 237 prnt.write(ch); 238 } 239 } 240 empty = false; 241 } 242 prnt.write('\n'); 243 } 244 } 245 246 /** 247 * Searches for the property with the specified key in this property list. 248 * If the key is not found in this property list, the default property list, 249 * and its defaults, recursively, are then checked. The method returns 250 * <code>null</code> if the property is not found. 251 * 252 * @param key the property key. 253 * @return the value in this property list with the specified key value. 254 * @see com.sun.interview.Properties2#defaults 255 */ 256 public String getProperty(String key) { 257 Object oval = super.get(key); 258 String sval = (oval instanceof String) ? (String)oval : null; 259 return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval; 260 } 261 262 /** 263 * Searches for the property with the specified key in this property list. 264 * If the key is not found in this property list, the default property list, 265 * and its defaults, recursively, are then checked. The method returns the 266 * default value argument if the property is not found. 267 * 268 * @param key the hashtable key. 269 * @param defaultValue a default value. 270 * 271 * @return the value in this property list with the specified key value. 272 * @see com.sun.interview.Properties2#defaults 273 */ 274 public String getProperty(String key, String defaultValue) { 275 String val = getProperty(key); 276 return (val == null) ? defaultValue : val; 277 } 278 279 /** 280 * Returns an enumeration of all the keys in this property list, including 281 * the keys in the default property list. 282 * 283 * @return an enumeration of all the keys in this property list, including 284 * the keys in the default property list. 285 * @see java.util.Enumeration 286 * @see com.sun.interview.Properties2#defaults 287 */ 288 public Enumeration propertyNames() { 289 Hashtable<String, Object> h = new Hashtable<>(); 290 enumerate(h); 291 return h.keys(); 292 } 293 294 295 /** 296 * Prints this property list out to the specified output stream. 297 * This method is useful for debugging. 298 * 299 * @param out an output stream. 300 */ 301 /* 302 * Rather than use an anonymous inner class to share common code, this 303 * method is duplicated in order to ensure that a non-1.1 compiler can 304 * compile this file. 305 */ 306 public void list(PrintWriter out) { 307 out.println("-- listing properties --"); 308 Hashtable<String, Object> h = new Hashtable<>(); 309 enumerate(h); 310 for (Enumeration e = h.keys() ; e.hasMoreElements() ;) { 311 String key = (String)e.nextElement(); 312 String val = (String)h.get(key); 313 if (val.length() > 40) { 314 val = val.substring(0, 37) + "..."; 315 } 316 out.println(key + "=" + val); 317 } 318 } 319 320 /** 321 * Enumerates all key/value pairs in the specified hastable. 322 * @param h the hashtable 323 */ 324 private synchronized void enumerate(Hashtable<String, Object> h) { 325 if (defaults != null) { 326 defaults.enumerate(h); 327 } 328 for (Enumeration e = keys() ; e.hasMoreElements() ;) { 329 String key = (String)e.nextElement(); 330 h.put(key, get(key)); 331 } 332 } 333 334 /** 335 * Convert a nibble to a hex character 336 * @param nibble the nibble to convert. 337 */ 338 private static char toHex(int nibble) { 339 return hexDigit[(nibble & 0xF)]; 340 } 341 342 /** A table of hex digits */ 343 private static char[] hexDigit = { 344 '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' 345 }; 346 }