001: /* ========================================================================
002: * JCommon : a free general purpose class library for the Java(tm) platform
003: * ========================================================================
004: *
005: * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006: *
007: * Project Info: http://www.jfree.org/jcommon/index.html
008: *
009: * This library is free software; you can redistribute it and/or modify it
010: * under the terms of the GNU Lesser General Public License as published by
011: * the Free Software Foundation; either version 2.1 of the License, or
012: * (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but
015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017: * License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022: * USA.
023: *
024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025: * in the United States and other countries.]
026: *
027: * ------------------------------
028: * HierarchicalConfiguration.java
029: * ------------------------------
030: * (C) Copyright 2004, by Object Refinery Limited.
031: *
032: * Original Author: Thomas Morgner;
033: * Contributor(s): David Gilbert (for Object Refinery Limited);
034: *
035: * $Id: HierarchicalConfiguration.java,v 1.7 2006/11/20 21:36:30 taqua Exp $
036: *
037: * Changes
038: * -------
039: * 07-Jun-2004 : Added JCommon header (DG);
040: * 29-Jul-2004 : Replaced 'enum' variable name (reserved word in JDK 1.5) (DG);
041: *
042: */
043:
044: package org.jfree.base.config;
045:
046: import java.io.IOException;
047: import java.io.ObjectInputStream;
048: import java.io.ObjectOutputStream;
049: import java.util.Collections;
050: import java.util.Enumeration;
051: import java.util.Iterator;
052: import java.util.Properties;
053: import java.util.TreeSet;
054:
055: import org.jfree.util.Configuration;
056: import org.jfree.util.PublicCloneable;
057:
058: /**
059: * A hierarchical configuration. Such a configuration can have one or more
060: * parent configurations providing usefull default values.
061: *
062: * @author Thomas Morgner
063: */
064: public class HierarchicalConfiguration implements
065: ModifiableConfiguration, PublicCloneable {
066:
067: /**
068: * The instance configuration properties.
069: */
070: private Properties configuration;
071:
072: /**
073: * The parent configuration (null if this is the root configuration).
074: */
075: private transient Configuration parentConfiguration;
076:
077: /**
078: * Creates a new configuration.
079: */
080: public HierarchicalConfiguration() {
081: this .configuration = new Properties();
082: }
083:
084: /**
085: * Creates a new configuration.
086: *
087: * @param parentConfiguration the parent configuration.
088: */
089: public HierarchicalConfiguration(
090: final Configuration parentConfiguration) {
091: this ();
092: this .parentConfiguration = parentConfiguration;
093: }
094:
095: /**
096: * Returns the configuration property with the specified key.
097: *
098: * @param key the property key.
099: * @return the property value.
100: */
101: public String getConfigProperty(final String key) {
102: return getConfigProperty(key, null);
103: }
104:
105: /**
106: * Returns the configuration property with the specified key (or the
107: * specified default value if there is no such property).
108: * <p/>
109: * If the property is not defined in this configuration, the code will
110: * lookup the property in the parent configuration.
111: *
112: * @param key the property key.
113: * @param defaultValue the default value.
114: * @return the property value.
115: */
116: public String getConfigProperty(final String key,
117: final String defaultValue) {
118: String value = this .configuration.getProperty(key);
119: if (value == null) {
120: if (isRootConfig()) {
121: value = defaultValue;
122: } else {
123: value = this .parentConfiguration.getConfigProperty(key,
124: defaultValue);
125: }
126: }
127: return value;
128: }
129:
130: /**
131: * Sets a configuration property.
132: *
133: * @param key the property key.
134: * @param value the property value.
135: */
136: public void setConfigProperty(final String key, final String value) {
137: if (key == null) {
138: throw new NullPointerException();
139: }
140:
141: if (value == null) {
142: this .configuration.remove(key);
143: } else {
144: this .configuration.setProperty(key, value);
145: }
146: }
147:
148: /**
149: * Returns true if this object has no parent.
150: *
151: * @return true, if this report is the root configuration, false otherwise.
152: */
153: private boolean isRootConfig() {
154: return this .parentConfiguration == null;
155: }
156:
157: /**
158: * Checks, whether the given key is localy defined in this instance or
159: * whether the key's value is inherited.
160: *
161: * @param key the key that should be checked.
162: * @return true, if the key is defined locally, false otherwise.
163: */
164: public boolean isLocallyDefined(final String key) {
165: return this .configuration.containsKey(key);
166: }
167:
168: /**
169: * Returns the collection of properties for the configuration.
170: *
171: * @return the properties.
172: */
173: protected Properties getConfiguration() {
174: return this .configuration;
175: }
176:
177: /**
178: * The new configuartion will be inserted into the list of report
179: * configuration, so that this configuration has the given report
180: * configuration instance as parent.
181: *
182: * @param config the new report configuration.
183: */
184: public void insertConfiguration(
185: final HierarchicalConfiguration config) {
186: config.setParentConfig(getParentConfig());
187: setParentConfig(config);
188: }
189:
190: /**
191: * Set the parent configuration. The parent configuration is queried, if the
192: * requested configuration values was not found in this report
193: * configuration.
194: *
195: * @param config the parent configuration.
196: */
197: protected void setParentConfig(final Configuration config) {
198: if (this .parentConfiguration == this ) {
199: throw new IllegalArgumentException(
200: "Cannot add myself as parent configuration.");
201: }
202: this .parentConfiguration = config;
203: }
204:
205: /**
206: * Returns the parent configuration. The parent configuration is queried, if
207: * the requested configuration values was not found in this report
208: * configuration.
209: *
210: * @return the parent configuration.
211: */
212: protected Configuration getParentConfig() {
213: return this .parentConfiguration;
214: }
215:
216: /**
217: * Returns all defined configuration properties for the report. The
218: * enumeration contains all keys of the changed properties, properties set
219: * from files or the system properties are not included.
220: *
221: * @return all defined configuration properties for the report.
222: */
223: public Enumeration getConfigProperties() {
224: return this .configuration.keys();
225: }
226:
227: /**
228: * Searches all property keys that start with a given prefix.
229: *
230: * @param prefix the prefix that all selected property keys should share
231: * @return the properties as iterator.
232: */
233: public Iterator findPropertyKeys(final String prefix) {
234: final TreeSet keys = new TreeSet();
235: collectPropertyKeys(prefix, this , keys);
236: return Collections.unmodifiableSet(keys).iterator();
237: }
238:
239: /**
240: * Collects property keys from this and all parent report configurations,
241: * which start with the given prefix.
242: *
243: * @param prefix the prefix, that selects the property keys.
244: * @param config the currently processed report configuration.
245: * @param collector the target list, that should receive all valid keys.
246: */
247: private void collectPropertyKeys(final String prefix,
248: final Configuration config, final TreeSet collector) {
249: final Enumeration enum1 = config.getConfigProperties();
250: while (enum1.hasMoreElements()) {
251: final String key = (String) enum1.nextElement();
252: if (key.startsWith(prefix)) {
253: if (collector.contains(key) == false) {
254: collector.add(key);
255: }
256: }
257: }
258:
259: if (config instanceof HierarchicalConfiguration) {
260: final HierarchicalConfiguration hconfig = (HierarchicalConfiguration) config;
261: if (hconfig.parentConfiguration != null) {
262: collectPropertyKeys(prefix,
263: hconfig.parentConfiguration, collector);
264: }
265: }
266: }
267:
268: /**
269: * Checks, whether the parent configuration can be serialized. Usually the
270: * global configuration is not serialized and should return false here.
271: *
272: * @return true, if the parent config can be serialized, false otherwise.
273: */
274: protected boolean isParentSaved() {
275: return true;
276: }
277:
278: /**
279: * A callback method to reconnect this configuration with the global
280: * configuration after deserialization.
281: */
282: protected void configurationLoaded() {
283: }
284:
285: /**
286: * Helper method for serialization.
287: *
288: * @param out the output stream where to write the object.
289: * @throws java.io.IOException if errors occur while writing the stream.
290: */
291: private void writeObject(final ObjectOutputStream out)
292: throws IOException {
293: out.defaultWriteObject();
294: if (isParentSaved() == false) {
295: out.writeBoolean(false);
296: } else {
297: out.writeBoolean(true);
298: out.writeObject(parentConfiguration);
299: }
300: }
301:
302: /**
303: * Helper method for serialization.
304: *
305: * @param in the input stream from where to read the serialized object.
306: * @throws IOException when reading the stream fails.
307: * @throws ClassNotFoundException if a class definition for a serialized
308: * object could not be found.
309: */
310: private void readObject(final ObjectInputStream in)
311: throws IOException, ClassNotFoundException {
312: in.defaultReadObject();
313: final boolean readParent = in.readBoolean();
314: if (readParent) {
315: parentConfiguration = (ModifiableConfiguration) in
316: .readObject();
317: } else {
318: parentConfiguration = null;
319: }
320: configurationLoaded();
321: }
322:
323: public Object clone() throws CloneNotSupportedException {
324: HierarchicalConfiguration config = (HierarchicalConfiguration) super
325: .clone();
326: config.configuration = (Properties) configuration.clone();
327: return config;
328: }
329: }
|