001: /*--
002:
003: Copyright (C) 2000-2003 Anthony Eden.
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: 1. Redistributions of source code must retain the above copyright
011: notice, this list of conditions, and the following disclaimer.
012:
013: 2. Redistributions in binary form must reproduce the above copyright
014: notice, this list of conditions, and the disclaimer that follows
015: these conditions in the documentation and/or other materials
016: provided with the distribution.
017:
018: 3. The name "EdenLib" must not be used to endorse or promote products
019: derived from this software without prior written permission. For
020: written permission, please contact me@anthonyeden.com.
021:
022: 4. Products derived from this software may not be called "EdenLib", nor
023: may "EdenLib" appear in their name, without prior written permission
024: from Anthony Eden (me@anthonyeden.com).
025:
026: In addition, I request (but do not require) that you include in the
027: end-user documentation provided with the redistribution and/or in the
028: software itself an acknowledgement equivalent to the following:
029: "This product includes software developed by
030: Anthony Eden (http://www.anthonyeden.com/)."
031:
032: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
033: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
034: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
035: DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
036: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
037: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
038: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
039: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
040: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
041: IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
042: POSSIBILITY OF SUCH DAMAGE.
043:
044: For more information on EdenLib, please see <http://edenlib.sf.net/>.
045:
046: */
047:
048: package com.anthonyeden.lib.config;
049:
050: import org.znerd.xmlenc.XMLOutputter;
051:
052: import java.io.*;
053: import java.util.*;
054:
055: /**
056: * The ConfigurationBase is a base implementation of the MutableConfiguration
057: * interface.
058: *
059: * @author Anthony Eden
060: * @author <a href="mailto:florin.patrascu@gmail.com">Florin T.PATRASCU</a>
061: */
062:
063: public class ConfigurationBase implements MutableConfiguration {
064:
065: public static final String ENCODING = "UTF-8";
066: private static final String EMPTY_STRING = "";
067:
068: private String name;
069: private Object value;
070: private List children;
071: private Map attributes;
072: private Configuration parent;
073: private Location location;
074:
075: /**
076: * Construct a new ConfigurationBase object.
077: *
078: * @param name The configuration object name
079: * @param value The configuration object value
080: * @param parent The parent Configuration
081: */
082:
083: public ConfigurationBase(String name, Object value,
084: Configuration parent) {
085: this (name, value, new ArrayList(), new HashMap(), parent, null);
086: }
087:
088: /**
089: * Construct a new ConfigurationBase object.
090: *
091: * @param name The configuration object name
092: * @param value The configuration object value
093: * @param parent The parent Configuration
094: * @param location The Location object or null
095: */
096:
097: public ConfigurationBase(String name, Object value,
098: Configuration parent, Location location) {
099: this (name, value, new ArrayList(), new HashMap(), parent,
100: location);
101: }
102:
103: /**
104: * Construct a new ConfigurationBase object.
105: *
106: * @param name The configuration object name
107: * @param value The configuration object value
108: * @param children The List of children
109: * @param attributes The Map of attributes
110: * @param parent The parent Configuration
111: */
112:
113: public ConfigurationBase(String name, Object value, List children,
114: Map attributes, Configuration parent) {
115: this (name, value, children, attributes, parent, null);
116: }
117:
118: /**
119: * Construct a new ConfigurationBase object.
120: *
121: * @param name The configuration object name
122: * @param value The configuration object value
123: * @param children The List of children
124: * @param attributes The Map of attributes
125: * @param parent The parent Configuration
126: * @param location The Location or null
127: */
128:
129: public ConfigurationBase(String name, Object value, List children,
130: Map attributes, Configuration parent, Location location) {
131: this .name = name;
132: this .value = value;
133: this .children = children;
134: this .attributes = attributes;
135: this .parent = parent;
136: this .location = location;
137: }
138:
139: /**
140: * Get the node's name.
141: *
142: * @return The node's name
143: */
144:
145: public String getName() {
146: return name;
147: }
148:
149: /**
150: * Set the node name.
151: *
152: * @param name The new node name
153: */
154:
155: public void setName(String name) {
156: this .name = name;
157: }
158:
159: /**
160: * Get the parent configuration object. This method will return null
161: * if this configuration object is the top-most configuration object
162: * in the configuration tree.
163: *
164: * @return The parent configuration object or null
165: */
166:
167: public Configuration getParent() {
168: return parent;
169: }
170:
171: /**
172: * Get the child configuration object with the given name. If the
173: * child with the name does not exist then this method returns
174: * null. If more than one child with the given name exists then
175: * this method returns the first child.
176: *
177: * @param name The child name
178: * @return The first named child or null
179: */
180:
181: public Configuration getChild(String name) {
182: Iterator iter = children.iterator();
183: while (iter.hasNext()) {
184: Configuration child = (Configuration) iter.next();
185: if (child.getName().equals(name)) {
186: return child;
187: }
188: }
189: return null;
190: }
191:
192: /**
193: * Get the value of the first child configuration object with the
194: * given name. If the child cannot be found or the child had no
195: * data then this method returns null.
196: *
197: * @param name The child name
198: * @return The value or null
199: */
200:
201: public String getChildValue(String name) {
202: Configuration child = getChild(name);
203: if (child == null) {
204: return null;
205: } else {
206: return child.getValue();
207: }
208: }
209:
210: /**
211: * Get the value of the first child configuration object with the
212: * given name. If the child cannot be found or the child had no
213: * data then this method returns the given default value.
214: *
215: * @param name The child name
216: * @param defaultValue The default value
217: * @return The value
218: */
219:
220: public String getChildValue(String name, String defaultValue) {
221: Configuration child = getChild(name);
222: if (child == null) {
223: return defaultValue;
224: } else {
225: String value = child.getValue();
226: if (value == null) {
227: return defaultValue;
228: } else {
229: return value;
230: }
231: }
232: }
233:
234: /**
235: * Get a list of all child nodes.
236: *
237: * @return A List of Configuration objects
238: */
239:
240: public List getChildren() {
241: return children;
242: }
243:
244: /**
245: * Get a list of all child nodes with the given name.
246: *
247: * @param name The child node name
248: * @return A List of Configuration objects
249: */
250:
251: public List getChildren(String name) {
252: List filteredChildren = new ArrayList();
253: Iterator iter = children.iterator();
254: while (iter.hasNext()) {
255: Configuration child = (Configuration) iter.next();
256: if (child.getName().equals(name)) {
257: filteredChildren.add(child);
258: }
259: }
260: return filteredChildren;
261: }
262:
263: /**
264: * Add a child node with no child value to the configuration. This
265: * method should be the same as calling
266: * <code>addChild(name, null)</code>
267: *
268: * @param name The name of the new configuration node
269: * @return The configuration node
270: */
271:
272: public MutableConfiguration addChild(String name) {
273: ConfigurationBase child = new ConfigurationBase(name,
274: EMPTY_STRING, this );
275: children.add(child);
276: return child;
277: }
278:
279: /**
280: * Add a child node to the configuration. The value's toString() method
281: * will be used to convert the value to a String.
282: *
283: * @param name The name of the new configuration node
284: * @param value The value of the new configuration node
285: * @return The configuration node
286: */
287:
288: public MutableConfiguration addChild(String name, Object value) {
289: ConfigurationBase child = new ConfigurationBase(name, value,
290: this );
291: children.add(child);
292: return child;
293: }
294:
295: /**
296: * Add a child node to the configuration. The value's toString() method
297: * will be used to convert the value to a String. If the value is null use the
298: * default value.
299: *
300: * @param name The name of the new configuration node
301: * @param value The value of the new configuration node
302: * @param defaultValue value to use if the value was null
303: * @return The configuration node
304: */
305: public MutableConfiguration addChild(String name, Object value,
306: Object defaultValue) {
307: if (value == null) {
308: return addChild(name, defaultValue);
309: } else {
310: return addChild(name, value);
311: }
312: }
313:
314: /**
315: * Add the configuration object as a child of this configuration object.
316: *
317: * @param configuration The child configuration object
318: */
319:
320: public void addChild(Configuration configuration) {
321: children.add(configuration);
322: }
323:
324: /**
325: * Remove the specified configuration object.
326: *
327: * @param configuration The child configuration object
328: */
329:
330: public void removeChild(Configuration configuration) {
331: children.remove(configuration);
332: }
333:
334: /**
335: * Add an attribute with the given name. The value's toString() method
336: * will be used to convert the value to a String.
337: *
338: * @param name The attribute name
339: * @param value The attribute value
340: */
341:
342: public void addAttribute(String name, Object value) {
343: attributes.put(name, value);
344: }
345:
346: /**
347: * Set the configuration object's value.
348: *
349: * @param value The new value
350: */
351:
352: public void setValue(String value) {
353: this .value = value;
354: }
355:
356: /**
357: * Remove all of the children of this configuration node.
358: */
359:
360: public void clearChildren() {
361: children.clear();
362: }
363:
364: /**
365: * Get a List of attribute names.
366: *
367: * @return A List of attribute names
368: */
369:
370: public List getAttributeNames() {
371: List attributeNames = new ArrayList();
372: Iterator attributeKeys = attributes.keySet().iterator();
373: while (attributeKeys.hasNext()) {
374: attributeNames.add(attributeKeys.next());
375: }
376: return attributeNames;
377: }
378:
379: /**
380: * Get the named attribute or null.
381: *
382: * @param name The attribute name
383: * @return The attribute value
384: */
385:
386: public String getAttribute(String name) {
387: Object value = attributes.get(name);
388: if (value == null) {
389: return null;
390: } else {
391: return value.toString();
392: }
393: }
394:
395: /**
396: * Get the named attribute. If the attribute is not found then
397: * return the given default value.
398: *
399: * @param name The attribute name
400: * @param defaultValue The default value
401: * @return The attribute value
402: */
403:
404: public String getAttribute(String name, String defaultValue) {
405: Object value = getAttribute(name);
406: if (value == null) {
407: return defaultValue;
408: } else {
409: return value.toString();
410: }
411: }
412:
413: /**
414: * Get the node's value or null if the node contains no data.
415: *
416: * @return The node value or null
417: */
418:
419: public String getValue() {
420: if (value == null) {
421: return null;
422: } else {
423: return value.toString();
424: }
425: }
426:
427: /**
428: * Get the node's value. If the node contains no data then return
429: * the given default value.
430: *
431: * @param defaultValue The default value
432: * @return The node value
433: */
434:
435: public String getValue(String defaultValue) {
436: if (value == null) {
437: return defaultValue;
438: } else {
439: return value.toString();
440: }
441: }
442:
443: /**
444: * Get location information for this configuration object.
445: *
446: * @return Location
447: */
448:
449: public Location getLocation() {
450: return location;
451: }
452:
453: /**
454: * Save the configuration data to the specified output stream. Please note
455: * that the caller must close the OutputStream.
456: *
457: * @param out The OutputStream
458: * @throws ConfigurationException
459: */
460:
461: public void save(OutputStream out) throws ConfigurationException {
462: save(new OutputStreamWriter(out));
463: }
464:
465: /**
466: * Save the configuration data to the specified output stream. Please note
467: * that the caller must close the writer.
468: *
469: * @param out The Writer
470: * @throws ConfigurationException
471: */
472:
473: public void save(Writer out) throws ConfigurationException {
474: try {
475: BufferedWriter bout = new BufferedWriter(out);
476: XMLOutputter xmlOut = new XMLOutputter(bout, ENCODING);
477: write(this , xmlOut);
478: xmlOut.endDocument();
479: } catch (IOException e) {
480: throw new ConfigurationException(
481: "IO error while writing configuration", e);
482: }
483: }
484:
485: public void setLocation(String id) {
486: if (id != null)
487: location.setSourceId(id);
488: }
489:
490: /**
491: * Write the given Configuration object to the XMLOutputter. This method
492: * is recursive and will call itself until all children are printed.
493: *
494: * @param configuration The Configuration object
495: * @param xmlOut The XMLOutputter
496: * @throws IOException
497: */
498:
499: void write(Configuration configuration, XMLOutputter xmlOut)
500: throws IOException {
501: xmlOut.startTag(configuration.getName());
502: xmlOut.pcdata(configuration.getValue());
503:
504: Iterator attributeNames = configuration.getAttributeNames()
505: .iterator();
506: while (attributeNames.hasNext()) {
507: String attributeName = (String) attributeNames.next();
508: String attributeValue = configuration
509: .getAttribute(attributeName);
510: xmlOut.attribute(attributeName, attributeValue);
511: }
512:
513: Iterator children = configuration.getChildren().iterator();
514: while (children.hasNext()) {
515: Configuration child = (Configuration) children.next();
516: write(child, xmlOut);
517: }
518:
519: xmlOut.endTag();
520: }
521:
522: /**
523: * This would be a deep copy if you used immutable objects such as Strings for values and attributes,
524: * and shallow otherwise.
525: * It clones the Location object, however it sets the parent to null.
526: *
527: * @return copy of this Configuration
528: */
529: public Configuration copy() {
530: return copy(null);
531: }
532:
533: /**
534: * This would be a deep copy if you used immutable objects such as Strings for values and
535: * attributes, and shallow otherwise. It clones the Location object, however it sets the
536: * parent to null.
537: *
538: * @return copy of this Configuration
539: * @param parentConfig
540: */
541: public Configuration copy(Configuration parentConfig) {
542: ArrayList childrenCopy = new ArrayList();
543: HashMap attributesCopy = new HashMap();
544: Iterator attributeKeys = attributes.keySet().iterator();
545: while (attributeKeys.hasNext()) {
546: Object key = attributeKeys.next();
547: attributesCopy.put(key, attributes.get(key));
548: }
549: Iterator childIter = children.iterator();
550: while (childIter.hasNext()) {
551: childrenCopy.add(((Configuration) childIter.next()).copy());
552: }
553: Location locationCopy = null;
554: if (this .location != null) {
555: locationCopy = (Location) location.clone();
556: }
557: return new ConfigurationBase(this.name, this.value,
558: childrenCopy, attributesCopy, parentConfig,
559: locationCopy);
560: }
561: }
|