001: /*
002: * ====================================================================
003: * JAFFA - Java Application Framework For All
004: *
005: * Copyright (C) 2002 JAFFA Development Group
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * Redistribution and use of this software and associated documentation ("Software"),
022: * with or without modification, are permitted provided that the following conditions are met:
023: * 1. Redistributions of source code must retain copyright statements and notices.
024: * Redistributions must also contain a copy of this document.
025: * 2. Redistributions in binary form must reproduce the above copyright notice,
026: * this list of conditions and the following disclaimer in the documentation
027: * and/or other materials provided with the distribution.
028: * 3. The name "JAFFA" must not be used to endorse or promote products derived from
029: * this Software without prior written permission. For written permission,
030: * please contact mail to: jaffagroup@yahoo.com.
031: * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"
032: * appear in their names without prior written permission.
033: * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).
034: *
035: * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: */
049:
050: package org.jaffa.util;
051:
052: import java.util.*;
053: import java.io.*;
054:
055: /** This class combines the utility of the Properties class with a List. Features are:
056: * 1) Maintains the order of properties when loading them from an InputStream and when the properties are added manually.
057: * 2) Provides a sort capability for the properties
058: * 3) Provides a 'comments' attribute for each property
059: * 4) Stores the properties in an OutputStream, maintaining the order of the properties and the comments
060: * NOTE: Even though this class extends HashTable (since Properties extends HashTable), this implementation is not synchronized !!
061: */
062: public class ListProperties extends Properties {
063:
064: // ************************************
065: // *** FIELDS ***
066: // ************************************
067:
068: /** This Map will contain the key/value pairs.
069: * It will maintain the order in which the entries are made.
070: * The iterators will be obtained from this map.
071: */
072: private Map m_valueMap = new LinkedHashMap();
073:
074: /** This Map will contain the key/property pairs.
075: * The order is not important in this map.
076: * It is very much internal to the ListProperties class.
077: * NOTE: The iterators are obtained from the valueMap and hence this map may get out of sync !!
078: */
079: private Map m_propertyMap = new HashMap();
080:
081: /** This will be the contents of an InputStream after the last property entry
082: */
083: private String m_suffix = null;
084:
085: // ************************************
086: // *** CONSTRUCTORS ***
087: // ************************************
088:
089: /** Creates an empty property list with no default values. */
090: public ListProperties() {
091: super ();
092: }
093:
094: /** Creates an empty property list with the specified defaults.
095: * @param defaults The defaults.
096: */
097: public ListProperties(Properties defaults) {
098: super (defaults);
099: }
100:
101: // ************************************
102: // *** CUSTOM METHODS ***
103: // ************************************
104:
105: /** This sorts the Properties based on the keys.
106: */
107: public void sort() {
108: // Sort the existing keys
109: Set keys = new TreeSet(m_valueMap.keySet());
110:
111: // Now build a new LinkedHashMap
112: Map valueMap = new LinkedHashMap();
113: for (Iterator i = keys.iterator(); i.hasNext();) {
114: String key = (String) i.next();
115: valueMap.put(key, m_valueMap.get(key));
116: }
117:
118: // Assign the new Map to the field m_valueMap
119: m_valueMap.clear();
120: m_valueMap = valueMap;
121: }
122:
123: /** Returns the comments for the property.
124: * @param key The key used for adding the property.
125: * @return the comments for the property.
126: */
127: public String getComments(String key) {
128: String comments = null;
129: Property property = (Property) m_propertyMap.get(key);
130: if (property != null)
131: comments = property.getCommentsInFile();
132: return comments;
133: }
134:
135: /** Sets the comments for the property.
136: * Nothing will be done, in case there is no existing property for the input key.
137: * @param key The key used for adding the property.
138: * @param comments The comments for the property.
139: */
140: public void setComments(String key, String comments) {
141: Property property = (Property) m_propertyMap.get(key);
142: if (property != null)
143: property.setCommentsInFile(comments);
144: }
145:
146: /** Adds a new property, or updates if it already exists.
147: * @param key The key for the property.
148: * @param value The value for the property.
149: * @param comments The comment for the property.
150: * @return the previous value of the specified key in this property list, or null if it did not have one.
151: */
152: public Object setProperty(String key, String value, String comments) {
153: Property property = (Property) m_propertyMap.get(key);
154: if (property == null) {
155: property = new Property();
156: property.setKey(key);
157: property.setSeparatorInFile("=");
158: }
159: property.setValue(value);
160: property.setCommentsInFile(comments);
161: m_propertyMap.put(key, property);
162: return m_valueMap.put(key, value);
163: }
164:
165: /** Returns debug info.
166: * @return debug info.
167: */
168: public String toString() {
169: return m_valueMap.toString();
170: }
171:
172: // ************************************
173: // *** PROPERTIES METHODS ***
174: // ************************************
175:
176: /** Reads a property list (key and element pairs) from the input stream.
177: * @param inStream the input stream
178: * @throws IOException if an error occurred when reading from the input stream.
179: */
180: public void load(InputStream inStream) throws IOException {
181: PushbackReader reader = new PushbackReader(new BufferedReader(
182: new InputStreamReader(inStream)));
183: StringHelper.Line line = null; // holds each line from the file
184: StringBuffer commentsBlock = null; // reference to the buffer holding the comments before the current property
185: Property propertyBlock = null; // reference to the current Property object
186: boolean lineAfterContinuationChar = false; // True if the previous line ended with a '\'
187:
188: // Parse each line of the input file
189: while ((line = StringHelper.readLine(reader)) != null) {
190: boolean foundContinuationChar = false; // Set to true if a property-line ends with a '\'
191: String trimmedLine = line.getContents().trim();
192: if ((trimmedLine.length() == 0)
193: || (!lineAfterContinuationChar && ((trimmedLine
194: .startsWith("#") || trimmedLine
195: .startsWith("!"))))) {
196: // Add the existing Property object, if any, to the maps
197: if (propertyBlock != null) {
198: m_valueMap.put(propertyBlock.getKey(),
199: propertyBlock.getValue());
200: m_propertyMap.put(propertyBlock.getKey(),
201: propertyBlock);
202: propertyBlock = null;
203: }
204:
205: // Create a buffer for holding the comments
206: if (commentsBlock == null)
207: commentsBlock = new StringBuffer();
208:
209: // Append the line to the buffer
210: commentsBlock.append(line);
211:
212: } else { // This is the complex bit where we deal with the Property object
213: if (!lineAfterContinuationChar) {
214: // Add the existing Property object, if any, to the maps
215: if (propertyBlock != null) {
216: m_valueMap.put(propertyBlock.getKey(),
217: propertyBlock.getValue());
218: m_propertyMap.put(propertyBlock.getKey(),
219: propertyBlock);
220: propertyBlock = null;
221: }
222:
223: // Create a new Property object
224: propertyBlock = new Property();
225:
226: // Add the existing commentsBlock, if any, to the property
227: if (commentsBlock != null) {
228: propertyBlock.m_commentsInFile = commentsBlock;
229: commentsBlock = null;
230: }
231:
232: }
233:
234: for (int i = 0; i < line.getContents().length(); i++) {
235: char ch = line.getContents().charAt(i);
236: if (propertyBlock.m_separatorInFile.length() == 0) {
237: if (ch == ' ' || ch == '\t') {
238: if (propertyBlock.m_key.length() == 0
239: || lineAfterContinuationChar)
240: propertyBlock.m_keyInFile.append(ch);
241: else
242: propertyBlock.m_separatorInFile
243: .append(ch);
244: } else if (ch == '=' || ch == ':') {
245: propertyBlock.m_separatorInFile.append(ch);
246: } else if (ch == '\\') {
247: if (i == line.getContents().length() - 1) {
248: propertyBlock.m_keyInFile.append(ch);
249: foundContinuationChar = true;
250: } else {
251: char nextCh = line.getContents()
252: .charAt(++i);
253: propertyBlock.m_keyInFile.append(ch);
254: propertyBlock.m_keyInFile
255: .append(nextCh);
256: propertyBlock.m_key.append(nextCh);
257: }
258: } else {
259: propertyBlock.m_keyInFile.append(ch);
260: propertyBlock.m_key.append(ch);
261: }
262: } else if (propertyBlock.m_valueInFile.length() == 0) {
263: if (ch == ' ' || ch == '\t') {
264: propertyBlock.m_separatorInFile.append(ch);
265: } else if (ch == '\\') {
266: if (i == line.getContents().length() - 1) {
267: propertyBlock.m_valueInFile.append(ch);
268: foundContinuationChar = true;
269: } else {
270: char nextCh = line.getContents()
271: .charAt(++i);
272: propertyBlock.m_valueInFile.append(ch);
273: propertyBlock.m_valueInFile
274: .append(nextCh);
275: propertyBlock.m_value.append(nextCh);
276: }
277: } else {
278: propertyBlock.m_valueInFile.append(ch);
279: propertyBlock.m_value.append(ch);
280: }
281: } else {
282: if (ch == '\\') {
283: if (i == line.getContents().length() - 1) {
284: propertyBlock.m_valueInFile.append(ch);
285: foundContinuationChar = true;
286: } else {
287: char nextCh = line.getContents()
288: .charAt(++i);
289: propertyBlock.m_valueInFile.append(ch);
290: propertyBlock.m_valueInFile
291: .append(nextCh);
292: propertyBlock.m_value.append(nextCh);
293: }
294: } else {
295: propertyBlock.m_valueInFile.append(ch);
296: propertyBlock.m_value.append(ch);
297: }
298: }
299: }
300: // Append newline characters to the appropriate buffer in the Property object
301: if (propertyBlock.m_valueInFile.length() > 0)
302: propertyBlock.m_valueInFile.append(line.getEol());
303: else if (propertyBlock.m_separatorInFile.length() > 0)
304: propertyBlock.m_separatorInFile.append(line
305: .getEol());
306: else
307: propertyBlock.m_keyInFile.append(line.getEol());
308: }
309: lineAfterContinuationChar = foundContinuationChar;
310: }
311: // Add the existing Property object, if any, to the maps
312: if (propertyBlock != null) {
313: m_valueMap.put(propertyBlock.getKey(), propertyBlock
314: .getValue());
315: m_propertyMap.put(propertyBlock.getKey(), propertyBlock);
316: }
317: // The commentsBlock at the end of the file will be treated as the suffix
318: if (commentsBlock != null)
319: m_suffix = commentsBlock.toString();
320: }
321:
322: /** Adds a new property, or updates if it already exists.
323: * @param key The key for the property.
324: * @param value The value for the property.
325: * @return the previous value of the specified key in this property list, or null if it did not have one.
326: */
327: public Object setProperty(String key, String value) {
328: Property property = (Property) m_propertyMap.get(key);
329: if (property == null) {
330: property = new Property();
331: property.setKey(key);
332: property.setSeparatorInFile("=");
333: }
334: property.setValue(value);
335: m_propertyMap.put(key, property);
336: return m_valueMap.put(key, value);
337: }
338:
339: /** Searches for the property with the specified key in this property list. If the key is not found in this property list, the default property list, and its defaults, recursively, are then checked. The method returns null if the property is not found.
340: * @param key the property key.
341: * @return the value in this property list with the specified key value.
342: */
343: public String getProperty(String key) {
344: return getProperty(key, null);
345: }
346:
347: /** Searches for the property with the specified key in this property list. If the key is not found in this property list, the default property list, and its defaults, recursively, are then checked. The method returns the default value argument if the property is not found.
348: * @param key the property key.
349: * @param defaultValue a default value.
350: * @return the value in this property list with the specified key value.
351: */
352: public String getProperty(String key, String defaultValue) {
353: String value = (String) m_valueMap.get(key);
354: if (value == null && super .defaults != null)
355: value = super .defaults.getProperty(key);
356: if (value == null)
357: value = defaultValue;
358: return value;
359: }
360:
361: /** Returns an enumeration of all the keys in this property list, including distinct keys in the default property list if a key of the same name has not already been found from the main properties list.
362: * @return an enumeration of all the keys in this property list, including the keys in the default property list.
363: */
364: public Enumeration propertyNames() {
365: Vector propertyNames = new Vector(m_valueMap.keySet());
366: if (super .defaults != null) {
367: for (Enumeration enum = super .defaults.propertyNames(); enum.hasMoreElements(); ) {
368: String propertyName = (String) enum.nextElement();
369: if (!propertyNames.contains(propertyName))
370: propertyNames.add(propertyName);
371: }
372: }
373: return propertyNames.elements();
374: }
375:
376: /** Writes this property list (key and element pairs) in this Properties table to the output stream.
377: * Properties from the defaults table of this Properties table (if any) are not written out by this method.
378: * After the entries have been written, the output stream is flushed. The output stream remains open after this method returns.
379: * @param out an output stream.
380: * @param header a description of the property list.
381: * @throws IOException if writing this property list to the specified output stream throws an IOException.
382: */
383: public void store(OutputStream out, String header)
384: throws IOException {
385: BufferedWriter writer = new BufferedWriter(
386: new OutputStreamWriter(out));
387:
388: // First write the header
389: if (header != null) {
390: writer.write(header);
391: writer.newLine();
392: }
393:
394: // Now write all the properties
395: for (Iterator i = m_valueMap.keySet().iterator(); i.hasNext();) {
396: Property property = (Property) m_propertyMap.get(i.next());
397: if (property != null)
398: writer.write(property.getContents());
399: }
400:
401: // Finally write the suffix
402: if (m_suffix != null)
403: writer.write(m_suffix);
404:
405: writer.flush();
406: }
407:
408: /** Prints this property list out to the specified output stream. This method is useful for debugging.
409: * @param out an output stream.
410: */
411: public void list(PrintStream out) {
412: list(new PrintWriter(out));
413: }
414:
415: /** Prints this property list out to the specified output stream. This method is useful for debugging.
416: * @param out an output stream.
417: */
418: public void list(PrintWriter out) {
419: out.println("-- listing properties --");
420: for (Enumeration enum = propertyNames(); enum.hasMoreElements(); ) {
421: String propertyName = (String) enum.nextElement();
422: out.print(propertyName); out.print('='); out.println(getProperty(propertyName));
423: }
424: }
425:
426: // ************************************
427: // *** MAP INTERFACE METHODS ***
428: // ************************************
429:
430: /** Adds an object to the Map. If the map previously contained a mapping for this key, the old value is replaced by the specified value.
431: * An IllegalArgumentException is thrown if either of the key and value are not Strings.
432: * @param key The key used for adding the object.
433: * @param value The object to be added.
434: * @return previous value associated with specified key, or null if there was no mapping for key. A null return can also indicate that the map previously associated null with the specified key.
435: */
436: public Object put(Object key, Object value) {
437: if (!(key instanceof String) || !(value instanceof String))
438: throw new IllegalArgumentException(
439: "The key and value should be String");
440: return setProperty((String) key, (String) value);
441: }
442:
443: /** Removes the mapping for this key from this map if it is present.
444: * The default mappings are not touched.
445: * @param key key whose mapping is to be removed from the map.
446: * @return previous value associated with specified key, or null if there was no mapping for key.
447: */
448: public Object remove(Object key) {
449: m_propertyMap.remove(key);
450: return m_valueMap.remove(key);
451: }
452:
453: /** Returns a set view of the keys contained in this map.
454: * The keys from the default mappings are not part of the Set.
455: * @return a set view of the keys contained in this map.
456: */
457: public Set keySet() {
458: return m_valueMap.keySet();
459: }
460:
461: /** Removes all mappings from this map.
462: * The default mappings are not touched.
463: */
464: public void clear() {
465: m_propertyMap.clear();
466: m_valueMap.clear();
467: m_suffix = null;
468: }
469:
470: /** Returns a collection view of the values contained in this map.
471: * The values from the default mappings are not part of the Collection.
472: * @return a collection view of the values contained in this map.
473: */
474: public Collection values() {
475: return m_valueMap.values();
476: }
477:
478: /** Returns the hash code value for this map.
479: * The hashCode will not take into account the comments, if any.
480: * The default mappings do not affect the return value.
481: * @return the hash code value for this map.
482: */
483: public int hashCode() {
484: return m_valueMap.hashCode();
485: }
486:
487: /** Returns true if this map contains a mapping for the specified key.
488: * The default mappings are not searched for the key.
489: * @param key key whose presence in this map is to be tested.
490: * @return true if this map contains a mapping for the specified key.
491: */
492: public boolean containsKey(Object key) {
493: return m_valueMap.containsKey(key);
494: }
495:
496: /** Returns the number of key-value mappings in this map.
497: * The default mappings are not included in the result.
498: * @return the number of key-value mappings in this map.
499: */
500: public int size() {
501: return m_valueMap.size();
502: }
503:
504: /** Returns a set view of the mappings contained in this map.
505: * The entries from the default mappings are not part of the Set.
506: * @return a set view of the mappings contained in this map.
507: */
508: public Set entrySet() {
509: return m_valueMap.entrySet();
510: }
511:
512: /** Returns true if this map maps one or more keys to the specified value.
513: * The default mappings are not searched for the value.
514: * @param value value whose presence in this map is to be tested.
515: * @return true if this map maps one or more keys to the specified value.
516: */
517: public boolean containsValue(Object value) {
518: return m_valueMap.containsValue(value);
519: }
520:
521: /** Copies all of the mappings from the specified map to this map.
522: * This will invoke the put() method for each entry from the specified map.
523: * An IllegalArgumentException is thrown if either of the key and value are not Strings.
524: * @param t Mappings to be stored in this map.
525: */
526: public void putAll(Map t) {
527: if (t != null && t.size() > 0) {
528: for (Iterator i = t.entrySet().iterator(); i.hasNext();) {
529: Map.Entry me = (Map.Entry) i.next();
530: put(me.getKey(), me.getValue());
531: }
532: }
533: }
534:
535: /** Compares the specified object with this map for equality.
536: * Returns true if the given object is also a ListProperties and the two Properties represent the same mappings.
537: * The comments are not compared.
538: * The default mappings are not compared.
539: * @param o object to be compared for equality with this map.
540: * @return true if the specified object is equal to this map.
541: */
542: public boolean equals(Object o) {
543: boolean result = false;
544: if (o instanceof ListProperties) {
545: ListProperties listProperties = (ListProperties) o;
546: result = m_valueMap.equals(listProperties.m_valueMap);
547: }
548: return result;
549: }
550:
551: /** Returns true if this map contains no key-value mappings.
552: * The default mappings have no effect on the return value.
553: * @return true if this map contains no key-value mappings.
554: */
555: public boolean isEmpty() {
556: return m_valueMap.isEmpty();
557: }
558:
559: /** Returns the value to which this map maps the specified key.
560: * Returns null if the map contains no mapping for this key.
561: * A return value of null does not necessarily indicate that the map contains no mapping for the key; it's also possible that the map explicitly maps the key to null.
562: * The containsKey operation may be used to distinguish these two cases.
563: * The default mappings are not searched for the key.
564: * @param key key whose associated value is to be returned.
565: * @return the value to which this map maps the specified key, or null if the map contains no mapping for this key.
566: */
567: public Object get(Object key) {
568: return m_valueMap.get(key);
569: }
570:
571: // ************************************
572: // *** HASHTABLE METHODS ***
573: // ************************************
574:
575: /** Returns an enumeration of the values in this hashtable.
576: * Use the Enumeration methods on the returned object to fetch the elements sequentially.
577: * The values from the default mappings are not part of the Enumeration.
578: * @return an enumeration of the values in this hashtable.
579: */
580: public Enumeration elements() {
581: return new Vector(m_valueMap.values()).elements();
582: }
583:
584: /** Returns an enumeration of the keys in this hashtable.
585: * The keys from the default mappings are not part of the Enumeration.
586: * @return an enumeration of the keys in this hashtable.
587: */
588: public Enumeration keys() {
589: return new Vector(m_valueMap.keySet()).elements();
590: }
591:
592: // ************************************
593: // *** CLONEABLE INTERFACE METHODS ***
594: // ************************************
595:
596: /** Returns a clone of this object.
597: * @return a clone of the Map.
598: */
599: public Object clone() {
600: ListProperties obj = (ListProperties) super .clone();
601:
602: if (m_valueMap != null)
603: obj.m_valueMap = (Map) ((HashMap) m_valueMap).clone();
604:
605: if (m_propertyMap != null)
606: obj.m_propertyMap = (Map) ((HashMap) m_propertyMap).clone();
607:
608: obj.m_suffix = m_suffix;
609:
610: return obj;
611: }
612:
613: /** For the input file, a Property object encapsulates the key, value and comments for an entry.
614: */
615: private static class Property implements Cloneable, Serializable {
616:
617: private StringBuffer m_commentsInFile = new StringBuffer();
618: private StringBuffer m_key = new StringBuffer();
619: private StringBuffer m_keyInFile = new StringBuffer();
620: private StringBuffer m_value = new StringBuffer();
621: private StringBuffer m_valueInFile = new StringBuffer();
622: private StringBuffer m_separatorInFile = new StringBuffer();
623:
624: /** Getter for property commentsInFile.
625: * @return Value of property commentsInFile.
626: */
627: public String getCommentsInFile() {
628: return m_commentsInFile.toString();
629: }
630:
631: /** Getter for property key.
632: * @return Value of property key.
633: */
634: public String getKey() {
635: return m_key.toString();
636: }
637:
638: /** Getter for property keyInFile.
639: * @return Value of property keyInFile.
640: */
641: public String getKeyInFile() {
642: return m_keyInFile.toString();
643: }
644:
645: /** Getter for property value.
646: * @return Value of property value.
647: */
648: public String getValue() {
649: return m_value.toString();
650: }
651:
652: /** Getter for property valueInFile.
653: * @return Value of property valueInFile.
654: */
655: public String getValueInFile() {
656: return m_valueInFile.toString();
657: }
658:
659: /** Getter for property separatorInFile.
660: * @return Value of property separatorInFile.
661: */
662: public String getSeparatorInFile() {
663: return m_separatorInFile.toString();
664: }
665:
666: /** Getter for property contents.
667: * This concatenates the commentsInFile, keyInFile, separatorInFile and valueInFile.
668: * @return Value of property contents.
669: */
670: public String getContents() {
671: StringBuffer buf = new StringBuffer();
672: buf.append(m_commentsInFile);
673: buf.append(m_keyInFile);
674: buf.append(m_separatorInFile);
675: buf.append(m_valueInFile);
676: return buf.toString();
677: }
678:
679: /** Sets the key.
680: * This will also set the keyInFile, in case the new key is different from the existing key.
681: * @param key the key.
682: */
683: public void setKey(String key) {
684: String eol = StringHelper.findEol(m_keyInFile.toString());
685: if (key == null) {
686: m_key.setLength(0);
687: m_keyInFile.setLength(0);
688: if (eol != null)
689: m_keyInFile.append(eol);
690: } else if (!m_key.toString().equals(key)) {
691: m_key.setLength(0);
692: m_key.append(key);
693: m_keyInFile.setLength(0);
694: m_keyInFile.append(key);
695: if (eol != null)
696: m_keyInFile.append(eol);
697: }
698: }
699:
700: /** Sets the separatorInFile.
701: * @param separatorInFile the separatorInFile.
702: */
703: public void setSeparatorInFile(String separatorInFile) {
704: String eol = StringHelper.findEol(m_separatorInFile
705: .toString());
706: if (separatorInFile == null) {
707: m_separatorInFile.setLength(0);
708: if (eol != null)
709: m_separatorInFile.append(eol);
710: } else if (!m_separatorInFile.toString().equals(
711: separatorInFile)) {
712: m_separatorInFile.setLength(0);
713: m_separatorInFile.append(separatorInFile);
714: if (eol != null)
715: m_separatorInFile.append(eol);
716: }
717: }
718:
719: /** Sets the value.
720: * This will also set the valueInFile, in case the new value is different from the existing value.
721: * @param value the value.
722: */
723: public void setValue(String value) {
724: String eol = StringHelper.findEol(m_valueInFile.toString());
725: if (value == null) {
726: m_value.setLength(0);
727: m_valueInFile.setLength(0);
728: if (eol != null)
729: m_valueInFile.append(eol);
730: else
731: m_valueInFile.append(System
732: .getProperty("line.separator"));
733: } else if (!m_value.toString().equals(value)) {
734: m_value.setLength(0);
735: m_value.append(value);
736: m_valueInFile.setLength(0);
737: m_valueInFile.append(value);
738: if (eol != null)
739: m_valueInFile.append(eol);
740: else
741: m_valueInFile.append(System
742: .getProperty("line.separator"));
743: }
744: }
745:
746: /** Sets the commentsInFile.
747: * It'll add an initial '#' for each non-blank line, if it doesn't start with a '#' or a '!'
748: * @param commentsInFile the commentsInFile.
749: */
750: public void setCommentsInFile(String commentsInFile) {
751: m_commentsInFile.setLength(0);
752: if (commentsInFile != null) {
753: StringHelper.Line line = null;
754: PushbackReader reader = new PushbackReader(
755: new StringReader(commentsInFile));
756: try {
757: while ((line = StringHelper.readLine(reader)) != null) {
758: if (line.getContents() != null) {
759: String trimmedLine = line.getContents()
760: .trim();
761: // Add a '#' for a non-blank line, if it doesn't start with a '#' or a '!'
762: if (trimmedLine.length() > 0
763: && !trimmedLine.startsWith("#")
764: && !trimmedLine.startsWith("!"))
765: m_commentsInFile.append("#");
766: m_commentsInFile.append(line.getContents());
767: }
768: // Add an EndOfLine
769: if (line.getEol().length() > 0)
770: m_commentsInFile.append(line.getEol());
771: else
772: m_commentsInFile.append(System
773: .getProperty("line.separator"));
774: }
775: } catch (IOException e) {
776: // This should never happen
777: m_commentsInFile.setLength(0);
778: }
779: }
780: }
781:
782: /** Returns debug info.
783: * @return debug info.
784: */
785: public String toString() {
786: StringBuffer buf = new StringBuffer();
787: buf.append("<Property>");
788: buf.append("<commentsInFile>");
789: buf.append(m_commentsInFile);
790: buf.append("</commentsInFile>");
791: buf.append("<keyInFile>");
792: buf.append(m_keyInFile);
793: buf.append("</keyInFile>");
794: buf.append("<separatorInFile>");
795: buf.append(m_separatorInFile);
796: buf.append("</separatorInFile>");
797: buf.append("<valueInFile>");
798: buf.append(m_valueInFile);
799: buf.append("</valueInFile>");
800:
801: buf.append("<key>");
802: buf.append(m_key);
803: buf.append("</key>");
804: buf.append("<value>");
805: buf.append(m_value);
806: buf.append("</value>");
807: buf.append("</Property>");
808: return buf.toString();
809: }
810:
811: /** Returns a clone of this object.
812: * @throws CloneNotSupportedException if cloning is not supported. Should never happen.
813: * @return a clone of the Map.
814: */
815: public Object clone() throws CloneNotSupportedException {
816: Property obj = (Property) super .clone();
817: obj.m_commentsInFile = new StringBuffer(m_commentsInFile
818: .toString());
819: obj.m_key = new StringBuffer(m_key.toString());
820: obj.m_keyInFile = new StringBuffer(m_keyInFile.toString());
821: obj.m_value = new StringBuffer(m_value.toString());
822: obj.m_valueInFile = new StringBuffer(m_valueInFile
823: .toString());
824: obj.m_separatorInFile = new StringBuffer(m_separatorInFile
825: .toString());
826: return obj;
827: }
828:
829: }
830:
831: }
|