Source Code Cross Referenced for HierarchicalConfiguration.java in  » Library » Apache-commons-configuration-1.4-src » org » apache » commons » configuration » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Library » Apache commons configuration 1.4 src » org.apache.commons.configuration 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Licensed to the Apache Software Foundation (ASF) under one or more
0003:         * contributor license agreements.  See the NOTICE file distributed with
0004:         * this work for additional information regarding copyright ownership.
0005:         * The ASF licenses this file to You under the Apache License, Version 2.0
0006:         * (the "License"); you may not use this file except in compliance with
0007:         * the License.  You may obtain a copy of the License at
0008:         *
0009:         *     http://www.apache.org/licenses/LICENSE-2.0
0010:         *
0011:         * Unless required by applicable law or agreed to in writing, software
0012:         * distributed under the License is distributed on an "AS IS" BASIS,
0013:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014:         * See the License for the specific language governing permissions and
0015:         * limitations under the License.
0016:         */
0017:
0018:        package org.apache.commons.configuration;
0019:
0020:        import java.io.Serializable;
0021:        import java.util.ArrayList;
0022:        import java.util.Collection;
0023:        import java.util.Iterator;
0024:        import java.util.LinkedList;
0025:        import java.util.List;
0026:        import java.util.Set;
0027:        import java.util.Stack;
0028:
0029:        import org.apache.commons.collections.set.ListOrderedSet;
0030:        import org.apache.commons.collections.iterators.SingletonIterator;
0031:        import org.apache.commons.configuration.tree.ConfigurationNode;
0032:        import org.apache.commons.configuration.tree.ConfigurationNodeVisitorAdapter;
0033:        import org.apache.commons.configuration.tree.DefaultConfigurationNode;
0034:        import org.apache.commons.configuration.tree.DefaultExpressionEngine;
0035:        import org.apache.commons.configuration.tree.ExpressionEngine;
0036:        import org.apache.commons.configuration.tree.NodeAddData;
0037:        import org.apache.commons.lang.StringUtils;
0038:
0039:        /**
0040:         * <p>A specialized configuration class that extends its base class by the
0041:         * ability of keeping more structure in the stored properties.</p><p>There
0042:         * are some sources of configuration data that cannot be stored very well in a
0043:         * <code>BaseConfiguration</code> object because then their structure is lost.
0044:         * This is especially true for XML documents. This class can deal with such
0045:         * structured configuration sources by storing the properties in a tree-like
0046:         * organization.</p><p>The internal used storage form allows for a more
0047:         * sophisticated access to single properties. As an example consider the
0048:         * following XML document:</p><p>
0049:         *
0050:         * <pre>
0051:         * &lt;database&gt;
0052:         *   &lt;tables&gt;
0053:         *     &lt;table&gt;
0054:         *       &lt;name&gt;users&lt;/name&gt;
0055:         *       &lt;fields&gt;
0056:         *         &lt;field&gt;
0057:         *           &lt;name&gt;lid&lt;/name&gt;
0058:         *           &lt;type&gt;long&lt;/name&gt;
0059:         *         &lt;/field&gt;
0060:         *         &lt;field&gt;
0061:         *           &lt;name&gt;usrName&lt;/name&gt;
0062:         *           &lt;type&gt;java.lang.String&lt;/type&gt;
0063:         *         &lt;/field&gt;
0064:         *        ...
0065:         *       &lt;/fields&gt;
0066:         *     &lt;/table&gt;
0067:         *     &lt;table&gt;
0068:         *       &lt;name&gt;documents&lt;/name&gt;
0069:         *       &lt;fields&gt;
0070:         *         &lt;field&gt;
0071:         *           &lt;name&gt;docid&lt;/name&gt;
0072:         *           &lt;type&gt;long&lt;/type&gt;
0073:         *         &lt;/field&gt;
0074:         *         ...
0075:         *       &lt;/fields&gt;
0076:         *     &lt;/table&gt;
0077:         *     ...
0078:         *   &lt;/tables&gt;
0079:         * &lt;/database&gt;
0080:         * </pre>
0081:         *
0082:         * </p><p>If this document is parsed and stored in a
0083:         * <code>HierarchicalConfiguration</code> object (which can be done by one of
0084:         * the sub classes), there are enhanced possibilities of accessing properties.
0085:         * The keys for querying information can contain indices that select a certain
0086:         * element if there are multiple hits.</p><p>For instance the key
0087:         * <code>tables.table(0).name</code> can be used to find out the name of the
0088:         * first table. In opposite <code>tables.table.name</code> would return a
0089:         * collection with the names of all available tables. Similarily the key
0090:         * <code>tables.table(1).fields.field.name</code> returns a collection with
0091:         * the names of all fields of the second table. If another index is added after
0092:         * the <code>field</code> element, a single field can be accessed:
0093:         * <code>tables.table(1).fields.field(0).name</code>.</p><p>There is a
0094:         * <code>getMaxIndex()</code> method that returns the maximum allowed index
0095:         * that can be added to a given property key. This method can be used to iterate
0096:         * over all values defined for a certain property.</p>
0097:         *
0098:         * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger </a>
0099:         * @version $Id: HierarchicalConfiguration.java,v 1.14 2004/12/02 22:05:52
0100:         * ebourg Exp $
0101:         */
0102:        public class HierarchicalConfiguration extends AbstractConfiguration
0103:                implements  Serializable, Cloneable {
0104:            /** Constant for the clear tree event.*/
0105:            public static final int EVENT_CLEAR_TREE = 10;
0106:
0107:            /** Constant for the add nodes event.*/
0108:            public static final int EVENT_ADD_NODES = 11;
0109:
0110:            /**
0111:             * The serial version UID.
0112:             */
0113:            private static final long serialVersionUID = 3373812230395363192L;
0114:
0115:            /** Stores the default expression engine to be used for new objects.*/
0116:            private static ExpressionEngine defaultExpressionEngine = new DefaultExpressionEngine();
0117:
0118:            /** Stores the root node of this configuration. This field is required for
0119:             * backwards compatibility only.
0120:             */
0121:            private Node root;
0122:
0123:            /** Stores the root configuration node.*/
0124:            private ConfigurationNode rootNode;
0125:
0126:            /** Stores the expression engine for this instance.*/
0127:            private ExpressionEngine expressionEngine;
0128:
0129:            /**
0130:             * Creates a new instance of <code>HierarchicalConfiguration</code>.
0131:             */
0132:            public HierarchicalConfiguration() {
0133:                setRootNode(new Node());
0134:            }
0135:
0136:            /**
0137:             * Creates a new instance of <code>HierarchicalConfiguration</code> and
0138:             * copies all data contained in the specified configuration into the new
0139:             * one.
0140:             *
0141:             * @param c the configuration that is to be copied (if <b>null</b>, this
0142:             * constructor will behave like the standard constructor)
0143:             * @since 1.4
0144:             */
0145:            public HierarchicalConfiguration(HierarchicalConfiguration c) {
0146:                this ();
0147:                if (c != null) {
0148:                    CloneVisitor visitor = new CloneVisitor();
0149:                    c.getRootNode().visit(visitor);
0150:                    setRootNode(visitor.getClone());
0151:                }
0152:            }
0153:
0154:            /**
0155:             * Returns the root node of this hierarchical configuration. This method
0156:             * exists for backwards compatibility only. New code should use the
0157:             * <code>{@link #getRootNode()}</code> method instead, which operates on
0158:             * the preferred data type <code>ConfigurationNode</code>.
0159:             *
0160:             * @return the root node
0161:             */
0162:            public Node getRoot() {
0163:                return root;
0164:            }
0165:
0166:            /**
0167:             * Sets the root node of this hierarchical configuration. This method
0168:             * exists for backwards compatibility only. New code should use the
0169:             * <code>{@link #setRootNode(ConfigurationNode)}</code> method instead,
0170:             * which operates on the preferred data type <code>ConfigurationNode</code>.
0171:             *
0172:             * @param node the root node
0173:             */
0174:            public void setRoot(Node node) {
0175:                if (node == null) {
0176:                    throw new IllegalArgumentException(
0177:                            "Root node must not be null!");
0178:                }
0179:                root = node;
0180:                rootNode = null;
0181:            }
0182:
0183:            /**
0184:             * Returns the root node of this hierarchical configuration.
0185:             *
0186:             * @return the root node
0187:             * @since 1.3
0188:             */
0189:            public ConfigurationNode getRootNode() {
0190:                return (rootNode != null) ? rootNode : root;
0191:            }
0192:
0193:            /**
0194:             * Sets the root node of this hierarchical configuration.
0195:             *
0196:             * @param rootNode the root node
0197:             * @since 1.3
0198:             */
0199:            public void setRootNode(ConfigurationNode rootNode) {
0200:                if (rootNode == null) {
0201:                    throw new IllegalArgumentException(
0202:                            "Root node must not be null!");
0203:                }
0204:                this .rootNode = rootNode;
0205:
0206:                // For backward compatibility also set the old root field.
0207:                root = (rootNode instanceof  Node) ? (Node) rootNode : new Node(
0208:                        rootNode);
0209:            }
0210:
0211:            /**
0212:             * Returns the default expression engine.
0213:             *
0214:             * @return the default expression engine
0215:             * @since 1.3
0216:             */
0217:            public static ExpressionEngine getDefaultExpressionEngine() {
0218:                return defaultExpressionEngine;
0219:            }
0220:
0221:            /**
0222:             * Sets the default expression engine. This expression engine will be used
0223:             * if no specific engine was set for an instance. It is shared between all
0224:             * hierarchical configuration instances. So modifying its properties will
0225:             * impact all instances, for which no specific engine is set.
0226:             *
0227:             * @param engine the new default expression engine
0228:             * @since 1.3
0229:             */
0230:            public static void setDefaultExpressionEngine(
0231:                    ExpressionEngine engine) {
0232:                if (engine == null) {
0233:                    throw new IllegalArgumentException(
0234:                            "Default expression engine must not be null!");
0235:                }
0236:                defaultExpressionEngine = engine;
0237:            }
0238:
0239:            /**
0240:             * Returns the expression engine used by this configuration. This method
0241:             * will never return <b>null</b>; if no specific expression engine was set,
0242:             * the default expression engine will be returned.
0243:             *
0244:             * @return the current expression engine
0245:             * @since 1.3
0246:             */
0247:            public ExpressionEngine getExpressionEngine() {
0248:                return (expressionEngine != null) ? expressionEngine
0249:                        : getDefaultExpressionEngine();
0250:            }
0251:
0252:            /**
0253:             * Sets the expression engine to be used by this configuration. All property
0254:             * keys this configuration has to deal with will be interpreted by this
0255:             * engine.
0256:             *
0257:             * @param expressionEngine the new expression engine; can be <b>null</b>,
0258:             * then the default expression engine will be used
0259:             * @since 1.3
0260:             */
0261:            public void setExpressionEngine(ExpressionEngine expressionEngine) {
0262:                this .expressionEngine = expressionEngine;
0263:            }
0264:
0265:            /**
0266:             * Fetches the specified property. This task is delegated to the associated
0267:             * expression engine.
0268:             *
0269:             * @param key the key to be looked up
0270:             * @return the found value
0271:             */
0272:            public Object getProperty(String key) {
0273:                List nodes = fetchNodeList(key);
0274:
0275:                if (nodes.size() == 0) {
0276:                    return null;
0277:                } else {
0278:                    List list = new ArrayList();
0279:                    for (Iterator it = nodes.iterator(); it.hasNext();) {
0280:                        ConfigurationNode node = (ConfigurationNode) it.next();
0281:                        if (node.getValue() != null) {
0282:                            list.add(node.getValue());
0283:                        }
0284:                    }
0285:
0286:                    if (list.size() < 1) {
0287:                        return null;
0288:                    } else {
0289:                        return (list.size() == 1) ? list.get(0) : list;
0290:                    }
0291:                }
0292:            }
0293:
0294:            /**
0295:             * Adds the property with the specified key. This task will be delegated to
0296:             * the associated <code>ExpressionEngine</code>, so the passed in key
0297:             * must match the requirements of this implementation.
0298:             *
0299:             * @param key the key of the new property
0300:             * @param obj the value of the new property
0301:             */
0302:            protected void addPropertyDirect(String key, Object obj) {
0303:                NodeAddData data = getExpressionEngine().prepareAdd(
0304:                        getRootNode(), key);
0305:                ConfigurationNode node = processNodeAddData(data);
0306:                node.setValue(obj);
0307:            }
0308:
0309:            /**
0310:             * Adds a collection of nodes at the specified position of the configuration
0311:             * tree. This method works similar to <code>addProperty()</code>, but
0312:             * instead of a single property a whole collection of nodes can be added -
0313:             * and thus complete configuration sub trees. E.g. with this method it is
0314:             * possible to add parts of another <code>HierarchicalConfiguration</code>
0315:             * object to this object. If the passed in key refers to an existing and
0316:             * unique node, the new nodes are added to this node. Otherwise a new node
0317:             * will be created at the specified position in the hierarchy.
0318:             *
0319:             * @param key the key where the nodes are to be added; can be <b>null </b>,
0320:             * then they are added to the root node
0321:             * @param nodes a collection with the <code>Node</code> objects to be
0322:             * added
0323:             */
0324:            public void addNodes(String key, Collection nodes) {
0325:                if (nodes == null || nodes.isEmpty()) {
0326:                    return;
0327:                }
0328:
0329:                fireEvent(EVENT_ADD_NODES, key, nodes, true);
0330:                ConfigurationNode parent;
0331:                List target = fetchNodeList(key);
0332:                if (target.size() == 1) {
0333:                    // existing unique key
0334:                    parent = (ConfigurationNode) target.get(0);
0335:                } else {
0336:                    // otherwise perform an add operation
0337:                    parent = processNodeAddData(getExpressionEngine()
0338:                            .prepareAdd(getRootNode(), key));
0339:                }
0340:
0341:                if (parent.isAttribute()) {
0342:                    throw new IllegalArgumentException(
0343:                            "Cannot add nodes to an attribute node!");
0344:                }
0345:                for (Iterator it = nodes.iterator(); it.hasNext();) {
0346:                    ConfigurationNode child = (ConfigurationNode) it.next();
0347:                    if (child.isAttribute()) {
0348:                        parent.addAttribute(child);
0349:                    } else {
0350:                        parent.addChild(child);
0351:                    }
0352:                }
0353:                fireEvent(EVENT_ADD_NODES, key, nodes, false);
0354:            }
0355:
0356:            /**
0357:             * Checks if this configuration is empty. Empty means that there are no keys
0358:             * with any values, though there can be some (empty) nodes.
0359:             *
0360:             * @return a flag if this configuration is empty
0361:             */
0362:            public boolean isEmpty() {
0363:                return !nodeDefined(getRootNode());
0364:            }
0365:
0366:            /**
0367:             * Creates a new <code>Configuration</code> object containing all keys
0368:             * that start with the specified prefix. This implementation will return a
0369:             * <code>HierarchicalConfiguration</code> object so that the structure of
0370:             * the keys will be saved.
0371:             *
0372:             * @param prefix the prefix of the keys for the subset
0373:             * @return a new configuration object representing the selected subset
0374:             */
0375:            public Configuration subset(String prefix) {
0376:                Collection nodes = fetchNodeList(prefix);
0377:                if (nodes.isEmpty()) {
0378:                    return new HierarchicalConfiguration();
0379:                }
0380:
0381:                final HierarchicalConfiguration parent = this ;
0382:                HierarchicalConfiguration result = new HierarchicalConfiguration() {
0383:                    // Override interpolate to always interpolate on the parent
0384:                    protected Object interpolate(Object value) {
0385:                        return parent.interpolate(value);
0386:                    }
0387:                };
0388:                CloneVisitor visitor = new CloneVisitor();
0389:
0390:                for (Iterator it = nodes.iterator(); it.hasNext();) {
0391:                    ConfigurationNode nd = (ConfigurationNode) it.next();
0392:                    nd.visit(visitor);
0393:
0394:                    for (Iterator it2 = visitor.getClone().getChildren()
0395:                            .iterator(); it2.hasNext();) {
0396:                        result.getRootNode().addChild(
0397:                                (ConfigurationNode) it2.next());
0398:                    }
0399:                    for (Iterator it2 = visitor.getClone().getAttributes()
0400:                            .iterator(); it2.hasNext();) {
0401:                        result.getRootNode().addAttribute(
0402:                                (ConfigurationNode) it2.next());
0403:                    }
0404:                }
0405:
0406:                return (result.isEmpty()) ? new HierarchicalConfiguration()
0407:                        : result;
0408:            }
0409:
0410:            /**
0411:             * <p>
0412:             * Returns a hierarchical subnode configuration object that wraps the
0413:             * configuration node specified by the given key. This method provides an
0414:             * easy means of accessing sub trees of a hierarchical configuration. In the
0415:             * returned configuration the sub tree can directly be accessed, it becomes
0416:             * the root node of this configuration. Because of this the passed in key
0417:             * must select exactly one configuration node; otherwise an
0418:             * <code>IllegalArgumentException</code> will be thrown.
0419:             * </p>
0420:             * <p>
0421:             * The difference between this method and the
0422:             * <code>{@link #subset(String)}</code> method is that
0423:             * <code>subset()</code> supports arbitrary subsets of configuration nodes
0424:             * while <code>configurationAt()</code> only returns a single sub tree.
0425:             * Please refer to the documentation of the
0426:             * <code>SubnodeConfiguration</code> class to obtain further information
0427:             * about subnode configurations and when they should be used.
0428:             * </p>
0429:             *
0430:             * @param key the key that selects the sub tree
0431:             * @return a hierarchical configuration that contains this sub tree
0432:             * @see SubnodeConfiguration
0433:             * @since 1.3
0434:             */
0435:            public SubnodeConfiguration configurationAt(String key) {
0436:                List nodes = fetchNodeList(key);
0437:                if (nodes.size() != 1) {
0438:                    throw new IllegalArgumentException(
0439:                            "Passed in key must select exactly one node: "
0440:                                    + key);
0441:                }
0442:                return createSubnodeConfiguration((ConfigurationNode) nodes
0443:                        .get(0));
0444:            }
0445:
0446:            /**
0447:             * Returns a list of sub configurations for all configuration nodes selected
0448:             * by the given key. This method will evaluate the passed in key (using the
0449:             * current <code>ExpressionEngine</code>) and then create a subnode
0450:             * configuration for each returned node (like
0451:             * <code>{@link #configurationAt(String)}</code>}). This is especially
0452:             * useful when dealing with list-like structures. As an example consider the
0453:             * configuration that contains data about database tables and their fields.
0454:             * If you need access to all fields of a certain table, you can simply do
0455:             *
0456:             * <pre>
0457:             * List fields = config.configurationsAt("tables.table(0).fields.field");
0458:             * for(Iterator it = fields.iterator(); it.hasNext();)
0459:             * {
0460:             *     HierarchicalConfiguration sub = (HierarchicalConfiguration) it.next();
0461:             *     // now the children and attributes of the field node can be
0462:             *     // directly accessed
0463:             *     String fieldName = sub.getString("name");
0464:             *     String fieldType = sub.getString("type");
0465:             *     ...
0466:             * </pre>
0467:             *
0468:             * @param key the key for selecting the desired nodes
0469:             * @return a list with hierarchical configuration objects; each
0470:             * configuration represents one of the nodes selected by the passed in key
0471:             * @since 1.3
0472:             */
0473:            public List configurationsAt(String key) {
0474:                List nodes = fetchNodeList(key);
0475:                List configs = new ArrayList(nodes.size());
0476:                for (Iterator it = nodes.iterator(); it.hasNext();) {
0477:                    configs
0478:                            .add(createSubnodeConfiguration((ConfigurationNode) it
0479:                                    .next()));
0480:                }
0481:                return configs;
0482:            }
0483:
0484:            /**
0485:             * Creates a subnode configuration for the specified node. This method is
0486:             * called by <code>configurationAt()</code> and
0487:             * <code>configurationsAt()</code>.
0488:             *
0489:             * @param node the node, for which a subnode configuration is to be created
0490:             * @return the configuration for the given node
0491:             * @since 1.3
0492:             */
0493:            protected SubnodeConfiguration createSubnodeConfiguration(
0494:                    ConfigurationNode node) {
0495:                return new SubnodeConfiguration(this , node);
0496:            }
0497:
0498:            /**
0499:             * Checks if the specified key is contained in this configuration. Note that
0500:             * for this configuration the term &quot;contained&quot; means that the key
0501:             * has an associated value. If there is a node for this key that has no
0502:             * value but children (either defined or undefined), this method will still
0503:             * return <b>false </b>.
0504:             *
0505:             * @param key the key to be chekced
0506:             * @return a flag if this key is contained in this configuration
0507:             */
0508:            public boolean containsKey(String key) {
0509:                return getProperty(key) != null;
0510:            }
0511:
0512:            /**
0513:             * Sets the value of the specified property.
0514:             *
0515:             * @param key the key of the property to set
0516:             * @param value the new value of this property
0517:             */
0518:            public void setProperty(String key, Object value) {
0519:                fireEvent(EVENT_SET_PROPERTY, key, value, true);
0520:
0521:                Iterator itNodes = fetchNodeList(key).iterator();
0522:                Iterator itValues;
0523:                if (!isDelimiterParsingDisabled()) {
0524:                    itValues = PropertyConverter.toIterator(value,
0525:                            getListDelimiter());
0526:                } else {
0527:                    itValues = new SingletonIterator(value);
0528:                }
0529:                while (itNodes.hasNext() && itValues.hasNext()) {
0530:                    ((ConfigurationNode) itNodes.next()).setValue(itValues
0531:                            .next());
0532:                }
0533:
0534:                // Add additional nodes if necessary
0535:                while (itValues.hasNext()) {
0536:                    addPropertyDirect(key, itValues.next());
0537:                }
0538:
0539:                // Remove remaining nodes
0540:                while (itNodes.hasNext()) {
0541:                    clearNode((ConfigurationNode) itNodes.next());
0542:                }
0543:
0544:                fireEvent(EVENT_SET_PROPERTY, key, value, false);
0545:            }
0546:
0547:            /**
0548:             * Removes all values of the property with the given name and of keys that
0549:             * start with this name. So if there is a property with the key
0550:             * &quot;foo&quot; and a property with the key &quot;foo.bar&quot;, a call
0551:             * of <code>clearTree("foo")</code> would remove both properties.
0552:             *
0553:             * @param key the key of the property to be removed
0554:             */
0555:            public void clearTree(String key) {
0556:                fireEvent(EVENT_CLEAR_TREE, key, null, true);
0557:                List nodes = fetchNodeList(key);
0558:
0559:                for (Iterator it = nodes.iterator(); it.hasNext();) {
0560:                    removeNode((ConfigurationNode) it.next());
0561:                }
0562:                fireEvent(EVENT_CLEAR_TREE, key, nodes, false);
0563:            }
0564:
0565:            /**
0566:             * Removes the property with the given key. Properties with names that start
0567:             * with the given key (i.e. properties below the specified key in the
0568:             * hierarchy) won't be affected.
0569:             *
0570:             * @param key the key of the property to be removed
0571:             */
0572:            public void clearProperty(String key) {
0573:                fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);
0574:                List nodes = fetchNodeList(key);
0575:
0576:                for (Iterator it = nodes.iterator(); it.hasNext();) {
0577:                    clearNode((ConfigurationNode) it.next());
0578:                }
0579:
0580:                fireEvent(EVENT_CLEAR_PROPERTY, key, null, false);
0581:            }
0582:
0583:            /**
0584:             * Returns an iterator with all keys defined in this configuration.
0585:             * Note that the keys returned by this method will not contain any
0586:             * indices. This means that some structure will be lost.</p>
0587:             *
0588:             * @return an iterator with the defined keys in this configuration
0589:             */
0590:            public Iterator getKeys() {
0591:                DefinedKeysVisitor visitor = new DefinedKeysVisitor();
0592:                getRootNode().visit(visitor);
0593:
0594:                return visitor.getKeyList().iterator();
0595:            }
0596:
0597:            /**
0598:             * Returns an iterator with all keys defined in this configuration that
0599:             * start with the given prefix. The returned keys will not contain any
0600:             * indices.
0601:             *
0602:             * @param prefix the prefix of the keys to start with
0603:             * @return an iterator with the found keys
0604:             */
0605:            public Iterator getKeys(String prefix) {
0606:                DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix);
0607:                List nodes = fetchNodeList(prefix);
0608:
0609:                for (Iterator itNodes = nodes.iterator(); itNodes.hasNext();) {
0610:                    ConfigurationNode node = (ConfigurationNode) itNodes.next();
0611:                    for (Iterator it = node.getChildren().iterator(); it
0612:                            .hasNext();) {
0613:                        ((ConfigurationNode) it.next()).visit(visitor);
0614:                    }
0615:                    for (Iterator it = node.getAttributes().iterator(); it
0616:                            .hasNext();) {
0617:                        ((ConfigurationNode) it.next()).visit(visitor);
0618:                    }
0619:                }
0620:
0621:                return visitor.getKeyList().iterator();
0622:            }
0623:
0624:            /**
0625:             * Returns the maximum defined index for the given key. This is useful if
0626:             * there are multiple values for this key. They can then be addressed
0627:             * separately by specifying indices from 0 to the return value of this
0628:             * method.
0629:             *
0630:             * @param key the key to be checked
0631:             * @return the maximum defined index for this key
0632:             */
0633:            public int getMaxIndex(String key) {
0634:                return fetchNodeList(key).size() - 1;
0635:            }
0636:
0637:            /**
0638:             * Creates a copy of this object. This new configuration object will contain
0639:             * copies of all nodes in the same structure. Registered event listeners
0640:             * won't be cloned; so they are not registered at the returned copy.
0641:             *
0642:             * @return the copy
0643:             * @since 1.2
0644:             */
0645:            public Object clone() {
0646:                try {
0647:                    HierarchicalConfiguration copy = (HierarchicalConfiguration) super 
0648:                            .clone();
0649:
0650:                    // clone the nodes, too
0651:                    CloneVisitor v = new CloneVisitor();
0652:                    getRootNode().visit(v);
0653:                    copy.setRootNode(v.getClone());
0654:
0655:                    return copy;
0656:                } catch (CloneNotSupportedException cex) {
0657:                    // should not happen
0658:                    throw new ConfigurationRuntimeException(cex);
0659:                }
0660:            }
0661:
0662:            /**
0663:             * Helper method for fetching a list of all nodes that are addressed by the
0664:             * specified key.
0665:             *
0666:             * @param key the key
0667:             * @return a list with all affected nodes (never <b>null </b>)
0668:             */
0669:            protected List fetchNodeList(String key) {
0670:                return getExpressionEngine().query(getRootNode(), key);
0671:            }
0672:
0673:            /**
0674:             * Recursive helper method for fetching a property. This method processes
0675:             * all facets of a configuration key, traverses the tree of properties and
0676:             * fetches the the nodes of all matching properties.
0677:             *
0678:             * @param keyPart the configuration key iterator
0679:             * @param node the actual node
0680:             * @param nodes here the found nodes are stored
0681:             * @deprecated Property keys are now evaluated by the expression engine
0682:             * associated with the configuration; this method will no longer be called.
0683:             * If you want to modify the way properties are looked up, consider
0684:             * implementing you own <code>ExpressionEngine</code> implementation.
0685:             */
0686:            protected void findPropertyNodes(
0687:                    ConfigurationKey.KeyIterator keyPart, Node node,
0688:                    Collection nodes) {
0689:            }
0690:
0691:            /**
0692:             * Checks if the specified node is defined.
0693:             *
0694:             * @param node the node to be checked
0695:             * @return a flag if this node is defined
0696:             * @deprecated Use the method <code>{@link #nodeDefined(ConfigurationNode)}</code>
0697:             * instead.
0698:             */
0699:            protected boolean nodeDefined(Node node) {
0700:                return nodeDefined((ConfigurationNode) node);
0701:            }
0702:
0703:            /**
0704:             * Checks if the specified node is defined.
0705:             *
0706:             * @param node the node to be checked
0707:             * @return a flag if this node is defined
0708:             */
0709:            protected boolean nodeDefined(ConfigurationNode node) {
0710:                DefinedVisitor visitor = new DefinedVisitor();
0711:                node.visit(visitor);
0712:                return visitor.isDefined();
0713:            }
0714:
0715:            /**
0716:             * Removes the specified node from this configuration. This method ensures
0717:             * that parent nodes that become undefined by this operation are also
0718:             * removed.
0719:             *
0720:             * @param node the node to be removed
0721:             * @deprecated Use the method <code>{@link #removeNode(ConfigurationNode)}</code>
0722:             * instead.
0723:             */
0724:            protected void removeNode(Node node) {
0725:                removeNode((ConfigurationNode) node);
0726:            }
0727:
0728:            /**
0729:             * Removes the specified node from this configuration. This method ensures
0730:             * that parent nodes that become undefined by this operation are also
0731:             * removed.
0732:             *
0733:             * @param node the node to be removed
0734:             */
0735:            protected void removeNode(ConfigurationNode node) {
0736:                ConfigurationNode parent = node.getParentNode();
0737:                if (parent != null) {
0738:                    parent.removeChild(node);
0739:                    if (!nodeDefined(parent)) {
0740:                        removeNode(parent);
0741:                    }
0742:                }
0743:            }
0744:
0745:            /**
0746:             * Clears the value of the specified node. If the node becomes undefined by
0747:             * this operation, it is removed from the hierarchy.
0748:             *
0749:             * @param node the node to be cleard
0750:             * @deprecated Use the method <code>{@link #clearNode(ConfigurationNode)}</code>
0751:             * instead
0752:             */
0753:            protected void clearNode(Node node) {
0754:                clearNode((ConfigurationNode) node);
0755:            }
0756:
0757:            /**
0758:             * Clears the value of the specified node. If the node becomes undefined by
0759:             * this operation, it is removed from the hierarchy.
0760:             *
0761:             * @param node the node to be cleard
0762:             */
0763:            protected void clearNode(ConfigurationNode node) {
0764:                node.setValue(null);
0765:                if (!nodeDefined(node)) {
0766:                    removeNode(node);
0767:                }
0768:            }
0769:
0770:            /**
0771:             * Returns a reference to the parent node of an add operation. Nodes for new
0772:             * properties can be added as children of this node. If the path for the
0773:             * specified key does not exist so far, it is created now.
0774:             *
0775:             * @param keyIt the iterator for the key of the new property
0776:             * @param startNode the node to start the search with
0777:             * @return the parent node for the add operation
0778:             * @deprecated Adding new properties is now to a major part delegated to the
0779:             * <code>ExpressionEngine</code> associated with this configuration instance.
0780:             * This method will no longer be called. Developers who want to modify the
0781:             * process of adding new properties should consider implementing their own
0782:             * expression engine.
0783:             */
0784:            protected Node fetchAddNode(ConfigurationKey.KeyIterator keyIt,
0785:                    Node startNode) {
0786:                return null;
0787:            }
0788:
0789:            /**
0790:             * Finds the last existing node for an add operation. This method traverses
0791:             * the configuration tree along the specified key. The last existing node on
0792:             * this path is returned.
0793:             *
0794:             * @param keyIt the key iterator
0795:             * @param node the actual node
0796:             * @return the last existing node on the given path
0797:             * @deprecated Adding new properties is now to a major part delegated to the
0798:             * <code>ExpressionEngine</code> associated with this configuration instance.
0799:             * This method will no longer be called. Developers who want to modify the
0800:             * process of adding new properties should consider implementing their own
0801:             * expression engine.
0802:             */
0803:            protected Node findLastPathNode(ConfigurationKey.KeyIterator keyIt,
0804:                    Node node) {
0805:                return null;
0806:            }
0807:
0808:            /**
0809:             * Creates the missing nodes for adding a new property. This method ensures
0810:             * that there are corresponding nodes for all components of the specified
0811:             * configuration key.
0812:             *
0813:             * @param keyIt the key iterator
0814:             * @param root the base node of the path to be created
0815:             * @return the last node of the path
0816:             * @deprecated Adding new properties is now to a major part delegated to the
0817:             * <code>ExpressionEngine</code> associated with this configuration instance.
0818:             * This method will no longer be called. Developers who want to modify the
0819:             * process of adding new properties should consider implementing their own
0820:             * expression engine.
0821:             */
0822:            protected Node createAddPath(ConfigurationKey.KeyIterator keyIt,
0823:                    Node root) {
0824:                return null;
0825:            }
0826:
0827:            /**
0828:             * Creates a new <code>Node</code> object with the specified name. This
0829:             * method can be overloaded in derived classes if a specific node type is
0830:             * needed. This base implementation always returns a new object of the
0831:             * <code>Node</code> class.
0832:             *
0833:             * @param name the name of the new node
0834:             * @return the new node
0835:             */
0836:            protected Node createNode(String name) {
0837:                return new Node(name);
0838:            }
0839:
0840:            /**
0841:             * Helper method for processing a node add data object obtained from the
0842:             * expression engine. This method will create all new nodes.
0843:             *
0844:             * @param data the data object
0845:             * @return the new node
0846:             * @since 1.3
0847:             */
0848:            private ConfigurationNode processNodeAddData(NodeAddData data) {
0849:                ConfigurationNode node = data.getParent();
0850:
0851:                // Create missing nodes on the path
0852:                for (Iterator it = data.getPathNodes().iterator(); it.hasNext();) {
0853:                    ConfigurationNode child = createNode((String) it.next());
0854:                    node.addChild(child);
0855:                    node = child;
0856:                }
0857:
0858:                // Add new target node
0859:                ConfigurationNode child = createNode(data.getNewNodeName());
0860:                if (data.isAttribute()) {
0861:                    node.addAttribute(child);
0862:                } else {
0863:                    node.addChild(child);
0864:                }
0865:                return child;
0866:            }
0867:
0868:            /**
0869:             * Clears all reference fields in a node structure. A configuration node can
0870:             * store a so-called &quot;reference&quot;. The meaning of this data is
0871:             * determined by a concrete sub class. Typically such references are
0872:             * specific for a configuration instance. If this instance is cloned or
0873:             * copied, they must be cleared. This can be done using this method.
0874:             *
0875:             * @param node the root node of the node hierarchy, in which the references
0876:             * are to be cleared
0877:             * @since 1.4
0878:             */
0879:            protected static void clearReferences(ConfigurationNode node) {
0880:                node.visit(new ConfigurationNodeVisitorAdapter() {
0881:                    public void visitBeforeChildren(ConfigurationNode node) {
0882:                        node.setReference(null);
0883:                    }
0884:                });
0885:            }
0886:
0887:            /**
0888:             * A data class for storing (hierarchical) property information. A property
0889:             * can have a value and an arbitrary number of child properties. From
0890:             * version 1.3 on this class is only a thin wrapper over the
0891:             * <code>{@link org.apache.commons.configuration.tree.DefaultConfigurationNode DefaultconfigurationNode}</code>
0892:             * class that exists mainly for the purpose of backwards compatibility.
0893:             */
0894:            public static class Node extends DefaultConfigurationNode implements 
0895:                    Serializable {
0896:                /**
0897:                 * The serial version UID.
0898:                 */
0899:                private static final long serialVersionUID = -6357500633536941775L;
0900:
0901:                /**
0902:                 * Creates a new instance of <code>Node</code>.
0903:                 */
0904:                public Node() {
0905:                    super ();
0906:                }
0907:
0908:                /**
0909:                 * Creates a new instance of <code>Node</code> and sets the name.
0910:                 *
0911:                 * @param name the node's name
0912:                 */
0913:                public Node(String name) {
0914:                    super (name);
0915:                }
0916:
0917:                /**
0918:                 * Creates a new instance of <code>Node</code> and sets the name and the value.
0919:                 *
0920:                 * @param name the node's name
0921:                 * @param value the value
0922:                 */
0923:                public Node(String name, Object value) {
0924:                    super (name, value);
0925:                }
0926:
0927:                /**
0928:                 * Creates a new instance of <code>Node</code> based on the given
0929:                 * source node. All properties of the source node, including its
0930:                 * children and attributes, will be copied.
0931:                 *
0932:                 * @param src the node to be copied
0933:                 */
0934:                public Node(ConfigurationNode src) {
0935:                    this (src.getName(), src.getValue());
0936:                    setReference(src.getReference());
0937:                    for (Iterator it = src.getChildren().iterator(); it
0938:                            .hasNext();) {
0939:                        addChild((ConfigurationNode) it.next());
0940:                    }
0941:                    for (Iterator it = src.getAttributes().iterator(); it
0942:                            .hasNext();) {
0943:                        addAttribute((ConfigurationNode) it.next());
0944:                    }
0945:                }
0946:
0947:                /**
0948:                 * Returns the parent of this node.
0949:                 *
0950:                 * @return this node's parent (can be <b>null</b>)
0951:                 */
0952:                public Node getParent() {
0953:                    return (Node) getParentNode();
0954:                }
0955:
0956:                /**
0957:                 * Sets the parent of this node.
0958:                 *
0959:                 * @param node the parent node
0960:                 */
0961:                public void setParent(Node node) {
0962:                    setParentNode(node);
0963:                }
0964:
0965:                /**
0966:                 * Adds the given node to the children of this node.
0967:                 *
0968:                 * @param node the child to be added
0969:                 */
0970:                public void addChild(Node node) {
0971:                    addChild((ConfigurationNode) node);
0972:                }
0973:
0974:                /**
0975:                 * Returns a flag whether this node has child elements.
0976:                 *
0977:                 * @return <b>true</b> if there is a child node, <b>false</b> otherwise
0978:                 */
0979:                public boolean hasChildren() {
0980:                    return getChildrenCount() > 0 || getAttributeCount() > 0;
0981:                }
0982:
0983:                /**
0984:                 * Removes the specified child from this node.
0985:                 *
0986:                 * @param child the child node to be removed
0987:                 * @return a flag if the child could be found
0988:                 */
0989:                public boolean remove(Node child) {
0990:                    return child.isAttribute() ? removeAttribute(child)
0991:                            : removeChild(child);
0992:                }
0993:
0994:                /**
0995:                 * Removes all children with the given name.
0996:                 *
0997:                 * @param name the name of the children to be removed
0998:                 * @return a flag if children with this name existed
0999:                 */
1000:                public boolean remove(String name) {
1001:                    boolean childrenRemoved = removeChild(name);
1002:                    boolean attrsRemoved = removeAttribute(name);
1003:                    return childrenRemoved || attrsRemoved;
1004:                }
1005:
1006:                /**
1007:                 * A generic method for traversing this node and all of its children.
1008:                 * This method sends the passed in visitor to this node and all of its
1009:                 * children.
1010:                 *
1011:                 * @param visitor the visitor
1012:                 * @param key here a configuration key with the name of the root node of
1013:                 * the iteration can be passed; if this key is not <b>null </b>, the
1014:                 * full pathes to the visited nodes are builded and passed to the
1015:                 * visitor's <code>visit()</code> methods
1016:                 */
1017:                public void visit(NodeVisitor visitor, ConfigurationKey key) {
1018:                    int length = 0;
1019:                    if (key != null) {
1020:                        length = key.length();
1021:                        if (getName() != null) {
1022:                            key
1023:                                    .append(StringUtils
1024:                                            .replace(
1025:                                                    isAttribute() ? ConfigurationKey
1026:                                                            .constructAttributeKey(getName())
1027:                                                            : getName(),
1028:                                                    String
1029:                                                            .valueOf(ConfigurationKey.PROPERTY_DELIMITER),
1030:                                                    ConfigurationKey.ESCAPED_DELIMITER));
1031:                        }
1032:                    }
1033:
1034:                    visitor.visitBeforeChildren(this , key);
1035:
1036:                    for (Iterator it = getChildren().iterator(); it.hasNext()
1037:                            && !visitor.terminate();) {
1038:                        ((Node) it.next()).visit(visitor, key);
1039:                    }
1040:                    for (Iterator it = getAttributes().iterator(); it.hasNext()
1041:                            && !visitor.terminate();) {
1042:                        ((Node) it.next()).visit(visitor, key);
1043:                    }
1044:
1045:                    if (key != null) {
1046:                        key.setLength(length);
1047:                    }
1048:                    visitor.visitAfterChildren(this , key);
1049:                }
1050:            }
1051:
1052:            /**
1053:             * <p>Definition of a visitor class for traversing a node and all of its
1054:             * children.</p><p>This class defines the interface of a visitor for
1055:             * <code>Node</code> objects and provides a default implementation. The
1056:             * method <code>visit()</code> of <code>Node</code> implements a generic
1057:             * iteration algorithm based on the <em>Visitor</em> pattern. By providing
1058:             * different implementations of visitors it is possible to collect different
1059:             * data during the iteration process.</p>
1060:             *
1061:             */
1062:            public static class NodeVisitor {
1063:                /**
1064:                 * Visits the specified node. This method is called during iteration for
1065:                 * each node before its children have been visited.
1066:                 *
1067:                 * @param node the actual node
1068:                 * @param key the key of this node (may be <b>null </b>)
1069:                 */
1070:                public void visitBeforeChildren(Node node, ConfigurationKey key) {
1071:                }
1072:
1073:                /**
1074:                 * Visits the specified node after its children have been processed.
1075:                 * This gives a visitor the opportunity of collecting additional data
1076:                 * after the child nodes have been visited.
1077:                 *
1078:                 * @param node the node to be visited
1079:                 * @param key the key of this node (may be <b>null </b>)
1080:                 */
1081:                public void visitAfterChildren(Node node, ConfigurationKey key) {
1082:                }
1083:
1084:                /**
1085:                 * Returns a flag that indicates if iteration should be stopped. This
1086:                 * method is called after each visited node. It can be useful for
1087:                 * visitors that search a specific node. If this node is found, the
1088:                 * whole process can be stopped. This base implementation always returns
1089:                 * <b>false </b>.
1090:                 *
1091:                 * @return a flag if iteration should be stopped
1092:                 */
1093:                public boolean terminate() {
1094:                    return false;
1095:                }
1096:            }
1097:
1098:            /**
1099:             * A specialized visitor that checks if a node is defined.
1100:             * &quot;Defined&quot; in this terms means that the node or at least one of
1101:             * its sub nodes is associated with a value.
1102:             *
1103:             */
1104:            static class DefinedVisitor extends ConfigurationNodeVisitorAdapter {
1105:                /** Stores the defined flag. */
1106:                private boolean defined;
1107:
1108:                /**
1109:                 * Checks if iteration should be stopped. This can be done if the first
1110:                 * defined node is found.
1111:                 *
1112:                 * @return a flag if iteration should be stopped
1113:                 */
1114:                public boolean terminate() {
1115:                    return isDefined();
1116:                }
1117:
1118:                /**
1119:                 * Visits the node. Checks if a value is defined.
1120:                 *
1121:                 * @param node the actual node
1122:                 */
1123:                public void visitBeforeChildren(ConfigurationNode node) {
1124:                    defined = node.getValue() != null;
1125:                }
1126:
1127:                /**
1128:                 * Returns the defined flag.
1129:                 *
1130:                 * @return the defined flag
1131:                 */
1132:                public boolean isDefined() {
1133:                    return defined;
1134:                }
1135:            }
1136:
1137:            /**
1138:             * A specialized visitor that fills a list with keys that are defined in a
1139:             * node hierarchy.
1140:             */
1141:            class DefinedKeysVisitor extends ConfigurationNodeVisitorAdapter {
1142:                /** Stores the list to be filled. */
1143:                private Set keyList;
1144:
1145:                /** A stack with the keys of the already processed nodes. */
1146:                private Stack parentKeys;
1147:
1148:                /**
1149:                 * Default constructor.
1150:                 */
1151:                public DefinedKeysVisitor() {
1152:                    keyList = new ListOrderedSet();
1153:                    parentKeys = new Stack();
1154:                }
1155:
1156:                /**
1157:                 * Creates a new <code>DefinedKeysVisitor</code> instance and sets the
1158:                 * prefix for the keys to fetch.
1159:                 *
1160:                 * @param prefix the prefix
1161:                 */
1162:                public DefinedKeysVisitor(String prefix) {
1163:                    this ();
1164:                    parentKeys.push(prefix);
1165:                }
1166:
1167:                /**
1168:                 * Returns the list with all defined keys.
1169:                 *
1170:                 * @return the list with the defined keys
1171:                 */
1172:                public Set getKeyList() {
1173:                    return keyList;
1174:                }
1175:
1176:                /**
1177:                 * Visits the node after its children has been processed. Removes this
1178:                 * node's key from the stack.
1179:                 *
1180:                 * @param node the node
1181:                 */
1182:                public void visitAfterChildren(ConfigurationNode node) {
1183:                    parentKeys.pop();
1184:                }
1185:
1186:                /**
1187:                 * Visits the specified node. If this node has a value, its key is added
1188:                 * to the internal list.
1189:                 *
1190:                 * @param node the node to be visited
1191:                 */
1192:                public void visitBeforeChildren(ConfigurationNode node) {
1193:                    String parentKey = parentKeys.isEmpty() ? null
1194:                            : (String) parentKeys.peek();
1195:                    String key = getExpressionEngine().nodeKey(node, parentKey);
1196:                    parentKeys.push(key);
1197:                    if (node.getValue() != null) {
1198:                        keyList.add(key);
1199:                    }
1200:                }
1201:            }
1202:
1203:            /**
1204:             * A specialized visitor that is able to create a deep copy of a node
1205:             * hierarchy.
1206:             */
1207:            static class CloneVisitor extends ConfigurationNodeVisitorAdapter {
1208:                /** A stack with the actual object to be copied. */
1209:                private Stack copyStack;
1210:
1211:                /** Stores the result of the clone process. */
1212:                private ConfigurationNode result;
1213:
1214:                /**
1215:                 * Creates a new instance of <code>CloneVisitor</code>.
1216:                 */
1217:                public CloneVisitor() {
1218:                    copyStack = new Stack();
1219:                }
1220:
1221:                /**
1222:                 * Visits the specified node after its children have been processed.
1223:                 *
1224:                 * @param node the node
1225:                 */
1226:                public void visitAfterChildren(ConfigurationNode node) {
1227:                    ConfigurationNode copy = (ConfigurationNode) copyStack
1228:                            .pop();
1229:                    if (copyStack.isEmpty()) {
1230:                        result = copy;
1231:                    }
1232:                }
1233:
1234:                /**
1235:                 * Visits and copies the specified node.
1236:                 *
1237:                 * @param node the node
1238:                 */
1239:                public void visitBeforeChildren(ConfigurationNode node) {
1240:                    ConfigurationNode copy = (ConfigurationNode) node.clone();
1241:                    copy.setParentNode(null);
1242:
1243:                    if (!copyStack.isEmpty()) {
1244:                        if (node.isAttribute()) {
1245:                            ((ConfigurationNode) copyStack.peek())
1246:                                    .addAttribute(copy);
1247:                        } else {
1248:                            ((ConfigurationNode) copyStack.peek())
1249:                                    .addChild(copy);
1250:                        }
1251:                    }
1252:
1253:                    copyStack.push(copy);
1254:                }
1255:
1256:                /**
1257:                 * Returns the result of the clone process. This is the root node of the
1258:                 * cloned node hierarchy.
1259:                 *
1260:                 * @return the cloned root node
1261:                 */
1262:                public ConfigurationNode getClone() {
1263:                    return result;
1264:                }
1265:            }
1266:
1267:            /**
1268:             * A specialized visitor base class that can be used for storing the tree of
1269:             * configuration nodes. The basic idea is that each node can be associated
1270:             * with a reference object. This reference object has a concrete meaning in
1271:             * a derived class, e.g. an entry in a JNDI context or an XML element. When
1272:             * the configuration tree is set up, the <code>load()</code> method is
1273:             * responsible for setting the reference objects. When the configuration
1274:             * tree is later modified, new nodes do not have a defined reference object.
1275:             * This visitor class processes all nodes and finds the ones without a
1276:             * defined reference object. For those nodes the <code>insert()</code>
1277:             * method is called, which must be defined in concrete sub classes. This
1278:             * method can perform all steps to integrate the new node into the original
1279:             * structure.
1280:             *
1281:             */
1282:            protected abstract static class BuilderVisitor extends NodeVisitor {
1283:                /**
1284:                 * Visits the specified node before its children have been traversed.
1285:                 *
1286:                 * @param node the node to visit
1287:                 * @param key the current key
1288:                 */
1289:                public void visitBeforeChildren(Node node, ConfigurationKey key) {
1290:                    Collection subNodes = new LinkedList(node.getChildren());
1291:                    subNodes.addAll(node.getAttributes());
1292:                    Iterator children = subNodes.iterator();
1293:                    Node sibling1 = null;
1294:                    Node nd = null;
1295:
1296:                    while (children.hasNext()) {
1297:                        // find the next new node
1298:                        do {
1299:                            sibling1 = nd;
1300:                            nd = (Node) children.next();
1301:                        } while (nd.getReference() != null
1302:                                && children.hasNext());
1303:
1304:                        if (nd.getReference() == null) {
1305:                            // find all following new nodes
1306:                            List newNodes = new LinkedList();
1307:                            newNodes.add(nd);
1308:                            while (children.hasNext()) {
1309:                                nd = (Node) children.next();
1310:                                if (nd.getReference() == null) {
1311:                                    newNodes.add(nd);
1312:                                } else {
1313:                                    break;
1314:                                }
1315:                            }
1316:
1317:                            // Insert all new nodes
1318:                            Node sibling2 = (nd.getReference() == null) ? null
1319:                                    : nd;
1320:                            for (Iterator it = newNodes.iterator(); it
1321:                                    .hasNext();) {
1322:                                Node insertNode = (Node) it.next();
1323:                                if (insertNode.getReference() == null) {
1324:                                    Object ref = insert(insertNode, node,
1325:                                            sibling1, sibling2);
1326:                                    if (ref != null) {
1327:                                        insertNode.setReference(ref);
1328:                                    }
1329:                                    sibling1 = insertNode;
1330:                                }
1331:                            }
1332:                        }
1333:                    }
1334:                }
1335:
1336:                /**
1337:                 * Inserts a new node into the structure constructed by this builder.
1338:                 * This method is called for each node that has been added to the
1339:                 * configuration tree after the configuration has been loaded from its
1340:                 * source. These new nodes have to be inserted into the original
1341:                 * structure. The passed in nodes define the position of the node to be
1342:                 * inserted: its parent and the siblings between to insert. The return
1343:                 * value is interpreted as the new reference of the affected
1344:                 * <code>Node</code> object; if it is not <b>null </b>, it is passed
1345:                 * to the node's <code>setReference()</code> method.
1346:                 *
1347:                 * @param newNode the node to be inserted
1348:                 * @param parent the parent node
1349:                 * @param sibling1 the sibling after which the node is to be inserted;
1350:                 * can be <b>null </b> if the new node is going to be the first child
1351:                 * node
1352:                 * @param sibling2 the sibling before which the node is to be inserted;
1353:                 * can be <b>null </b> if the new node is going to be the last child
1354:                 * node
1355:                 * @return the reference object for the node to be inserted
1356:                 */
1357:                protected abstract Object insert(Node newNode, Node parent,
1358:                        Node sibling1, Node sibling2);
1359:            }
1360:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.