001: /* Copyright (c) 2001-2005, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb.persist;
032:
033: import java.io.FileNotFoundException;
034: import java.io.IOException;
035: import java.io.InputStream;
036: import java.io.OutputStream;
037: import java.util.Enumeration;
038: import java.util.Properties;
039:
040: import org.hsqldb.Trace;
041: import org.hsqldb.lib.ArrayUtil;
042: import org.hsqldb.lib.FileAccess;
043: import org.hsqldb.lib.FileUtil;
044: import org.hsqldb.lib.java.JavaSystem;
045:
046: /**
047: * Wrapper for java.util.Properties to limit values to Specific types and
048: * allow saving and loading.<p>
049: *
050: * @author fredt@users
051: * @version 1.7.2
052: * @since 1.7.0
053: */
054: public class HsqlProperties {
055:
056: public static final int NO_VALUE_FOR_KEY = 1;
057: protected String fileName;
058: protected Properties stringProps;
059: protected int[] errorCodes = new int[0];
060: protected String[] errorKeys = new String[0];
061: protected boolean resource = false;
062: protected FileAccess fa;
063:
064: public HsqlProperties() {
065: stringProps = new Properties();
066: fileName = null;
067: }
068:
069: public HsqlProperties(String name) {
070:
071: stringProps = new Properties();
072: fileName = name;
073: fa = FileUtil.getDefaultInstance();
074: }
075:
076: public HsqlProperties(String name, FileAccess accessor, boolean b) {
077:
078: stringProps = new Properties();
079: fileName = name;
080: resource = b;
081: fa = accessor;
082: }
083:
084: public HsqlProperties(Properties props) {
085: stringProps = props;
086: }
087:
088: public void setFileName(String name) {
089: fileName = name;
090: }
091:
092: public String setProperty(String key, int value) {
093: return setProperty(key, Integer.toString(value));
094: }
095:
096: public String setProperty(String key, boolean value) {
097: return setProperty(key, String.valueOf(value));
098: }
099:
100: public String setProperty(String key, String value) {
101: return (String) stringProps.put(key, value);
102: }
103:
104: public String setPropertyIfNotExists(String key, String value) {
105:
106: value = getProperty(key, value);
107:
108: return setProperty(key, value);
109: }
110:
111: public Properties getProperties() {
112: return stringProps;
113: }
114:
115: public String getProperty(String key) {
116: return stringProps.getProperty(key);
117: }
118:
119: public String getProperty(String key, String defaultValue) {
120: return stringProps.getProperty(key, defaultValue);
121: }
122:
123: public int getIntegerProperty(String key, int defaultValue) {
124:
125: String prop = getProperty(key);
126:
127: try {
128: if (prop != null) {
129: defaultValue = Integer.parseInt(prop);
130: }
131: } catch (NumberFormatException e) {
132: }
133:
134: return defaultValue;
135: }
136:
137: public int getIntegerProperty(String key, int defaultValue,
138: int minimum, int maximum) {
139:
140: String prop = getProperty(key);
141: boolean badvalue = false;
142:
143: try {
144: defaultValue = Integer.parseInt(prop);
145: } catch (NumberFormatException e) {
146: }
147:
148: if (defaultValue < minimum) {
149: defaultValue = minimum;
150: badvalue = true;
151: } else if (defaultValue > maximum) {
152: defaultValue = maximum;
153: badvalue = true;
154: }
155:
156: return defaultValue;
157: }
158:
159: /**
160: * Choice limited to values list, defaultValue must be in the values list.
161: */
162: public int getIntegerProperty(String key, int defaultValue,
163: int[] values) {
164:
165: String prop = getProperty(key);
166: int value = defaultValue;
167:
168: try {
169: if (prop != null) {
170: value = Integer.parseInt(prop);
171: }
172: } catch (NumberFormatException e) {
173: }
174:
175: if (ArrayUtil.find(values, value) == -1) {
176: return defaultValue;
177: }
178:
179: return value;
180: }
181:
182: public boolean isPropertyTrue(String key) {
183: return isPropertyTrue(key, false);
184: }
185:
186: public boolean isPropertyTrue(String key, boolean defaultValue) {
187:
188: String value = stringProps.getProperty(key);
189:
190: if (value == null) {
191: return defaultValue;
192: }
193:
194: return value.toLowerCase().equals("true");
195: }
196:
197: public void removeProperty(String key) {
198: stringProps.remove(key);
199: }
200:
201: public void addProperties(Properties props) {
202:
203: if (props == null) {
204: return;
205: }
206:
207: Enumeration keys = props.keys();
208:
209: while (keys.hasMoreElements()) {
210: Object key = keys.nextElement();
211:
212: this .stringProps.put(key, props.get(key));
213: }
214: }
215:
216: public void addProperties(HsqlProperties props) {
217:
218: if (props == null) {
219: return;
220: }
221:
222: addProperties(props.stringProps);
223: }
224:
225: // oj@openoffice.org
226: public boolean checkFileExists() throws IOException {
227:
228: String propFilename = fileName + ".properties";
229:
230: return fa.isStreamElement(propFilename);
231: }
232:
233: public boolean load() throws Exception {
234:
235: if (!checkFileExists()) {
236: return false;
237: }
238:
239: if (fileName == null || fileName.length() == 0) {
240: throw new FileNotFoundException(Trace
241: .getMessage(Trace.HsqlProperties_load));
242: }
243:
244: InputStream fis = null;
245: String propsFilename = fileName + ".properties";
246:
247: // oj@openoffice.org
248: try {
249: fis = resource ? getClass().getResourceAsStream(
250: propsFilename) : fa
251: .openInputStreamElement(propsFilename);
252:
253: stringProps.load(fis);
254: } finally {
255: if (fis != null) {
256: fis.close();
257: }
258: }
259:
260: return true;
261: }
262:
263: /**
264: * Saves the properties.
265: */
266: public void save() throws Exception {
267:
268: if (fileName == null || fileName.length() == 0) {
269: throw new java.io.FileNotFoundException(Trace
270: .getMessage(Trace.HsqlProperties_load));
271: }
272:
273: String filestring = fileName + ".properties";
274:
275: save(filestring);
276: }
277:
278: /**
279: * Saves the properties using JDK2 method if present, otherwise JDK1.
280: */
281: public void save(String fileString) throws Exception {
282:
283: // oj@openoffice.org
284: fa.createParentDirs(fileString);
285:
286: OutputStream fos = fa.openOutputStreamElement(fileString);
287:
288: JavaSystem
289: .saveProperties(
290: stringProps,
291: HsqlDatabaseProperties.PRODUCT_NAME
292: + " "
293: + HsqlDatabaseProperties.THIS_FULL_VERSION,
294: fos);
295: fos.close();
296:
297: return;
298: }
299:
300: /**
301: * Adds the error code and the key to the list of errors. This list
302: * is populated during construction or addition of elements and is used
303: * outside this class to act upon the errors.
304: */
305: private void addError(int code, String key) {
306:
307: errorCodes = (int[]) ArrayUtil.resizeArray(errorCodes,
308: errorCodes.length + 1);
309: errorKeys = (String[]) ArrayUtil.resizeArray(errorKeys,
310: errorKeys.length + 1);
311: errorCodes[errorCodes.length - 1] = code;
312: errorKeys[errorKeys.length - 1] = key;
313: }
314:
315: /**
316: * Creates and populates an HsqlProperties Object from the arguments
317: * array of a Main method. Properties are in the form of "-key value"
318: * pairs. Each key is prefixed with the type argument and a dot before
319: * being inserted into the properties Object. <p>
320: *
321: * "-?" is treated as a key with no value and not inserted.
322: */
323: public static HsqlProperties argArrayToProps(String[] arg,
324: String type) {
325:
326: HsqlProperties props = new HsqlProperties();
327:
328: for (int i = 0; i < arg.length; i++) {
329: String p = arg[i];
330:
331: if (p.startsWith("-?")) {
332: props.addError(NO_VALUE_FOR_KEY, p.substring(1));
333: } else if (p.charAt(0) == '-') {
334: props.setProperty(type + "." + p.substring(1),
335: arg[i + 1]);
336:
337: i++;
338: }
339: }
340:
341: return props;
342: }
343:
344: /**
345: * Creates and populates a new HsqlProperties Object using a string
346: * such as "key1=value1;key2=value2". <p>
347: *
348: * The string that represents the = sign above is specified as pairsep
349: * and the one that represents the semicolon is specified as delimiter,
350: * allowing any string to be used for either.<p>
351: *
352: * Leading / trailing spaces around the keys and values are discarded.<p>
353: *
354: * The string is parsed by (1) subdividing into segments by delimiter
355: * (2) subdividing each segment in two by finding the first instance of
356: * the pairsep (3) trimming each pair of segments from step 2 and
357: * inserting into the properties object.<p>
358: *
359: * Each key is prefixed with the type argument and a dot before being
360: * inserted.<p>
361: *
362: * Any key without a value is added to the list of errors.
363: */
364: public static HsqlProperties delimitedArgPairsToProps(String s,
365: String pairsep, String dlimiter, String type) {
366:
367: HsqlProperties props = new HsqlProperties();
368: int currentpair = 0;
369:
370: while (true) {
371: int nextpair = s.indexOf(dlimiter, currentpair);
372:
373: if (nextpair == -1) {
374: nextpair = s.length();
375: }
376:
377: // find value within the segment
378: int valindex = s.substring(0, nextpair).indexOf(pairsep,
379: currentpair);
380:
381: if (valindex == -1) {
382: props.addError(NO_VALUE_FOR_KEY, s.substring(
383: currentpair, nextpair).trim());
384: } else {
385: String key = s.substring(currentpair, valindex).trim();
386: String value = s.substring(valindex + pairsep.length(),
387: nextpair).trim();
388:
389: if (type != null) {
390: key = type + "." + key;
391: }
392:
393: props.setProperty(key, value);
394: }
395:
396: if (nextpair == s.length()) {
397: break;
398: }
399:
400: currentpair = nextpair + dlimiter.length();
401: }
402:
403: return props;
404: }
405:
406: public Enumeration propertyNames() {
407: return stringProps.propertyNames();
408: }
409:
410: public boolean isEmpty() {
411: return stringProps.isEmpty();
412: }
413:
414: public String[] getErrorKeys() {
415: return errorKeys;
416: }
417: /*
418: public static void main(String[] argv) {
419:
420: HsqlProperties props1 = delimitedArgPairsToProps("-dbname.0", "=",
421: ";", "server");
422: HsqlProperties props2 = delimitedArgPairsToProps(
423: "filename.cvs;a=123 ; b=\\delta ;c= another; derrorkey;", "=",
424: ";", "textdb");
425: }
426: */
427: }
|