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    }