001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.util;
011:
012: import java.io.BufferedInputStream;
013: import java.io.BufferedOutputStream;
014: import java.io.File;
015: import java.io.FileInputStream;
016: import java.io.FileNotFoundException;
017: import java.io.FileOutputStream;
018: import java.io.IOException;
019: import java.io.InputStream;
020: import java.io.RandomAccessFile;
021: import java.util.Date;
022: import java.util.Enumeration;
023: import java.util.Hashtable;
024: import java.util.Properties;
025: import java.util.StringTokenizer;
026: import java.util.Vector;
027:
028: import org.mmbase.util.logging.Logger;
029: import org.mmbase.util.logging.Logging;
030:
031: /**
032: * This is a flexible Properties version, it can handle saving of Properties with
033: * the comments that will stay in your file.
034: * @author Jan van Oosterom
035: * @version $Id: ExtendedProperties.java,v 1.11 2007/02/24 21:57:50 nklasens Exp $
036: */
037: public class ExtendedProperties extends Properties {
038: // logger
039: private static Logger log = Logging
040: .getLoggerInstance(ExtendedProperties.class.getName());
041:
042: /**
043: * The prefix of the comment in the Properties file.
044: * Everything after it will be treaded as comment
045: */
046: protected String commentPrefix = "#";
047:
048: /**
049: * Extended Properties constructor
050: */
051: public ExtendedProperties() {
052: }
053:
054: /**
055: * Create and read an ExtendedProperty from file.
056: * @param filename The file from were to read.
057: */
058: public ExtendedProperties(String filename) {
059: try {
060: getProps(filename);
061: } catch (IOException e) {
062: log.error("Failed to load the ExtendedProperties for: "
063: + filename);
064: }
065: }
066:
067: /**
068: * Create an ExtendedProperties with a Allready filled ExtendedProperty list.
069: * @param exProp The list that will be put in this ExtendedProperty.
070: */
071: public ExtendedProperties(ExtendedProperties exProp) {
072: // super((Properties) exProp);
073: super (exProp);
074: }
075:
076: /**
077: * Read from Properties and return them.
078: * @param filename The file from were to read the Properties.
079: */
080: public Hashtable<Object, Object> readProperties(String filename) {
081: clear();
082: try {
083: getProps(filename);
084: } catch (IOException e) {
085: log.debug("Failed to load the ExtendedProperties from: "
086: + filename, e);
087: }
088: ExtendedProperties propsToReturn = new ExtendedProperties();
089: Enumeration<?> e = keys();
090: while (e.hasMoreElements()) {
091: String s = (String) e.nextElement();
092: propsToReturn.put(s, get(s));
093: }
094: return propsToReturn;
095: }
096:
097: /**
098: * save Properties to file.
099: * @param filename The File were to save them
100: * @param propsToSave The Properties which to save.
101: */
102: public synchronized void saveProperties(String filename,
103: Hashtable<Object, Object> propsToSave) {
104: clear();
105: Enumeration<?> e = propsToSave.keys();
106: while (e.hasMoreElements()) {
107: String s = (String) e.nextElement();
108: put(s, propsToSave.get(s));//ROB
109: }
110: try {
111: save(filename);
112: } catch (IOException ioe) {
113: log.error("Fail to save the ExtendedProperties to: "
114: + filename + " : " + ioe);
115: }
116: }
117:
118: /**
119: * return a Vector of Strings which is parsed from a specified Property.
120: * @param whichProp The Property to get the list from.
121: * @param delimeter The delimeter to split wichProp's value with.
122: */
123: public Vector<String> getPropertyValues(String whichProp,
124: String delimeter) {
125: Vector<String> parsedPropsToReturn = new Vector<String>();
126: if (containsKey(whichProp)) {
127: //whichProp is available in this Property list
128: String value = (String) get(whichProp);
129: StringTokenizer tok = new StringTokenizer(value, delimeter);
130: while (tok.hasMoreTokens()) {
131: parsedPropsToReturn.addElement(tok.nextToken());
132: }
133: return parsedPropsToReturn;
134: } else {
135: //whichProp is not available in this Property list
136: //log.debug("ExtendedProperties.getParsedProperty: " + whichProp + " not found." );
137: return null;
138: }
139: }
140:
141: /**
142: * Read to this Property, the Properties from a file.
143: * @param filename The file were to read from
144: */
145: public void getProps(String filename) throws IOException {
146: try {
147: FileInputStream fileInputStream = new FileInputStream(
148: filename);
149: BufferedInputStream bufferedInputStream = new BufferedInputStream(
150: fileInputStream);
151: load(bufferedInputStream);
152: bufferedInputStream.close();
153: } catch (FileNotFoundException e) {
154: log.debug("ExtendedProperties:: file " + filename
155: + " not found");
156: }
157: }
158:
159: /**
160: * Loads properties from an InputStream.
161: * Uses "=" as delimeter between key and value.
162: * Does <B> not </B> uses a ":" as delimiter!
163: * @param in the input stream
164: * @exception IOException Error when reading from input stream.
165: */
166: public synchronized void load(InputStream in) throws IOException {
167: in = Runtime.getRuntime().getLocalizedInputStream(in);
168:
169: int ch = in.read();
170: while (ch != -1) {
171: switch (ch) {
172: case '#':
173: case '!':
174: do {
175: ch = in.read();
176: } while ((ch >= 0) && (ch != '\n') && (ch != '\r'));
177: continue;
178:
179: case '\n':
180: case '\r':
181: case ' ':
182: case '\t':
183: ch = in.read();
184: continue;
185: }
186:
187: // Read the key
188: StringBuffer key = new StringBuffer();
189:
190: while ((ch >= 0) && (ch != '=') && (ch != ' ')
191: && (ch != '\t') && (ch != '\n') && (ch != '\r')) {
192: key.append((char) ch);
193: ch = in.read();
194: }
195: while ((ch == ' ') && (ch == '\t')) {
196: ch = in.read();
197: }
198: if ((ch == '=') || (ch == ':')) {
199: ch = in.read();
200: }
201: while ((ch == ' ') && (ch == '\t')) {
202: ch = in.read();
203: }
204:
205: // Read the value
206: StringBuffer val = new StringBuffer();
207: while ((ch >= 0) && (ch != '\n') && (ch != '\r')) {
208: if (ch == '\\') {
209: switch (ch = in.read()) {
210: case '\r':
211: if (((ch = in.read()) == '\n') || (ch == ' ')
212: || (ch == '\t')) {
213: // fall thru to '\n' case
214: } else
215: continue;
216: case '\n':
217: while (((ch = in.read()) == ' ')
218: || (ch == '\t'))
219: ;
220: continue;
221: case 't':
222: ch = '\t';
223: break;
224: case 'n':
225: ch = '\n';
226: break;
227: case 'r':
228: ch = '\r';
229: break;
230: case 'u': {
231: while ((ch = in.read()) == 'u')
232: ;
233: int d = 0;
234: loop: for (int i = 0; i < 4; i++, ch = in
235: .read()) {
236: switch (ch) {
237: case '0':
238: case '1':
239: case '2':
240: case '3':
241: case '4':
242: case '5':
243: case '6':
244: case '7':
245: case '8':
246: case '9':
247: d = (d << 4) + ch - '0';
248: break;
249: case 'a':
250: case 'b':
251: case 'c':
252: case 'd':
253: case 'e':
254: case 'f':
255: d = (d << 4) + 10 + ch - 'a';
256: break;
257: case 'A':
258: case 'B':
259: case 'C':
260: case 'D':
261: case 'E':
262: case 'F':
263: d = (d << 4) + 10 + ch - 'A';
264: break;
265: default:
266: break loop;
267: }
268: }
269: ch = d;
270: }
271: }
272: }
273: val.append((char) ch);
274: ch = in.read();
275: }
276: put(key.toString(), val.toString());
277: }
278: }
279:
280: /**
281: * Warning: this routine destroys your comments in your properties file.
282: * But it save's your Properties (If that is all that U want :-)
283: * @param filename The File were to save your Properties
284: * @param header If you want you can speciefy a header (on top of you file)
285: */
286: private void write(String filename, String header)
287: throws IOException {
288: FileOutputStream fileOutputStream = new FileOutputStream(
289: filename);
290: BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
291: fileOutputStream);
292: save(bufferedOutputStream, header);
293: bufferedOutputStream.close();
294: }
295:
296: /**
297: * This routine does not only saves your Properties but also
298: * merges your comments if the file already exists
299: * if the file doesn't exists the will call the "normal" write routine.
300: * @param filename The file were to save to.
301: */
302: public synchronized void save(String filename) throws IOException {
303: File file = new File(filename);
304: boolean des = false; // for skipping enters when destroying some info
305: if (file.exists()) {
306: ExtendedProperties copyOfProps = (ExtendedProperties) this
307: .clone();
308: //the file exists so we have to merge
309: String newlines = "";
310: String lines = readOldProps(file);
311:
312: StringTokenizer tok = new StringTokenizer(lines, "\n", true);
313: while (tok.hasMoreTokens()) {
314: String line = (String) tok.nextElement();
315: // Is it a comment
316: if (line.startsWith(commentPrefix)) {
317: newlines = newlines + line;
318: } else {
319: int index = line.indexOf('=');
320: if (index == -1) {
321: //assuming a empty line ....
322: if (!des) {
323: newlines = newlines + "\n";
324: } else {
325: des = false;
326: }
327: } else {
328: //we found a new property value in the props file
329: String name = line.substring(0, index);
330:
331: if (containsKey(name)) {
332: //this Property is in memory so get this one from memory
333: newlines = newlines + name + "="
334: + getProperty(name);
335: //remove it from the copy
336: copyOfProps.remove(name);
337: } else {
338: //Thats odd, this one is not in memory
339: //Well we didn't used it so leave it there and use the old one
340: des = true;
341: /* newlines = newlines + line;*/
342: }
343:
344: }
345: }
346: }
347:
348: //everything that is left in the copy should be written also:
349: Enumeration<?> e = copyOfProps.keys();
350: while (e.hasMoreElements()) {
351: String name = (String) e.nextElement();
352: newlines = newlines + "\n" + name + "="
353: + copyOfProps.getProperty(name);
354: }
355: //newlines = newlines + "\n";
356:
357: file.delete();
358: //save the file:
359: RandomAccessFile writefile = new RandomAccessFile(file,
360: "rw");
361: writefile.writeBytes(newlines);
362: writefile.close();
363: } else {
364: //the file does not exists so we can use the simple write routine
365: //there is nothing to merge
366: write(filename, "");
367: }
368: }
369:
370: /**
371: * Read a file and returns everything in a String.
372: * @param file The File were to read from (has to exsists).
373: */
374: private String readOldProps(File file) throws IOException {
375: String line = "";
376:
377: String lines = "";
378: RandomAccessFile readfile = new RandomAccessFile(file, "r");
379: do {
380: try {
381: line = readfile.readLine();
382: if (line != null) {
383: lines = lines + line + "\n";
384: }
385: } catch (Exception e) {
386: log.error("EOF!");
387: }
388: } while (line != null);
389:
390: if (!lines.equals("")) {
391: lines = lines.substring(0, lines.length() - 1);
392: }
393: readfile.close();
394: return lines;
395: }
396:
397: /**
398: * Set the Property.
399: * @param name the name of the Property (the part in front of the '=' in the Property file)
400: * @param value the (new) value of the Property (the part after the '=' in the Property file)
401: */
402: public Object setProperty(String name, String value) {
403: return (put(name, value));
404: }
405:
406: /**
407: * Dump the contents of this Property to your screen (for debugging)
408: */
409: public void showContents() {
410: Enumeration<?> names = propertyNames();
411: while (names.hasMoreElements()) {
412: String name = (String) names.nextElement();
413: log.debug(name + "=" + getProperty(name));
414: }
415: }
416:
417: public synchronized String save() {
418: StringBuffer b = new StringBuffer();
419: b.append('#');
420: b.append(new Date());
421: b.append('\n');
422:
423: for (Enumeration<?> e = keys(); e.hasMoreElements();) {
424: String key = (String) e.nextElement();
425: b.append(key);
426: b.append('=');
427:
428: String val = (String) get(key);
429: int len = val.length();
430: if (len > 0)
431: b.append(val);
432: b.append('\n');
433: }
434: return (b.toString());
435: }
436: }
|