Source Code Cross Referenced for HierarchicalConfiguration.java in  » Forum » mvnforum-1.1 » net » myvietnam » mvncore » 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 » Forum » mvnforum 1.1 » net.myvietnam.mvncore.configuration 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        package net.myvietnam.mvncore.configuration;
002:
003:        /* ====================================================================
004:         * The Apache Software License, Version 1.1
005:         *
006:         * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
007:         * reserved.
008:         *
009:         * Redistribution and use in source and binary forms, with or without
010:         * modification, are permitted provided that the following conditions
011:         * are met:
012:         *
013:         * 1. Redistributions of source code must retain the above copyright
014:         *    notice, this list of conditions and the following disclaimer.
015:         *
016:         * 2. Redistributions in binary form must reproduce the above copyright
017:         *    notice, this list of conditions and the following disclaimer in
018:         *    the documentation and/or other materials provided with the
019:         *    distribution.
020:         *
021:         * 3. The end-user documentation included with the redistribution, if
022:         *    any, must include the following acknowledgement:
023:         *       "This product includes software developed by the
024:         *        Apache Software Foundation (http://www.apache.org/)."
025:         *    Alternately, this acknowledgement may appear in the software itself,
026:         *    if and wherever such third-party acknowledgements normally appear.
027:         *
028:         * 4. The names "The Jakarta Project", "Commons", and "Apache Software
029:         *    Foundation" must not be used to endorse or promote products derived
030:         *    from this software without prior written permission. For written
031:         *    permission, please contact apache@apache.org.
032:         *
033:         * 5. Products derived from this software may not be called "Apache"
034:         *    nor may "Apache" appear in their names without prior written
035:         *    permission of the Apache Software Foundation.
036:         *
037:         * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
038:         * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
039:         * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
040:         * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
041:         * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
042:         * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
043:         * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
044:         * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
045:         * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
046:         * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
047:         * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
048:         * SUCH DAMAGE.
049:         * ====================================================================
050:         *
051:         * This software consists of voluntary contributions made by many
052:         * individuals on behalf of the Apache Software Foundation.  For more
053:         * information on the Apache Software Foundation, please see
054:         * <http://www.apache.org/>.
055:         */
056:
057:        import java.io.Serializable;
058:        import java.util.ArrayList;
059:        import java.util.Collection;
060:        import java.util.HashSet;
061:        import java.util.Iterator;
062:        import java.util.LinkedList;
063:        import java.util.List;
064:        import java.util.Map;
065:        import java.util.Set;
066:        import java.util.Stack;
067:
068:        import org.apache.commons.collections.SequencedHashMap;
069:        import org.apache.commons.lang.StringUtils;
070:
071:        /**
072:         * <p>A specialized configuration class that extends its base class by the
073:         * ability of keeping more structure in the stored properties.</p>
074:         * <p>There are some sources of configuration data that cannot be stored
075:         * very well in a <code>BaseConfiguration</code> object because then their
076:         * structure is lost. This is especially true for XML documents. This class
077:         * can deal with such structured configuration sources by storing the
078:         * properties in a tree-like organization.</p>
079:         * <p>The internal used storage form allows for a more sophisticated access to
080:         * single properties. As an example consider the following XML document:</p>
081:         * <p><pre>
082:         * &lt;database&gt;
083:         *   &lt;tables&gt;
084:         *     &lt;table&gt;
085:         *       &lt;name&gt;users&lt;/name&gt;
086:         *       &lt;fields&gt;
087:         *         &lt;field&gt;
088:         *           &lt;name&gt;lid&lt;/name&gt;
089:         *           &lt;type&gt;long&lt;/name&gt;
090:         *         &lt;/field&gt;
091:         *         &lt;field&gt;
092:         *           &lt;name&gt;usrName&lt;/name&gt;
093:         *           &lt;type&gt;java.lang.String&lt;/type&gt;
094:         *         &lt;/field&gt;
095:         *        ...
096:         *       &lt;/fields&gt;
097:         *     &lt;/table&gt;
098:         *     &lt;table&gt;
099:         *       &lt;name&gt;documents&lt;/name&gt;
100:         *       &lt;fields&gt;
101:         *         &lt;field&gt;
102:         *           &lt;name&gt;docid&lt;/name&gt;
103:         *           &lt;type&gt;long&lt;/type&gt;
104:         *         &lt;/field&gt;
105:         *         ...
106:         *       &lt;/fields&gt;
107:         *     &lt;/table&gt;
108:         *     ...
109:         *   &lt;/tables&gt;
110:         * &lt;/database&gt;
111:         * </pre></p>
112:         * <p>If this document is parsed and stored in a
113:         * <code>HierarchicalConfiguration</code> object (which can be done by one of
114:         * the sub classes), there are enhanced possibilities of accessing properties.
115:         * The keys for querying information can contain indices that select a certain
116:         * element if there are multiple hits.</p>
117:         * <p>For instance the key <code>tables.table(0).name</code> can be used to
118:         * find out the name of the first table. In opposite
119:         * <code>tables.table.name</code> would return a collection with the names of
120:         * all available tables. Similarily the key
121:         * <code>tables.table(1).fields.field.name</code> returns a collection with the
122:         * names of all fields of the second table. If another index is added after the
123:         * <code>field</code> element, a single field can be accessed:
124:         * <code>tables.table(1).fields.field(0).name</code>.</p>
125:         * <p>There is a <code>getMaxIndex()</code> method that returns the maximum
126:         * allowed index that can be added to a given property key. This method can be
127:         * used to iterate over all values defined for a certain property.</p>
128:         *
129:         * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
130:         * @version $Id: HierarchicalConfiguration.java,v 1.3 2006/10/26 08:30:35 minhnn Exp $
131:         */
132:        public class HierarchicalConfiguration extends AbstractConfiguration {
133:            /** Constant for a new dummy key.*/
134:            private static final String NEW_KEY = "newKey";
135:
136:            /** Stores the root node of this configuration.*/
137:            private Node root = new Node();
138:
139:            /**
140:             * Creates a new instance of <code>HierarchicalConfiguration</code>.
141:             */
142:            public HierarchicalConfiguration() {
143:                super ();
144:            }
145:
146:            /**
147:             * Creates a new instance of <code>HierarchicalConfiguration</code>
148:             * and initializes it with default properties.
149:             * @param defaults default properties to be used
150:             */
151:            public HierarchicalConfiguration(Configuration defaults) {
152:                super (defaults);
153:            }
154:
155:            /**
156:             * Returns the root node of this hierarchical configuration.
157:             * @return the root node
158:             */
159:            public Node getRoot() {
160:                return root;
161:            }
162:
163:            /**
164:             * Sets the root node of this hierarchical configuration.
165:             * @param node the root node
166:             */
167:            public void setRoot(Node node) {
168:                if (node == null) {
169:                    throw new IllegalArgumentException(
170:                            "Root node must not be null!");
171:                } /* if */
172:                root = node;
173:            }
174:
175:            /**
176:             * Fetches the specified property. Performs a recursive lookup in the
177:             * tree with the configuration properties.
178:             * @param key the key to be looked up
179:             * @return the found value
180:             */
181:            protected Object getPropertyDirect(String key) {
182:                List nodes = fetchNodeList(key);
183:
184:                if (nodes.size() == 0) {
185:                    return null;
186:                } /* if */
187:                else {
188:                    Container cont = new Container();
189:                    for (Iterator it = nodes.iterator(); it.hasNext();) {
190:                        Node nd = (Node) it.next();
191:                        if (nd.getValue() != null) {
192:                            cont.add(nd.getValue());
193:                        } /* if */
194:                    } /* for */
195:
196:                    if (cont.size() < 1) {
197:                        return null;
198:                    } /* if */
199:                    else {
200:                        return (cont.size() == 1) ? cont.get(0) : cont;
201:                    } /* else */
202:                } /* else */
203:            }
204:
205:            /**
206:             * <p>Adds the property with the specified key.</p>
207:             * <p>To be able to deal with the structure supported by this configuration
208:             * implementation the passed in key is of importance, especially the
209:             * indices it might contain. The following example should clearify this:
210:             * Suppose the actual configuration contains the following elements:</p>
211:             * <p><pre>
212:             * tables
213:             *    +-- table
214:             *            +-- name = user
215:             *            +-- fields
216:             *                    +-- field
217:             *                            +-- name = uid
218:             *                    +-- field
219:             *                            +-- name = firstName
220:             *                    ...
221:             *    +-- table
222:             *            +-- name = documents
223:             *            +-- fields
224:             *                   ...
225:             * </pre></p>
226:             * <p>In this example a database structure is defined, e.g. all fields of
227:             * the first table could be accessed using the key
228:             * <code>tables.table(0).fields.field.name</code>. If now properties are
229:             * to be added, it must be exactly specified at which position in the
230:             * hierarchy the new property is to be inserted. So to add a new field name
231:             * to a table it is not enough to say just</p>
232:             * <p><pre>
233:             * config.addProperty("tables.table.fields.field.name", "newField");
234:             * </pre></p>
235:             * <p>The statement given above contains some ambiguity. For instance
236:             * it is not clear, to which table the new field should be added. If this
237:             * method finds such an ambiguity, it is resolved by following the last
238:             * valid path. Here this would be the last table. The same is true for the
239:             * <code>field</code>; because there are multiple fields and no explicit
240:             * index is provided, a new <code>name</code> property would be
241:             * added to the last field - which is propably not what was desired.</p>
242:             * <p>To make things clear explicit indices should be provided whenever
243:             * possible. In the example above the exact table could be specified by
244:             * providing an index for the <code>table</code> element as in
245:             * <code>tables.table(1).fields</code>. By specifying an index it can also
246:             * be expressed that at a given position in the configuration tree a new
247:             * branch should be added. In the example above we did not want to add
248:             * an additional <code>name</code> element to the last field of the table,
249:             * but we want a complete new <code>field</code> element. This can be
250:             * achieved by specifying an invalid index (like -1) after the element
251:             * where a new branch should be created. Given this our example would run:
252:             * </p><p><pre>
253:             * config.addProperty("tables.table(1).fields.field(-1).name", "newField");
254:             * </pre></p>
255:             * <p>With this notation it is possible to add new branches everywhere.
256:             * We could for instance create a new <code>table</code> element by
257:             * specifying</p>
258:             * <p><pre>
259:             * config.addProperty("tables.table(-1).fields.field.name", "newField2");
260:             * </pre></p>
261:             * <p>(Note that because after the <code>table</code> element a new
262:             * branch is created indices in following elements are not relevant; the
263:             * branch is new so there cannot be any ambiguities.)</p>
264:             * @param key the key of the new property
265:             * @param obj the value of the new property
266:             */
267:            protected void addPropertyDirect(String key, Object obj) {
268:                ConfigurationKey.KeyIterator it = new ConfigurationKey(key)
269:                        .iterator();
270:                Node parent = fetchAddNode(it, getRoot());
271:
272:                Node child = new Node(it.currentKey(true));
273:                child.setValue(obj);
274:                parent.addChild(child);
275:            }
276:
277:            /**
278:             * Adds a collection of nodes at the specified position of the
279:             * configuration tree. This method works similar to
280:             * <code>addProperty()</code>, but instead of a single property a whole
281:             * collection of nodes can be added - and thus complete configuration
282:             * sub trees. E.g. with this method it is possible to add parts of
283:             * another <code>HierarchicalConfiguration</code> object to this object.
284:             * @param key the key where the nodes are to be added; can be <b>null</b>,
285:             * then they are added to the root node
286:             * @param nodes a collection with the <code>Node</code> objects to be
287:             * added
288:             */
289:            public void addNodes(String key, Collection nodes) {
290:                if (nodes == null || nodes.isEmpty()) {
291:                    return;
292:                } /* if */
293:
294:                Node parent;
295:                if (StringUtils.isEmpty(key)) {
296:                    parent = getRoot();
297:                } /* if */
298:                else {
299:                    ConfigurationKey.KeyIterator kit = new ConfigurationKey(key)
300:                            .iterator();
301:                    parent = fetchAddNode(kit, getRoot());
302:
303:                    // fetchAddNode() does not really fetch the last component,
304:                    // but one before. So we must perform an additional step.
305:                    ConfigurationKey keyNew = new ConfigurationKey(kit
306:                            .currentKey(true));
307:                    keyNew.append(NEW_KEY);
308:                    parent = fetchAddNode(keyNew.iterator(), parent);
309:                } /* else */
310:
311:                for (Iterator it = nodes.iterator(); it.hasNext();) {
312:                    parent.addChild((Node) it.next());
313:                } /* for */
314:            }
315:
316:            /**
317:             * Checks if this configuration is empty. Empty means that there are
318:             * no keys with any values, though there can be some (empty) nodes.
319:             * @return a flag if this configuration is empty
320:             */
321:            public boolean isEmpty() {
322:                return !nodeDefined(getRoot());
323:            }
324:
325:            /**
326:             * Checks if the specified key is contained in this configuration.
327:             * Note that for this configuration the term &quot;contained&quot; means
328:             * that the key has an associated value. If there is a node for this key
329:             * that has no value but children (either defined or undefined), this
330:             * method will still return <b>false</b>.
331:             * @param key the key to be chekced
332:             * @return a flag if this key is contained in this configuration
333:             */
334:            public boolean containsKey(String key) {
335:                return getPropertyDirect(key) != null;
336:            }
337:
338:            /**
339:             * Removes all values of the property with the given name.
340:             * @param key the key of the property to be removed
341:             */
342:            public void clearProperty(String key) {
343:                List nodes = fetchNodeList(key);
344:
345:                for (Iterator it = nodes.iterator(); it.hasNext();) {
346:                    removeNode((Node) it.next());
347:                } /* for */
348:            }
349:
350:            /**
351:             * <p>Returns an iterator with all keys defined in this configuration.</p>
352:             * <p>Note that the keys returned by this method will not contain
353:             * any indices. This means that some structure will be lost.</p>
354:             * @return an iterator with the defined keys in this configuration
355:             */
356:            public Iterator getKeys() {
357:                DefinedKeysVisitor visitor = new DefinedKeysVisitor();
358:                getRoot().visit(visitor, new ConfigurationKey());
359:                return visitor.getKeyList().iterator();
360:            }
361:
362:            /**
363:             * Creates a new <code>Configuration</code> object containing all keys
364:             * that start with the specified prefix. This implementation will return
365:             * a <code>HierarchicalConfiguration</code> object so that the structure
366:             * of the keys will be saved.
367:             * @param prefix the prefix of the keys for the subset
368:             * @return a new configuration object representing the selected subset
369:             */
370:            public Configuration subset(String prefix) {
371:                Collection nodes = fetchNodeList(prefix);
372:                if (nodes.isEmpty()) {
373:                    return null;
374:                } /* if */
375:
376:                HierarchicalConfiguration result = new HierarchicalConfiguration();
377:                CloneVisitor visitor = new CloneVisitor();
378:
379:                for (Iterator it = nodes.iterator(); it.hasNext();) {
380:                    Node nd = (Node) it.next();
381:                    nd.visit(visitor, null);
382:
383:                    Container children = visitor.getClone().getChildren();
384:                    if (children.size() > 0) {
385:                        for (int i = 0; i < children.size(); i++) {
386:                            result.getRoot().addChild((Node) children.get(i));
387:                        } /* for */
388:                    } /* if */
389:                    else {
390:                        // In this case we cannot shorten the key because only
391:                        // values are found without further child nodes.
392:                        result.getRoot().addChild(visitor.getClone());
393:                    } /* else */
394:                } /* for */
395:
396:                return (result.isEmpty()) ? null : result;
397:            }
398:
399:            /**
400:             * Returns the maximum defined index for the given key. This is
401:             * useful if there are multiple values for this key. They can then be
402:             * addressed separately by specifying indices from 0 to the return value
403:             * of this method.
404:             * @param key the key to be checked
405:             * @return the maximum defined index for this key
406:             */
407:            public int getMaxIndex(String key) {
408:                return fetchNodeList(key).size() - 1;
409:            }
410:
411:            /**
412:             * Helper method for fetching a list of all nodes that are addressed by
413:             * the specified key.
414:             * @param key the key
415:             * @return a list with all affected nodes (never <b>null</b>)
416:             */
417:            protected List fetchNodeList(String key) {
418:                List nodes = new LinkedList();
419:                findPropertyNodes(new ConfigurationKey(key).iterator(),
420:                        getRoot(), nodes);
421:                return nodes;
422:            }
423:
424:            /**
425:             * Recursive helper method for fetching a property. This method
426:             * processes all facets of a configuration key, traverses the tree of
427:             * properties and fetches the the nodes of all matching properties.
428:             * @param keyPart the configuration key iterator
429:             * @param node the actual node
430:             * @param data here the found nodes are stored
431:             */
432:            protected void findPropertyNodes(
433:                    ConfigurationKey.KeyIterator keyPart, Node node,
434:                    Collection data) {
435:                if (!keyPart.hasNext()) {
436:                    data.add(node);
437:                } /* if */
438:
439:                else {
440:                    String key = keyPart.nextKey(true);
441:                    Container children = node.getChildren(key);
442:                    if (keyPart.hasIndex()) {
443:                        if (keyPart.getIndex() < children.size()
444:                                && keyPart.getIndex() >= 0) {
445:                            findPropertyNodes(
446:                                    (ConfigurationKey.KeyIterator) keyPart
447:                                            .clone(), (Node) children
448:                                            .get(keyPart.getIndex()), data);
449:                        } /* if */
450:                    } /* if */
451:
452:                    else {
453:                        for (Iterator it = children.iterator(); it.hasNext();) {
454:                            findPropertyNodes(
455:                                    (ConfigurationKey.KeyIterator) keyPart
456:                                            .clone(), (Node) it.next(), data);
457:                        } /* for */
458:                    } /* else */
459:                }
460:            }
461:
462:            /**
463:             * Checks if the specified node is defined.
464:             * @param node the node to be checked
465:             * @return a flag if this node is defined
466:             */
467:            protected boolean nodeDefined(Node node) {
468:                DefinedVisitor visitor = new DefinedVisitor();
469:                node.visit(visitor, null);
470:                return visitor.isDefined();
471:            }
472:
473:            /**
474:             * Removes the specified node from this configuration. This method
475:             * ensures that parent nodes that become undefined by this operation
476:             * are also removed.
477:             * @param node the node to be removed
478:             */
479:            protected void removeNode(Node node) {
480:                Node parent = node.getParent();
481:                if (parent != null) {
482:                    parent.remove(node);
483:                    if (!nodeDefined(parent)) {
484:                        removeNode(parent);
485:                    } /* if */
486:                } /* if */
487:            }
488:
489:            /**
490:             * Returns a reference to the parent node of an add operation.
491:             * Nodes for new properties can be added as children of this node.
492:             * If the path for the specified key does not exist so far, it is created
493:             * now.
494:             * @param keyIt the iterator for the key of the new property
495:             * @param startNode the node to start the search with
496:             * @return the parent node for the add operation
497:             */
498:            protected Node fetchAddNode(ConfigurationKey.KeyIterator keyIt,
499:                    Node startNode) {
500:                if (!keyIt.hasNext()) {
501:                    throw new IllegalArgumentException("Key must be defined!");
502:                } /* if */
503:
504:                return createAddPath(keyIt, findLastPathNode(keyIt, startNode));
505:            }
506:
507:            /**
508:             * Finds the last existing node for an add operation. This method
509:             * traverses the configuration tree along the specified key. The last
510:             * existing node on this path is returned.
511:             * @param keyIt the key iterator
512:             * @param node the actual node
513:             * @return the last existing node on the given path
514:             */
515:            protected Node findLastPathNode(ConfigurationKey.KeyIterator keyIt,
516:                    Node node) {
517:                String keyPart = keyIt.nextKey(true);
518:
519:                if (keyIt.hasNext()) {
520:                    Container c = node.getChildren(keyPart);
521:                    int idx = (keyIt.hasIndex()) ? keyIt.getIndex()
522:                            : c.size() - 1;
523:                    if (idx < 0 || idx >= c.size()) {
524:                        return node;
525:                    } /* if */
526:                    else {
527:                        return findLastPathNode(keyIt, (Node) c.get(idx));
528:                    } /* else */
529:                } /* if */
530:
531:                else {
532:                    return node;
533:                } /* else */
534:            }
535:
536:            /**
537:             * Creates the missing nodes for adding a new property. This method
538:             * ensures that there are corresponding nodes for all components of the
539:             * specified configuration key.
540:             * @param keyIt the key iterator
541:             * @param root the base node of the path to be created
542:             * @return the last node of the path
543:             */
544:            protected Node createAddPath(ConfigurationKey.KeyIterator keyIt,
545:                    Node root) {
546:                if (keyIt.hasNext()) {
547:                    Node child = new Node(keyIt.currentKey(true));
548:                    root.addChild(child);
549:                    keyIt.next();
550:                    return createAddPath(keyIt, child);
551:                } /* if */
552:                else {
553:                    return root;
554:                } /* else */
555:            }
556:
557:            /**
558:             * Helper method for adding all elements of a collection to a
559:             * container.
560:             * @param cont the container
561:             * @param items the collection to be added
562:             */
563:            private static void addContainer(Container cont, Collection items) {
564:                for (Iterator it = items.iterator(); it.hasNext();) {
565:                    cont.add(it.next());
566:                } /* for */
567:            }
568:
569:            /**
570:             * A data class for storing (hierarchical) property information. A property
571:             * can have a value and an arbitrary number of child properties.
572:             *
573:             * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
574:             */
575:            public static class Node implements  Serializable, Cloneable {
576:                /** Stores a reference to this node's parent.*/
577:                private Node parent;
578:
579:                /** Stores the name of this node.*/
580:                private String name;
581:
582:                /** Stores the value of this node.*/
583:                private Object value;
584:
585:                /** Stores the children of this node.*/
586:                private Map children;
587:
588:                /**
589:                 * Creates a new instance of <code>Node</code>.
590:                 */
591:                public Node() {
592:                    this (null);
593:                }
594:
595:                /**
596:                 * Creates a new instance of <code>Node</code> and sets the name.
597:                 * @param name the node's name
598:                 */
599:                public Node(String name) {
600:                    setName(name);
601:                }
602:
603:                /**
604:                 * Returns the name of this node.
605:                 * @return the node name
606:                 */
607:                public String getName() {
608:                    return name;
609:                }
610:
611:                /**
612:                 * Returns the value of this node.
613:                 * @return the node value (may be <b>null</b>)
614:                 */
615:                public Object getValue() {
616:                    return value;
617:                }
618:
619:                /**
620:                 * Returns the parent of this node.
621:                 * @return this node's parent (can be <b>null</b>)
622:                 */
623:                public Node getParent() {
624:                    return parent;
625:                }
626:
627:                /**
628:                 * Sets the name of this node.
629:                 * @param string the node name
630:                 */
631:                public void setName(String string) {
632:                    name = string;
633:                }
634:
635:                /**
636:                 * Sets the value of this node.
637:                 * @param object the node value
638:                 */
639:                public void setValue(Object object) {
640:                    value = object;
641:                }
642:
643:                /**
644:                 * Sets the parent of this node.
645:                 * @param node the parent node
646:                 */
647:                public void setParent(Node node) {
648:                    parent = node;
649:                }
650:
651:                /**
652:                 * Adds the specified child object to this node. Note that there can
653:                 * be multiple children with the same name.
654:                 * @param child the child to be added
655:                 */
656:                public void addChild(Node child) {
657:                    if (children == null) {
658:                        children = new SequencedHashMap();
659:                    } /* if */
660:
661:                    List c = (List) children.get(child.getName());
662:                    if (c == null) {
663:                        c = new ArrayList();
664:                        children.put(child.getName(), c);
665:                    } /* if */
666:
667:                    c.add(child);
668:                    child.setParent(this );
669:                }
670:
671:                /**
672:                 * Returns a list with the child nodes of this node.
673:                 * @return a list with the children (can be empty, but never
674:                 * <b>null</b>)
675:                 */
676:                public Container getChildren() {
677:                    Container result = new Container();
678:
679:                    if (children != null) {
680:                        for (Iterator it = children.values().iterator(); it
681:                                .hasNext();) {
682:                            addContainer(result, (Collection) it.next());
683:                        } /* for */
684:                    } /* if */
685:
686:                    return result;
687:                }
688:
689:                /**
690:                 * Returns a list with this node's children with the given name.
691:                 * @param name the name of the children
692:                 * @return a list with all chidren with this name; may be empty, but
693:                 * never <b>null</b>
694:                 */
695:                public Container getChildren(String name) {
696:                    if (name == null || children == null) {
697:                        return getChildren();
698:                    } /* if */
699:
700:                    Container cont = new Container();
701:                    List c = (List) children.get(name);
702:                    if (c != null) {
703:                        addContainer(cont, c);
704:                    } /* if */
705:
706:                    return cont;
707:                }
708:
709:                /**
710:                 * Removes the specified child from this node.
711:                 * @param child the child node to be removed
712:                 * @return a flag if the child could be found
713:                 */
714:                public boolean remove(Node child) {
715:                    if (children == null) {
716:                        return false;
717:                    } /* if */
718:
719:                    List c = (List) children.get(child.getName());
720:                    if (c == null) {
721:                        return false;
722:                    } /* if */
723:
724:                    else {
725:                        if (c.remove(child)) {
726:                            if (c.isEmpty()) {
727:                                children.remove(child.getName());
728:                            } /* if */
729:                            return true;
730:                        } /* if */
731:                        else {
732:                            return false;
733:                        } /* else */
734:                    } /* else */
735:                }
736:
737:                /**
738:                 * Removes all children with the given name.
739:                 * @param name the name of the children to be removed
740:                 * @return a flag if children with this name existed
741:                 */
742:                public boolean remove(String name) {
743:                    if (children == null) {
744:                        return false;
745:                    } /* if */
746:
747:                    return children.remove(name) != null;
748:                }
749:
750:                /**
751:                 * Removes all children of this node.
752:                 */
753:                public void removeChildren() {
754:                    children = null;
755:                }
756:
757:                /**
758:                 * A generic method for traversing this node and all of its children.
759:                 * This method sends the passed in visitor to this node and all of its
760:                 * children.
761:                 * @param visitor the visitor
762:                 * @param key here a configuration key with the name of the root node
763:                 * of the iteration can be passed; if this key is not <b>null</b>, the
764:                 * full pathes to the visited nodes are builded and passed to the
765:                 * visitor's <code>visit()</code> methods
766:                 */
767:                public void visit(NodeVisitor visitor, ConfigurationKey key) {
768:                    int length = 0;
769:                    if (key != null) {
770:                        length = key.length();
771:                        if (getName() != null) {
772:                            key.append(getName());
773:                        } /* if */
774:                    } /* if */
775:
776:                    visitor.visitBeforeChildren(this , key);
777:
778:                    if (children != null) {
779:                        for (Iterator it = children.values().iterator(); it
780:                                .hasNext()
781:                                && !visitor.terminate();) {
782:                            Collection col = (Collection) it.next();
783:                            for (Iterator it2 = col.iterator(); it2.hasNext()
784:                                    && !visitor.terminate();) {
785:                                ((Node) it2.next()).visit(visitor, key);
786:                            } /* for */
787:                        } /* for */
788:                    } /* if */
789:
790:                    if (key != null) {
791:                        key.setLength(length);
792:                    } /* if */
793:                    visitor.visitAfterChildren(this , key);
794:                }
795:
796:                /**
797:                 * Creates a copy of this object. This is not a deep copy, the children
798:                 * are not cloned.
799:                 * @return a copy of this object
800:                 */
801:                protected Object clone() {
802:                    try {
803:                        return super .clone();
804:                    } /* try */
805:                    catch (CloneNotSupportedException cex) {
806:                        return null; // should not happen
807:                    } /* catch */
808:                }
809:            }
810:
811:            /**
812:             * <p>Definition of a visitor class for traversing a node and all of its
813:             * children.</p>
814:             * <p>This class defines the interface of a visitor for <code>Node</code>
815:             * objects and provides a default implementation. The method
816:             * <code>visit()</code> of <code>Node</code> implements a generic
817:             * iteration algorithm based on the <em>Visitor</em> pattern. By
818:             * providing different implementations of visitors it is possible to
819:             * collect different data during the iteration process.</p>
820:             *
821:             * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
822:             */
823:            public static class NodeVisitor {
824:                /**
825:                 * Visits the specified node. This method is called during iteration
826:                 * for each node before its children have been visited.
827:                 * @param node the actual node
828:                 * @param key the key of this node (may be <b>null</b>)
829:                 */
830:                public void visitBeforeChildren(Node node, ConfigurationKey key) {
831:                }
832:
833:                /**
834:                 * Visits the specified node after its children have been processed.
835:                 * This gives a visitor the opportunity of collecting additional data
836:                 * after the child nodes have been visited.
837:                 * @param node the node to be visited
838:                 * @param key the key of this node (may be <b>null</b>)
839:                 */
840:                public void visitAfterChildren(Node node, ConfigurationKey key) {
841:                }
842:
843:                /**
844:                 * Returns a flag that indicates if iteration should be stopped. This
845:                 * method is called after each visited node. It can be useful for
846:                 * visitors that search a specific node. If this node is found, the
847:                 * whole process can be stopped. This base implementation always
848:                 * returns <b>false</b>.
849:                 * @return a flag if iteration should be stopped
850:                 */
851:                public boolean terminate() {
852:                    return false;
853:                }
854:            }
855:
856:            /**
857:             * A specialized visitor that checks if a node is defined.
858:             * &quot;Defined&quot; in this terms means that the node or at least one
859:             * of its sub nodes is associated with a value.
860:             *
861:             * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
862:             */
863:            static class DefinedVisitor extends NodeVisitor {
864:                /** Stores the defined flag.*/
865:                private boolean defined;
866:
867:                /**
868:                 * Checks if iteration should be stopped. This can be done if the first
869:                 * defined node is found.
870:                 * @return a flag if iteration should be stopped
871:                 */
872:                public boolean terminate() {
873:                    return isDefined();
874:                }
875:
876:                /**
877:                 * Visits the node. Checks if a value is defined.
878:                 * @param node the actual node
879:                 * @param key the key of this node
880:                 */
881:                public void visitBeforeChildren(Node node, ConfigurationKey key) {
882:                    defined = node.getValue() != null;
883:                }
884:
885:                /**
886:                 * Returns the defined flag.
887:                 * @return the defined flag
888:                 */
889:                public boolean isDefined() {
890:                    return defined;
891:                }
892:            }
893:
894:            /**
895:             * A specialized visitor that fills a list with keys that are defined in
896:             * a node hierarchy.
897:             *
898:             * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
899:             */
900:            static class DefinedKeysVisitor extends NodeVisitor {
901:                /** Stores the list to be filled.*/
902:                private Set keyList;
903:
904:                /**
905:                 * Default constructor.
906:                 */
907:                public DefinedKeysVisitor() {
908:                    keyList = new HashSet();
909:                }
910:
911:                /**
912:                 * Returns the list with all defined keys.
913:                 * @return the list with the defined keys
914:                 */
915:                public Set getKeyList() {
916:                    return keyList;
917:                }
918:
919:                /**
920:                 * Visits the specified node. If this node has a value, its key is
921:                 * added to the internal list.
922:                 * @param node the node to be visited
923:                 * @param key the key of this node
924:                 */
925:                public void visitBeforeChildren(Node node, ConfigurationKey key) {
926:                    if (node.getValue() != null && key != null) {
927:                        keyList.add(key.toString());
928:                    } /* if */
929:                }
930:            }
931:
932:            /**
933:             * A specialized visitor that is able to create a deep copy of a node
934:             * hierarchy.
935:             *
936:             * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
937:             */
938:            static class CloneVisitor extends NodeVisitor {
939:                /** A stack with the actual object to be copied.*/
940:                private Stack copyStack;
941:
942:                /** Stores the result of the clone process.*/
943:                private Node result;
944:
945:                /**
946:                 * Creates a new instance of <code>CloneVisitor</code>.
947:                 */
948:                public CloneVisitor() {
949:                    copyStack = new Stack();
950:                }
951:
952:                /**
953:                 * Visits the specified node after its children have been processed.
954:                 * @param node the node
955:                 * @param key the key of this node
956:                 */
957:                public void visitAfterChildren(Node node, ConfigurationKey key) {
958:                    copyStack.pop();
959:                    if (copyStack.isEmpty()) {
960:                        result = node;
961:                    } /* if */
962:                }
963:
964:                /**
965:                 * Visits and copies the specified node.
966:                 * @param node the node
967:                 * @param key the key of this node
968:                 */
969:                public void visitBeforeChildren(Node node, ConfigurationKey key) {
970:                    Node copy = (Node) node.clone();
971:                    copy.removeChildren();
972:
973:                    if (!copyStack.isEmpty()) {
974:                        ((Node) copyStack.peek()).addChild(copy);
975:                    } /* if */
976:
977:                    copyStack.push(copy);
978:                }
979:
980:                /**
981:                 * Returns the result of the clone process. This is the root node of
982:                 * the cloned node hierarchy.
983:                 * @return the cloned root node
984:                 */
985:                public Node getClone() {
986:                    return result;
987:                }
988:            }
989:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.