001: /*
002: * Copyright (C) The DNA Group. All rights reserved.
003: *
004: * This software is published under the terms of the DNA
005: * Software License version 1.1, a copy of which has been included
006: * with this distribution in the LICENSE.txt file.
007: */
008: package org.codehaus.dna.impl;
009:
010: import java.util.ArrayList;
011: import java.util.HashMap;
012: import java.util.List;
013: import java.util.Map;
014: import java.util.Set;
015:
016: import org.codehaus.dna.Configuration;
017: import org.codehaus.dna.ConfigurationException;
018:
019: /**
020: * In memory Configuration implementation.
021: * The developer should create the DefaultConfiguration,
022: * associate value, attributes and/or child elements configuration
023: * and then invoke {@link #makeReadOnly()} before passing the
024: * Configuration to the client component.
025: *
026: * @version $Revision: 1.2 $ $Date: 2004/05/01 09:51:48 $
027: */
028: public class DefaultConfiguration extends AbstractFreezable implements
029: Configuration {
030: /**
031: * Postfix indicating that location is generated.
032: */
033: private static final String AUTOGEN_POSTFIX = "<autogen>";
034:
035: /**
036: * The constant that boolean values must equal to be "true".
037: */
038: private static final String TRUE_STRING = "true";
039:
040: /**
041: * Constant for empty String array to reduce
042: * creation cost for empty array.
043: */
044: private static final String[] EMPTY_STRING_ARRAY = new String[0];
045:
046: /**
047: * Constant for empty configuration array to reduce
048: * creation cost for empty array.
049: */
050: private static final Configuration[] EMPTY_CONFIG_ARRAY = new Configuration[0];
051:
052: /**
053: * The name of configuration element.
054: */
055: private final String m_name;
056:
057: /**
058: * The location of configuration element in source.
059: * May be empty string if unknown.
060: */
061: private final String m_location;
062:
063: /**
064: * The path of configuration element in document.
065: * May be empty string if unknown.
066: */
067: private final String m_path;
068:
069: /**
070: * The attributes defined by configuration (May be null).
071: */
072: private Map m_attributes;
073:
074: /**
075: * The child elements defined by
076: * configuration (May be null). If
077: * {@link #m_value} not null then
078: * m_children must be null.
079: */
080: private List m_children;
081:
082: /**
083: * The value contained in configuration
084: * (May be null). If {@link #m_children} not
085: * null then m_value must be null.
086: */
087: private String m_value;
088:
089: /**
090: * Create a DefaultConfiguration instance.
091: *
092: * @param name the name of configuration element
093: * @param location the location of configuration element in source
094: * @param path the path of configuration element in document
095: */
096: public DefaultConfiguration(final String name,
097: final String location, final String path) {
098: if (null == name) {
099: throw new NullPointerException("name");
100: }
101: if (null == path) {
102: throw new NullPointerException("path");
103: }
104: if (null == location) {
105: throw new NullPointerException("location");
106: }
107: m_name = name;
108: m_path = path;
109: m_location = location;
110: }
111:
112: /**
113: * Return the name of the configuration element.
114: *
115: * @return the name of the configuration element.
116: */
117: public String getName() {
118: return m_name;
119: }
120:
121: /**
122: * Return the path to the configuration element.
123: * The path should be in the xpath form but may
124: * be the empty string if unabel to determine path.
125: *
126: * @return the path to the configuration element.
127: */
128: public final String getPath() {
129: return m_path;
130: }
131:
132: /**
133: * Return the location of configuration element.
134: * Usually of the form "uri[:line number[:column number]]"
135: * if possible. ie "file:myFile.xml:80:2". However the line
136: * number and column number may be elided if unavailable.
137: *
138: * @return the location of configuration element.
139: */
140: public String getLocation() {
141: return m_location;
142: }
143:
144: /**
145: * Return an array of all the child elements.
146: *
147: * @return an array of all the child elements.
148: */
149: public Configuration[] getChildren() {
150: final List childList = getChildList();
151: if (null == childList) {
152: return EMPTY_CONFIG_ARRAY;
153: } else {
154: return (Configuration[]) childList
155: .toArray(new Configuration[childList.size()]);
156: }
157: }
158:
159: /**
160: * Return an array of all the child elements with specified name.
161: *
162: * @param name the name of child configuration objects
163: * @return an array of all the child elements with specified name.
164: */
165: public Configuration[] getChildren(final String name) {
166: if (null == name) {
167: throw new NullPointerException("name");
168: }
169: final List children = getChildList();
170: if (null == children) {
171: return EMPTY_CONFIG_ARRAY;
172: } else {
173: final ArrayList results = new ArrayList();
174: final int count = children.size();
175: for (int i = 0; i < count; i++) {
176: final Configuration child = (Configuration) children
177: .get(i);
178: if (child.getName().equals(name)) {
179: results.add(child);
180: }
181: }
182: return (Configuration[]) results
183: .toArray(new Configuration[results.size()]);
184: }
185: }
186:
187: /**
188: * Return a child Configuration element with specified name.
189: * If no such element exists an element will be autocreated.
190: *
191: * @param name the name of child configuration object
192: * @return a child Configuration element with specified name.
193: */
194: public Configuration getChild(final String name) {
195: return getChild(name, true);
196: }
197:
198: /**
199: * Return a child Configuration element with specified name.
200: * If no such element exists and createChild is true then an
201: * element will be autocreated otherwise null will be returned.
202: *
203: * @param name the name of child configuration object
204: * @param createChild true if child should be created if it does not exist
205: * @return a child Configuration element with specified name.
206: */
207: public Configuration getChild(final String name,
208: final boolean createChild) {
209: if (null == name) {
210: throw new NullPointerException("name");
211: }
212: final List children = getChildList();
213: if (null != children) {
214: final int count = children.size();
215: for (int i = 0; i < count; i++) {
216: final Configuration child = (Configuration) children
217: .get(i);
218: if (child.getName().equals(name)) {
219: return child;
220: }
221: }
222: }
223: if (createChild) {
224: final String path = getPath()
225: + ConfigurationUtil.PATH_SEPARATOR + getName();
226: return new DefaultConfiguration(name, generateLocation(),
227: path);
228: } else {
229: return null;
230: }
231:
232: }
233:
234: /**
235: * Return text value of element.
236: *
237: * @return the value
238: * @throws ConfigurationException if no value in element
239: */
240: public String getValue() throws ConfigurationException {
241: if (null != m_value) {
242: return m_value;
243: } else {
244: final String message = "No value specified";
245: throw new ConfigurationException(message, getPath(),
246: getLocation());
247: }
248: }
249:
250: /**
251: * Return text value of element.
252: * Use specified default if no value in element.
253: *
254: * @param defaultValue the default value
255: * @return the value
256: */
257: public String getValue(final String defaultValue) {
258: if (null != m_value) {
259: return m_value;
260: } else {
261: return defaultValue;
262: }
263: }
264:
265: /**
266: * Return text value of element as a boolean.
267: *
268: * @return the value
269: * @throws ConfigurationException if no value in element
270: * or value can not be converted to correct type
271: */
272: public boolean getValueAsBoolean() throws ConfigurationException {
273: return getValue().equals("true");
274: }
275:
276: /**
277: * Return text value of element as a boolean.
278: * Use specified default if no value in element or
279: * value can not be converted to correct type.
280: *
281: * @param defaultValue the default value
282: * @return the value
283: */
284: public boolean getValueAsBoolean(final boolean defaultValue) {
285: if (null == m_value) {
286: return defaultValue;
287: } else {
288: return m_value.equals(TRUE_STRING);
289: }
290: }
291:
292: /**
293: * Return text value of element as an integer.
294: *
295: * @return the value
296: * @throws ConfigurationException if no value in element
297: * or value can not be converted to correct type
298: */
299: public int getValueAsInteger() throws ConfigurationException {
300: try {
301: return Integer.parseInt(getValue());
302: } catch (final NumberFormatException nfe) {
303: final String message = "Unable to parse " + getValue()
304: + " as an integer";
305: throw new ConfigurationException(message, getPath(),
306: getLocation(), nfe);
307: }
308: }
309:
310: /**
311: * Return text value of element as an integer.
312: * Use specified default if no value in element or
313: * value can not be converted to correct type.
314: *
315: * @param defaultValue the default value
316: * @return the value
317: */
318: public int getValueAsInteger(final int defaultValue) {
319: if (null == m_value) {
320: return defaultValue;
321: } else {
322: try {
323: return Integer.parseInt(m_value);
324: } catch (final NumberFormatException nfe) {
325: return defaultValue;
326: }
327: }
328: }
329:
330: /**
331: * Return text value of element as a long.
332: *
333: * @return the value
334: * @throws ConfigurationException if no value in element
335: * or value can not be converted to correct type
336: */
337: public long getValueAsLong() throws ConfigurationException {
338: try {
339: return Long.parseLong(getValue());
340: } catch (final NumberFormatException nfe) {
341: final String message = "Unable to parse " + getValue()
342: + " as a Long";
343: throw new ConfigurationException(message, getPath(),
344: getLocation(), nfe);
345: }
346: }
347:
348: /**
349: * Return text value of element as a long.
350: * Use specified default if no value in element or
351: * value can not be converted to correct type.
352: *
353: * @param defaultValue the default value
354: * @return the value
355: */
356: public long getValueAsLong(final long defaultValue) {
357: if (null == m_value) {
358: return defaultValue;
359: } else {
360: try {
361: return Long.parseLong(m_value);
362: } catch (final NumberFormatException nfe) {
363: return defaultValue;
364: }
365: }
366: }
367:
368: /**
369: * Return text value of element as a float.
370: *
371: * @return the value
372: * @throws ConfigurationException if no value in element
373: * or value can not be converted to correct type
374: */
375: public float getValueAsFloat() throws ConfigurationException {
376: try {
377: return Float.parseFloat(getValue());
378: } catch (final NumberFormatException nfe) {
379: final String message = "Unable to parse " + getValue()
380: + " as a Long";
381: throw new ConfigurationException(message, getPath(),
382: getLocation(), nfe);
383: }
384: }
385:
386: /**
387: * Return text value of element as a float.
388: * Use specified default if no value in element or
389: * value can not be converted to correct type.
390: *
391: * @param defaultValue the default value
392: * @return the value
393: */
394: public float getValueAsFloat(final float defaultValue) {
395: if (null == m_value) {
396: return defaultValue;
397: } else {
398: try {
399: return Float.parseFloat(m_value);
400: } catch (final NumberFormatException nfe) {
401: return defaultValue;
402: }
403: }
404: }
405:
406: /**
407: * Return an array of all the attribute names.
408: *
409: * @return an array of all the attribute names.
410: */
411: public String[] getAttributeNames() {
412: final Map attributeMap = getAttributeMap();
413: if (null == attributeMap) {
414: return EMPTY_STRING_ARRAY;
415: } else {
416: final Set keys = attributeMap.keySet();
417: return (String[]) attributeMap.keySet().toArray(
418: new String[keys.size()]);
419: }
420: }
421:
422: /**
423: * Return attribute value with specified name.
424: *
425: * @param name the attribute name
426: * @return the attribute value
427: * @throws ConfigurationException if no attribute with
428: * specified name
429: */
430: public String getAttribute(final String name)
431: throws ConfigurationException {
432: final String value = doGetAttribute(name);
433: if (null != value) {
434: return value;
435: } else {
436: final String message = "Attribute named " + name
437: + " not specified.";
438: throw new ConfigurationException(message, getPath(),
439: getLocation());
440: }
441: }
442:
443: /**
444: * Return attribute value with specified name.
445: * If no attribute with specified name then return
446: * default value.
447: *
448: * @param name the attribute name
449: * @param defaultValue the default value
450: * @return the attribute value
451: */
452: public String getAttribute(final String name,
453: final String defaultValue) {
454: final String value = doGetAttribute(name);
455: if (null != value) {
456: return value;
457: } else {
458: return defaultValue;
459: }
460: }
461:
462: /**
463: * Return attribute value with specified name or null
464: * if no such attribute.
465: *
466: * @param name the attribute name
467: * @return the attribute value
468: */
469: private String doGetAttribute(final String name) {
470: if (null == name) {
471: throw new NullPointerException("name");
472: }
473: final Map attributeMap = getAttributeMap();
474: if (null != attributeMap) {
475: final String value = (String) attributeMap.get(name);
476: if (null != value) {
477: return value;
478: }
479: }
480: return null;
481: }
482:
483: /**
484: * Return attribute value with specified name as a boolean.
485: *
486: * @param name the attribute name
487: * @return the attribute value
488: * @throws ConfigurationException if no attribute with
489: * specified name or attribute can not be converted
490: * to correct type
491: */
492: public boolean getAttributeAsBoolean(final String name)
493: throws ConfigurationException {
494: return getAttribute(name).equals(TRUE_STRING);
495: }
496:
497: /**
498: * Return attribute value with specified name as a boolean.
499: * If no attribute with specified name or attribute can
500: * not be converted to correct type then return
501: * default value.
502: *
503: * @param name the attribute name
504: * @param defaultValue the default value
505: * @return the attribute value
506: */
507: public boolean getAttributeAsBoolean(final String name,
508: final boolean defaultValue) {
509: final String value = getAttribute(name, null);
510: if (null != value) {
511: return value.equals(TRUE_STRING);
512: }
513: return defaultValue;
514: }
515:
516: /**
517: * Return attribute value with specified name as an integer.
518: *
519: * @param name the attribute name
520: * @return the attribute value
521: * @throws ConfigurationException if no attribute with
522: * specified name or attribute can not be converted
523: * to correct type
524: */
525: public int getAttributeAsInteger(final String name)
526: throws ConfigurationException {
527: final String value = getAttribute(name);
528: try {
529: return Integer.parseInt(value);
530: } catch (final NumberFormatException nfe) {
531: final String message = "Unable to parse " + value
532: + " as an Integer.";
533: throw new ConfigurationException(message, getPath(),
534: getLocation());
535: }
536: }
537:
538: /**
539: * Return attribute value with specified name as an integer.
540: * If no attribute with specified name or attribute can
541: * not be converted to correct type then return
542: * default value.
543: *
544: * @param name the attribute name
545: * @param defaultValue the default value
546: * @return the attribute value
547: */
548: public int getAttributeAsInteger(final String name,
549: final int defaultValue) {
550: final String value = getAttribute(name, null);
551: if (null != value) {
552: try {
553: return Integer.parseInt(value);
554: } catch (final NumberFormatException nfe) {
555: //Fall through to return defaultValue
556: }
557: }
558: return defaultValue;
559: }
560:
561: /**
562: * Return attribute value with specified name as a long.
563: *
564: * @param name the attribute name
565: * @return the attribute value
566: * @throws ConfigurationException if no attribute with
567: * specified name or attribute can not be converted
568: * to correct type
569: */
570: public long getAttributeAsLong(final String name)
571: throws ConfigurationException {
572: final String value = getAttribute(name);
573: try {
574: return Long.parseLong(value);
575: } catch (final NumberFormatException nfe) {
576: final String message = "Unable to parse " + value
577: + " as a Long.";
578: throw new ConfigurationException(message, getPath(),
579: getLocation());
580: }
581: }
582:
583: /**
584: * Return attribute value with specified name as a long.
585: * If no attribute with specified name or attribute can
586: * not be converted to correct type then return
587: * default value.
588: *
589: * @param name the attribute name
590: * @param defaultValue the default value
591: * @return the attribute value
592: */
593: public long getAttributeAsLong(final String name,
594: final long defaultValue) {
595: final String value = getAttribute(name, null);
596: if (null != value) {
597: try {
598: return Long.parseLong(value);
599: } catch (final NumberFormatException nfe) {
600: //Fall through to return defaultValue
601: }
602: }
603: return defaultValue;
604: }
605:
606: /**
607: * Return attribute value with specified name as afloat.
608: *
609: * @param name the attribute name
610: * @return the attribute value
611: * @throws ConfigurationException if no attribute with
612: * specified name or attribute can not be converted
613: * to correct type
614: */
615: public float getAttributeAsFloat(final String name)
616: throws ConfigurationException {
617: final String value = getAttribute(name);
618: try {
619: return Float.parseFloat(value);
620: } catch (final NumberFormatException nfe) {
621: final String message = "Unable to parse " + value
622: + " as a Float.";
623: throw new ConfigurationException(message, getPath(),
624: getLocation());
625: }
626: }
627:
628: /**
629: * Return attribute value with specified name as a float.
630: * If no attribute with specified name or attribute can
631: * not be converted to correct type then return
632: * default value.
633: *
634: * @param name the attribute name
635: * @param defaultValue the default value
636: * @return the attribute value
637: */
638: public float getAttributeAsFloat(final String name,
639: final float defaultValue) {
640: final String value = getAttribute(name, null);
641: if (null != value) {
642: try {
643: return Float.parseFloat(value);
644: } catch (final NumberFormatException nfe) {
645: //Fall through to return defaultValue
646: }
647: }
648: return defaultValue;
649: }
650:
651: /**
652: * Mark the configuration and child configurations as read only.
653: */
654: public void makeReadOnly() {
655: super .makeReadOnly();
656: final List children = getChildList();
657: if (null != children) {
658: final int count = children.size();
659: for (int i = 0; i < count; i++) {
660: final Configuration configuration = (Configuration) children
661: .get(i);
662: if (configuration instanceof Freezable) {
663: ((Freezable) configuration).makeReadOnly();
664: }
665: }
666: }
667: }
668:
669: /**
670: * Set an attribute of configuration.
671: *
672: * @param key the attribute key
673: * @param value the attribute value
674: */
675: public void setAttribute(final String key, final String value) {
676: if (null == key) {
677: throw new NullPointerException("key");
678: }
679: if (null == value) {
680: throw new NullPointerException("value");
681: }
682: checkWriteable();
683: if (null == m_attributes) {
684: m_attributes = new HashMap();
685: }
686: m_attributes.put(key, value);
687: }
688:
689: /**
690: * Add a child configuration element.
691: *
692: * @param configuration the child configuration element.
693: */
694: public void addChild(final Configuration configuration) {
695: if (null == configuration) {
696: throw new NullPointerException("configuration");
697: }
698: checkWriteable();
699: if (null != m_value) {
700: throwMixedContentException();
701: }
702: if (null == m_children) {
703: m_children = new ArrayList();
704: }
705: m_children.add(configuration);
706: }
707:
708: /**
709: * Set the value of the configuration element.
710: *
711: * @param value the value of the configuration element.
712: */
713: public void setValue(final String value) {
714: if (null == value) {
715: throw new NullPointerException("value");
716: }
717: checkWriteable();
718: final List children = getChildList();
719: if (null != children && 0 != children.size()) {
720: throwMixedContentException();
721: }
722: m_value = value;
723: }
724:
725: /**
726: * Overide toString to improve ability to debug implementation.
727: *
728: * @return string representation of object
729: */
730: public String toString() {
731: final StringBuffer sb = new StringBuffer();
732: sb.append("[Configuration name='");
733: sb.append(getName());
734: sb.append("'");
735: if (null != m_attributes) {
736: sb.append(" attributes=");
737: sb.append(m_attributes);
738: }
739: sb.append("]");
740: return sb.toString();
741: }
742:
743: /**
744: * Return the list of child configuration objects.
745: *
746: * @return the list of child configuration objects.
747: */
748: protected final List getChildList() {
749: return m_children;
750: }
751:
752: /**
753: * Return the backing map for attributes.
754: *
755: * @return the backing map for attributes.
756: */
757: protected final Map getAttributeMap() {
758: return m_attributes;
759: }
760:
761: /**
762: * Generate a location string that postfixes
763: * autogenerated marker.
764: *
765: * @return a autogenerated location string
766: */
767: protected final String generateLocation() {
768: final String location = getLocation();
769: if (!location.endsWith(AUTOGEN_POSTFIX)) {
770: return location + AUTOGEN_POSTFIX;
771: } else {
772: return location;
773: }
774: }
775:
776: /**
777: * Throw an IllegalStateException warning about
778: * mixed content.
779: */
780: protected final void throwMixedContentException() {
781: final String message = "Configuration objects do not support Mixed content. "
782: + "Configuration elements should not have both a value and "
783: + "child elements.";
784: throw new IllegalStateException(message);
785: }
786: }
|