001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.configuration;
019
020 import java.io.File;
021 import java.io.InputStream;
022 import java.io.OutputStream;
023 import java.io.Reader;
024 import java.io.Writer;
025 import java.net.URL;
026 import java.util.Collection;
027 import java.util.Iterator;
028 import java.util.List;
029
030 import org.apache.commons.configuration.event.ConfigurationEvent;
031 import org.apache.commons.configuration.event.ConfigurationListener;
032 import org.apache.commons.configuration.reloading.ReloadingStrategy;
033
034 /**
035 * <p>Base class for implementing file based hierarchical configurations.</p>
036 * <p>This class serves an analogous purpose as the
037 * <code>{@link AbstractFileConfiguration}</code> class for non hierarchical
038 * configurations. It behaves in exactly the same way, so please refer to the
039 * documentation of <code>AbstractFileConfiguration</code> for further details.</p>
040 *
041 * @since 1.2
042 *
043 * @author Emmanuel Bourg
044 * @version $Revision: 712405 $, $Date: 2008-11-08 17:37:48 +0100 (Sa, 08 Nov 2008) $
045 */
046 public abstract class AbstractHierarchicalFileConfiguration
047 extends HierarchicalConfiguration
048 implements FileConfiguration, ConfigurationListener
049 {
050 /** Stores the delegate used for implementing functionality related to the
051 * <code>FileConfiguration</code> interface.
052 */
053 private FileConfigurationDelegate delegate;
054
055 /**
056 * Creates a new instance of
057 * <code>AbstractHierarchicalFileConfiguration</code>.
058 */
059 protected AbstractHierarchicalFileConfiguration()
060 {
061 initialize();
062 }
063
064 /**
065 * Creates a new instance of
066 * <code>AbstractHierarchicalFileConfiguration</code> and copies the
067 * content of the specified configuration into this object.
068 *
069 * @param c the configuration to copy
070 * @since 1.4
071 */
072 protected AbstractHierarchicalFileConfiguration(HierarchicalConfiguration c)
073 {
074 super(c);
075 initialize();
076 }
077
078 /**
079 * Creates and loads the configuration from the specified file.
080 *
081 * @param fileName The name of the plist file to load.
082 * @throws ConfigurationException Error while loading the file
083 */
084 public AbstractHierarchicalFileConfiguration(String fileName) throws ConfigurationException
085 {
086 this();
087 // store the file name
088 delegate.setFileName(fileName);
089
090 // load the file
091 load();
092 }
093
094 /**
095 * Creates and loads the configuration from the specified file.
096 *
097 * @param file The configuration file to load.
098 * @throws ConfigurationException Error while loading the file
099 */
100 public AbstractHierarchicalFileConfiguration(File file) throws ConfigurationException
101 {
102 this();
103 // set the file and update the url, the base path and the file name
104 setFile(file);
105
106 // load the file
107 if (file.exists())
108 {
109 load();
110 }
111 }
112
113 /**
114 * Creates and loads the configuration from the specified URL.
115 *
116 * @param url The location of the configuration file to load.
117 * @throws ConfigurationException Error while loading the file
118 */
119 public AbstractHierarchicalFileConfiguration(URL url) throws ConfigurationException
120 {
121 this();
122 // set the URL and update the base path and the file name
123 setURL(url);
124
125 // load the file
126 load();
127 }
128
129 /**
130 * Initializes this instance, mainly the internally used delegate object.
131 */
132 private void initialize()
133 {
134 delegate = createDelegate();
135 initDelegate(delegate);
136 }
137
138 protected void addPropertyDirect(String key, Object obj)
139 {
140 super.addPropertyDirect(key, obj);
141 delegate.possiblySave();
142 }
143
144 public void clearProperty(String key)
145 {
146 super.clearProperty(key);
147 delegate.possiblySave();
148 }
149
150 public void clearTree(String key)
151 {
152 super.clearTree(key);
153 delegate.possiblySave();
154 }
155
156 public void setProperty(String key, Object value)
157 {
158 super.setProperty(key, value);
159 delegate.possiblySave();
160 }
161
162 public void load() throws ConfigurationException
163 {
164 delegate.load();
165 }
166
167 public void load(String fileName) throws ConfigurationException
168 {
169 delegate.load(fileName);
170 }
171
172 public void load(File file) throws ConfigurationException
173 {
174 delegate.load(file);
175 }
176
177 public void load(URL url) throws ConfigurationException
178 {
179 delegate.load(url);
180 }
181
182 public void load(InputStream in) throws ConfigurationException
183 {
184 delegate.load(in);
185 }
186
187 public void load(InputStream in, String encoding) throws ConfigurationException
188 {
189 delegate.load(in, encoding);
190 }
191
192 public void save() throws ConfigurationException
193 {
194 delegate.save();
195 }
196
197 public void save(String fileName) throws ConfigurationException
198 {
199 delegate.save(fileName);
200 }
201
202 public void save(File file) throws ConfigurationException
203 {
204 delegate.save(file);
205 }
206
207 public void save(URL url) throws ConfigurationException
208 {
209 delegate.save(url);
210 }
211
212 public void save(OutputStream out) throws ConfigurationException
213 {
214 delegate.save(out);
215 }
216
217 public void save(OutputStream out, String encoding) throws ConfigurationException
218 {
219 delegate.save(out, encoding);
220 }
221
222 public String getFileName()
223 {
224 return delegate.getFileName();
225 }
226
227 public void setFileName(String fileName)
228 {
229 delegate.setFileName(fileName);
230 }
231
232 public String getBasePath()
233 {
234 return delegate.getBasePath();
235 }
236
237 public void setBasePath(String basePath)
238 {
239 delegate.setBasePath(basePath);
240 }
241
242 public File getFile()
243 {
244 return delegate.getFile();
245 }
246
247 public void setFile(File file)
248 {
249 delegate.setFile(file);
250 }
251
252 public URL getURL()
253 {
254 return delegate.getURL();
255 }
256
257 public void setURL(URL url)
258 {
259 delegate.setURL(url);
260 }
261
262 public void setAutoSave(boolean autoSave)
263 {
264 delegate.setAutoSave(autoSave);
265 }
266
267 public boolean isAutoSave()
268 {
269 return delegate.isAutoSave();
270 }
271
272 public ReloadingStrategy getReloadingStrategy()
273 {
274 return delegate.getReloadingStrategy();
275 }
276
277 public void setReloadingStrategy(ReloadingStrategy strategy)
278 {
279 delegate.setReloadingStrategy(strategy);
280 }
281
282 public void reload()
283 {
284 setDetailEvents(false);
285 try
286 {
287 delegate.reload();
288 }
289 finally
290 {
291 setDetailEvents(true);
292 }
293 }
294
295 public String getEncoding()
296 {
297 return delegate.getEncoding();
298 }
299
300 public void setEncoding(String encoding)
301 {
302 delegate.setEncoding(encoding);
303 }
304
305 public boolean containsKey(String key)
306 {
307 reload();
308 return super.containsKey(key);
309 }
310
311 public Iterator getKeys()
312 {
313 reload();
314 return super.getKeys();
315 }
316
317 public Iterator getKeys(String prefix)
318 {
319 reload();
320 return super.getKeys(prefix);
321 }
322
323 public Object getProperty(String key)
324 {
325 reload();
326 return super.getProperty(key);
327 }
328
329 public boolean isEmpty()
330 {
331 reload();
332 return super.isEmpty();
333 }
334
335 /**
336 * Directly adds sub nodes to this configuration. This implementation checks
337 * whether auto save is necessary after executing the operation.
338 *
339 * @param key the key where the nodes are to be added
340 * @param nodes a collection with the nodes to be added
341 * @since 1.5
342 */
343 public void addNodes(String key, Collection nodes)
344 {
345 super.addNodes(key, nodes);
346 delegate.possiblySave();
347 }
348
349 /**
350 * Fetches a list of nodes, which are selected by the specified key. This
351 * implementation will perform a reload if necessary.
352 *
353 * @param key the key
354 * @return a list with the selected nodes
355 */
356 protected List fetchNodeList(String key)
357 {
358 reload();
359 return super.fetchNodeList(key);
360 }
361
362 /**
363 * Reacts on changes of an associated subnode configuration. If the auto
364 * save mechanism is active, the configuration must be saved.
365 *
366 * @param event the event describing the change
367 * @since 1.5
368 */
369 protected void subnodeConfigurationChanged(ConfigurationEvent event)
370 {
371 delegate.possiblySave();
372 super.subnodeConfigurationChanged(event);
373 }
374
375 /**
376 * Creates the file configuration delegate, i.e. the object that implements
377 * functionality required by the <code>FileConfiguration</code> interface.
378 * This base implementation will return an instance of the
379 * <code>FileConfigurationDelegate</code> class. Derived classes may
380 * override it to create a different delegate object.
381 *
382 * @return the file configuration delegate
383 */
384 protected FileConfigurationDelegate createDelegate()
385 {
386 return new FileConfigurationDelegate();
387 }
388
389 /**
390 * Helper method for initializing the file configuration delegate.
391 *
392 * @param del the delegate
393 */
394 private void initDelegate(FileConfigurationDelegate del)
395 {
396 del.addConfigurationListener(this);
397 }
398
399 /**
400 * Reacts on configuration change events triggered by the delegate. These
401 * events are passed to the registered configuration listeners.
402 *
403 * @param event the triggered event
404 * @since 1.3
405 */
406 public void configurationChanged(ConfigurationEvent event)
407 {
408 // deliver reload events to registered listeners
409 setDetailEvents(true);
410 try
411 {
412 fireEvent(event.getType(), event.getPropertyName(), event
413 .getPropertyValue(), event.isBeforeUpdate());
414 }
415 finally
416 {
417 setDetailEvents(false);
418 }
419 }
420
421 /**
422 * Returns the file configuration delegate.
423 *
424 * @return the delegate
425 */
426 protected FileConfigurationDelegate getDelegate()
427 {
428 return delegate;
429 }
430
431 /**
432 * Allows to set the file configuration delegate.
433 * @param delegate the new delegate
434 */
435 protected void setDelegate(FileConfigurationDelegate delegate)
436 {
437 this.delegate = delegate;
438 }
439
440 /**
441 * A special implementation of the <code>FileConfiguration</code> interface that is
442 * used internally to implement the <code>FileConfiguration</code> methods
443 * for hierarchical configurations.
444 */
445 protected class FileConfigurationDelegate extends AbstractFileConfiguration
446 {
447 public void load(Reader in) throws ConfigurationException
448 {
449 AbstractHierarchicalFileConfiguration.this.load(in);
450 }
451
452 public void save(Writer out) throws ConfigurationException
453 {
454 AbstractHierarchicalFileConfiguration.this.save(out);
455 }
456
457 public void clear()
458 {
459 AbstractHierarchicalFileConfiguration.this.clear();
460 }
461 }
462 }