/************************************************************************** * Copyright (c) 2001 by Punch Telematix. All rights reserved. * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * 1. Redistributions of source code must retain the above copyright * * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * 3. Neither the name of Punch Telematix nor the names of * * other contributors may be used to endorse or promote products * * derived from this software without specific prior written permission.* * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * * IN NO EVENT SHALL PUNCH TELEMATIX OR OTHER CONTRIBUTORS BE LIABLE * * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **************************************************************************/ /* ** $Id: Properties.java,v 1.4 2006/04/18 11:35:28 cvs Exp $ ** */ package java.util; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; public class Properties extends Hashtable { private static final String keyValueSeparators = "= :\t\r\n\f"; private static final String commentChars = "#!"; private static final long serialVersionUID = 4112578634029874840L; protected Properties defaults; public Properties() { this(null); } public Properties(Properties deflts) { this.defaults = deflts; } public String getProperty(String key) { String val = (String) get(key); if (val == null && defaults != null){ val = defaults.getProperty(key); } return val; } public String getProperty(String key, String defaultValue){ String val = getProperty(key); return (val == null) ? defaultValue : val; } public Object setProperty(String key, String value){ return put(key, value); } /** * Copy all key/value pairs in a new hashtable merged with the * default values if any. */ private Hashtable flatten() { Hashtable h; if (defaults==null){ // Look out - this has to work with NativeProperties, which doesn't // implement many methods of Hashtable. Change at your peril! h = new Hashtable(); Object k; Object v; Enumeration e = keys(); while (e.hasMoreElements()) { k = e.nextElement(); v = get(k); h.put(k, v); } } else { h = defaults.flatten(); h.putAll(this); } return h; } public Enumeration propertyNames() { return this.flatten().keys(); } /** * Load from an input stream. */ public void load(InputStream in) throws IOException { int size = 1024; byte[] bytes = new byte[size]; char[] string = new char[size]; int stringSize = size; int have=0; int idx=1; boolean eol = false; boolean separated= false; boolean trim = true; do { //skip all whitespaces int ch; do { if (idx >= have) { have = in.read(bytes, 0, size); if (have == -1) { return; } idx = 0; } ch = bytes[idx]; if (ch < 0 || ch > 32){ break; } idx++; } while (true); //check for comment line ... if (idx >= have) { have = in.read(bytes, 0, size); if (have == -1) { return; } idx = 0; } boolean nocomment = commentChars.indexOf(bytes[idx]) == -1; //start parsing the key ... int chars = 0; do { if (idx >= have) { have = in.read(bytes, 0, size); if (have == -1) { if (chars > 0 && nocomment) { put(new String(string, 0, chars),""); } return; } idx = 0; } ch = bytes[idx++]; if (chars >= stringSize) { string = growByteArray(string); stringSize = string.length; } if (ch == '\\') { if (idx >= have) { have = in.read(bytes, 0, size); if (have == -1) { if (chars > 0 && nocomment) { string[chars++] = (char) (ch & 0xff); put(new String(string, 0, chars),""); } return; } idx = 0; } ch = bytes[idx++]; switch(ch){ case 'n': string[chars++] = '\n'; break; case 'r': string[chars++] = '\r'; break; case 't': string[chars++] = '\t'; break; case 'f': string[chars++] = '\f'; break; case '\r': if (idx >= have) { have = in.read(bytes, 0, size); if (have == -1) { if (chars > 0 && nocomment) { string[chars++] = (char) (ch & 0xff); put(new String(string, 0, chars),""); } return; } idx = 0; } if (bytes[idx] == '\n'){ idx++; } case '\n': //continuation. //[CG 20070522] comment lines cannot be extended using backslash if (!nocomment) { idx--; } break; case 'u': int unicode = 0; for(int k = 0; k < 4; k++){ if (idx >= have) { have = in.read(bytes, 0, size); if (have == -1) { if (chars > 0 && nocomment) { string[chars++] = (char) (ch & 0xff); put(new String(string, 0, chars),""); } return; } idx = 0; } unicode = unicode<<4 | fromHex(bytes[idx++]); } string[chars++] = (char)unicode; break; default: string[chars++] = (char)(ch & 0xff); break; } } else if (keyValueSeparators.indexOf(ch) != -1) { eol = (ch == '\n' || ch == '\r'); separated = (ch == '=' || ch == ':'); break; } else { string[chars++] = (char) (ch & 0xff); } } while(true); String key = new String(string, 0, chars); if(eol) { if(nocomment) { put(key,""); } eol = false; continue; } //strip all spaces before and/or after the separator if (!separated) { //no separator found yet separated = false; //trim all spaces and tabs do { if (idx >= have) { have = in.read(bytes, 0, size); if (have == -1) { if(nocomment) { put(new String(string, 0, chars),""); } return; } idx = 0; } ch = bytes[idx]; if (ch != ' ' && ch != '\t' && ch != '\f'){ break; } idx++; } while (true); //check if char is separator if(ch != ':' && ch != '='){ trim = false; } else { idx++; } } if (trim) { do { if (idx >= have) { have = in.read(bytes, 0, size); if (have == -1) { if(nocomment) { put(new String(string, 0, chars),""); } return; } idx = 0; } ch = bytes[idx]; if (ch != ' ' && ch != '\t' && ch != '\f'){ break; } idx++; } while (true); } else { trim = true; } //start parsing the value ... chars = 0; do { if (idx >= have) { have = in.read(bytes, 0, size); if (have == -1) { if (nocomment) { put(key, new String(string, 0, chars)); } return; } idx = 0; } ch = bytes[idx++]; if (chars >= stringSize) { string = growByteArray(string); stringSize = string.length; } if (ch == '\\') { if (idx >= have) { have = in.read(bytes, 0, size); if (have == -1) { if (nocomment) { string[chars++] = (char) (ch & 0xff); put(key, new String(string, 0, chars)); } return; } idx = 0; } ch = bytes[idx++]; switch(ch){ case 'n': string[chars++] = '\n'; break; case 'r': string[chars++] = '\r'; break; case 't': string[chars++] = '\t'; break; case 'f': string[chars++] = '\f'; break; case '\r': if (idx >= have) { have = in.read(bytes, 0, size); if (have == -1) { if (chars > 0 && nocomment) { string[chars++] = (char) (ch & 0xff); put(key, new String(string, 0, chars)); } return; } idx = 0; } if (bytes[idx] == '\n'){ idx++; } case '\n': //continuation. //[CG 20070522] comment lines cannot be extended using backslash if (!nocomment) { idx--; } break; case 'u': int unicode = 0; for(int k = 0; k < 4; k++){ if (idx >= have) { have = in.read(bytes, 0, size); if (have == -1) { if (chars > 0 && nocomment) { string[chars++] = (char) (ch & 0xff); put(key, new String(string, 0, chars)); } return; } idx = 0; } unicode = unicode<<4 | fromHex(bytes[idx++]); } string[chars++] = (char)unicode; break; default: string[chars++] = (char)(ch & 0xff); break; } } else if (ch == '\n') { if(nocomment) { put(key, new String(string, 0, chars)); } break; } else if (ch == '\r') { if(nocomment) { put(key, new String(string, 0, chars)); } if (idx >= have) { have = in.read(bytes, 0, size); if (have == -1) { return; } idx = 0; if (bytes[idx] == '\n'){ idx++; } } break; } else { string[chars++] = (char) (ch & 0xff); } } while(true); } while (true); } private char[] growByteArray(char[] bytes) { int l = bytes.length; char[] newBytes = new char[l*2]; System.arraycopy(bytes,0,newBytes,0,l); return newBytes; } public synchronized void save(OutputStream out, String header) { try { store(out, header); }catch(IOException ioe){} } public synchronized void store(OutputStream out, String header) throws IOException { //BufferedWriter bout = new BufferedWriter(new OutputStreamWriter(out)); byte[] bytes = new byte[1024]; int count = 0; if(header != null) { bytes[0] = (byte)'#'; out.write(bytes,0, addSlashes(header, out, bytes, '\n', 1)); } try { bytes[0] = (byte)'#'; String date = new Date().toString(); int l = date.length(); date.getBytes(0, l, bytes, 1); bytes[l+1] = (byte)'\n'; out.write(bytes,0, l+2); } catch (RuntimeException rt) {} Enumeration en = keys(); try { do { String key = (String) en.nextElement(); count = addSlashes(key,out, bytes, '=', count); count = addSlashes((String)get(key),out, bytes, '\n', count); } while (true); } catch (RuntimeException rt) {} out.write(bytes, 0, count); } private int addSlashes(String key, OutputStream out, byte[] bytes, int ch, int idx) throws IOException { int len = key.length(); char[] chars = new char[len]; key.getChars(0,len, chars,0); for(int i = 0 ; i < len ; i++){ int c = chars[i]; switch(c) { case '\t': bytes[idx++] = (byte)'\\'; bytes[idx++] = (byte)'t'; break; case '\r': bytes[idx++] = (byte)'\\'; bytes[idx++] = (byte)'r'; break; case '\n': bytes[idx++] = (byte)'\\'; bytes[idx++] = (byte)'n'; break; case '\f': bytes[idx++] = (byte)'\\'; bytes[idx++] = (byte)'f'; break; case '=': case ' ': case ':': case '\\': case '#': case '!': bytes[idx++] = (byte)'\\'; default: if(c < 0x20 || c > 0x7e) { bytes[idx++] = (byte)'\\'; bytes[idx++] = (byte)'u'; bytes[idx++] = toHex(c>>12); bytes[idx++] = toHex(c>>8); bytes[idx++] = toHex(c>>4); bytes[idx++] = toHex(c); } else { bytes[idx++] = (byte)c; } break; } if (idx >= 1018) { out.write(bytes,0,idx); idx = 0; } } bytes[idx++] = (byte)ch; return idx; } private int fromHex(int b) { if(b >= '0' && b <= '9') { return b - '0'; } if(b >= 'a' && b <= 'f') { return 10 + b - 'a'; } if(b >= 'A' && b <= 'F') { return 10 + b - 'A'; } return 0; } private byte toHex(int i) { int hex = '0' + (0x0f & i); if(hex > '9') { hex = ('A' - '9' - 1) + hex; } return (byte) hex; } public void list(PrintStream out) throws NullPointerException { out.println("-- listing properties --"); Hashtable h = flatten(); String key; String value; Enumeration en = h.keys(); while(en.hasMoreElements()) { Object o = en.nextElement(); key = (String)o; value = (String) h.get(key); if( value.length() > 40 ){ value = value.substring(0,37)+"..."; } out.println(key + "=" + value); } } public void list(PrintWriter out) throws NullPointerException { Hashtable h = flatten(); String key; String value; Enumeration en = h.keys(); while(en.hasMoreElements()) { key = (String) en.nextElement(); value = (String) h.get(key); if( value.length() > 40 ){ value = value.substring(0,37)+"..."; } out.println(key + "=" + value); } } }