001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.configuration2; 019 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.LinkedHashSet; 026import java.util.LinkedList; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030import java.util.Stack; 031 032import org.apache.commons.configuration2.event.ConfigurationEvent; 033import org.apache.commons.configuration2.ex.ConfigurationRuntimeException; 034import org.apache.commons.configuration2.sync.NoOpSynchronizer; 035import org.apache.commons.configuration2.tree.ConfigurationNodeVisitorAdapter; 036import org.apache.commons.configuration2.tree.DefaultExpressionEngine; 037import org.apache.commons.configuration2.tree.ExpressionEngine; 038import org.apache.commons.configuration2.tree.NodeAddData; 039import org.apache.commons.configuration2.tree.NodeHandler; 040import org.apache.commons.configuration2.tree.NodeKeyResolver; 041import org.apache.commons.configuration2.tree.NodeModel; 042import org.apache.commons.configuration2.tree.NodeTreeWalker; 043import org.apache.commons.configuration2.tree.NodeUpdateData; 044import org.apache.commons.configuration2.tree.QueryResult; 045 046/** 047 * <p> 048 * A specialized configuration class that extends its base class by the ability 049 * of keeping more structure in the stored properties. 050 * </p> 051 * <p> 052 * There are some sources of configuration data that cannot be stored very well 053 * in a {@code BaseConfiguration} object because then their structure is lost. 054 * This is for instance true for XML documents. This class can deal with such 055 * structured configuration sources by storing the properties in a tree-like 056 * organization. The exact storage structure of the underlying data does not 057 * matter for the configuration instance; it uses a {@link NodeModel} object for 058 * accessing it. 059 * </p> 060 * <p> 061 * The hierarchical organization allows for a more sophisticated access to 062 * single properties. As an example consider the following XML document: 063 * </p> 064 * 065 * <pre> 066 * <database> 067 * <tables> 068 * <table> 069 * <name>users</name> 070 * <fields> 071 * <field> 072 * <name>lid</name> 073 * <type>long</name> 074 * </field> 075 * <field> 076 * <name>usrName</name> 077 * <type>java.lang.String</type> 078 * </field> 079 * ... 080 * </fields> 081 * </table> 082 * <table> 083 * <name>documents</name> 084 * <fields> 085 * <field> 086 * <name>docid</name> 087 * <type>long</type> 088 * </field> 089 * ... 090 * </fields> 091 * </table> 092 * ... 093 * </tables> 094 * </database> 095 * </pre> 096 * 097 * <p> 098 * If this document is parsed and stored in a hierarchical configuration object 099 * (which can be done by one of the sub classes), there are enhanced 100 * possibilities of accessing properties. Per default, the keys for querying 101 * information can contain indices that select a specific element if there are 102 * multiple hits. 103 * </p> 104 * <p> 105 * For instance the key {@code tables.table(0).name} can be used to find out the 106 * name of the first table. In opposite {@code tables.table.name} would return a 107 * collection with the names of all available tables. Similarly the key 108 * {@code tables.table(1).fields.field.name} returns a collection with the names 109 * of all fields of the second table. If another index is added after the 110 * {@code field} element, a single field can be accessed: 111 * {@code tables.table(1).fields.field(0).name}. 112 * </p> 113 * <p> 114 * There is a {@code getMaxIndex()} method that returns the maximum allowed 115 * index that can be added to a given property key. This method can be used to 116 * iterate over all values defined for a certain property. 117 * </p> 118 * <p> 119 * Since the 1.3 release of <em>Commons Configuration</em> hierarchical 120 * configurations support an <em>expression engine</em>. This expression engine 121 * is responsible for evaluating the passed in configuration keys and map them 122 * to the stored properties. The examples above are valid for the default 123 * expression engine, which is used when a new 124 * {@code AbstractHierarchicalConfiguration} instance is created. With the 125 * {@code setExpressionEngine()} method a different expression engine can be 126 * set. For instance with 127 * {@link org.apache.commons.configuration2.tree.xpath.XPathExpressionEngine} 128 * there is an expression engine available that supports configuration keys in 129 * XPATH syntax. 130 * </p> 131 * <p> 132 * In addition to the events common for all configuration classes, hierarchical 133 * configurations support some more events that correspond to some specific 134 * methods and features. For those events specific event type constants in 135 * {@code ConfigurationEvent} exist: 136 * </p> 137 * <dl> 138 * <dt><em>ADD_NODES</em></dt> 139 * <dd>The {@code addNodes()} method was called; the event object contains the 140 * key, to which the nodes were added, and a collection with the new nodes as 141 * value.</dd> 142 * <dt><em>CLEAR_TREE</em></dt> 143 * <dd>The {@code clearTree()} method was called; the event object stores the 144 * key of the removed sub tree.</dd> 145 * <dt><em>SUBNODE_CHANGED</em></dt> 146 * <dd>A {@code SubnodeConfiguration} that was created from this configuration 147 * has been changed. The value property of the event object contains the 148 * original event object as it was sent by the subnode configuration.</dd> 149 * </dl> 150 * <p> 151 * Whether an {@code AbstractHierarchicalConfiguration} object is thread-safe or 152 * not depends on the underlying {@code NodeModel} and the 153 * {@link org.apache.commons.configuration2.sync.Synchronizer Synchronizer} 154 * it is associated with. Some {@code NodeModel} implementations are inherently 155 * thread-safe; they do not require a special {@code Synchronizer}. (Per 156 * default, a dummy {@code Synchronizer} is used which is not thread-safe!) The 157 * methods for querying or updating configuration data invoke this 158 * {@code Synchronizer} accordingly. When accessing the configuration's root 159 * node directly, the client application is responsible for proper 160 * synchronization. This is achieved by calling the methods 161 * {@link #lock(org.apache.commons.configuration2.sync.LockMode) lock()}, 162 * and {@link #unlock(org.apache.commons.configuration2.sync.LockMode) unlock()} with a proper 163 * {@link org.apache.commons.configuration2.sync.LockMode LockMode} argument. 164 * In any case, it is recommended to not access the 165 * root node directly, but to use corresponding methods for querying or updating 166 * configuration data instead. Direct manipulations of a configuration's node 167 * structure circumvent many internal mechanisms and thus can cause undesired 168 * effects. For concrete subclasses dealing with specific node structures, this 169 * situation may be different. 170 * </p> 171 * 172 * @since 2.0 173 * @param <T> the type of the nodes managed by this hierarchical configuration 174 */ 175public abstract class AbstractHierarchicalConfiguration<T> extends AbstractConfiguration 176 implements Cloneable, NodeKeyResolver<T>, HierarchicalConfiguration<T> 177{ 178 /** The model for managing the data stored in this configuration. */ 179 private NodeModel<T> model; 180 181 /** Stores the expression engine for this instance.*/ 182 private ExpressionEngine expressionEngine; 183 184 /** 185 * Creates a new instance of {@code AbstractHierarchicalConfiguration} and 186 * sets the {@code NodeModel} to be used. 187 * 188 * @param nodeModel the {@code NodeModel} 189 */ 190 protected AbstractHierarchicalConfiguration(final NodeModel<T> nodeModel) 191 { 192 model = nodeModel; 193 } 194 195 /** 196 * {@inheritDoc} This implementation handles synchronization and delegates 197 * to {@code getRootElementNameInternal()}. 198 */ 199 @Override 200 public final String getRootElementName() 201 { 202 beginRead(false); 203 try 204 { 205 return getRootElementNameInternal(); 206 } 207 finally 208 { 209 endRead(); 210 } 211 } 212 213 /** 214 * Actually obtains the name of the root element. This method is called by 215 * {@code getRootElementName()}. It just returns the name of the root node. 216 * Subclasses that treat the root element name differently can override this 217 * method. 218 * 219 * @return the name of this configuration's root element 220 */ 221 protected String getRootElementNameInternal() 222 { 223 final NodeHandler<T> nodeHandler = getModel().getNodeHandler(); 224 return nodeHandler.nodeName(nodeHandler.getRootNode()); 225 } 226 227 /** 228 * {@inheritDoc} This implementation returns the configuration's 229 * {@code NodeModel}. It is guarded by the current {@code Synchronizer}. 230 */ 231 @Override 232 public NodeModel<T> getNodeModel() 233 { 234 beginRead(false); 235 try 236 { 237 return getModel(); 238 } 239 finally 240 { 241 endRead(); 242 } 243 } 244 245 /** 246 * Returns the expression engine used by this configuration. This method 247 * will never return <b>null</b>; if no specific expression engine was set, 248 * the default expression engine will be returned. 249 * 250 * @return the current expression engine 251 * @since 1.3 252 */ 253 @Override 254 public ExpressionEngine getExpressionEngine() 255 { 256 return expressionEngine != null ? expressionEngine 257 : DefaultExpressionEngine.INSTANCE; 258 } 259 260 /** 261 * Sets the expression engine to be used by this configuration. All property 262 * keys this configuration has to deal with will be interpreted by this 263 * engine. 264 * 265 * @param expressionEngine the new expression engine; can be <b>null</b>, 266 * then the default expression engine will be used 267 * @since 1.3 268 */ 269 @Override 270 public void setExpressionEngine(final ExpressionEngine expressionEngine) 271 { 272 this.expressionEngine = expressionEngine; 273 } 274 275 /** 276 * Fetches the specified property. This task is delegated to the associated 277 * expression engine. 278 * 279 * @param key the key to be looked up 280 * @return the found value 281 */ 282 @Override 283 protected Object getPropertyInternal(final String key) 284 { 285 final List<QueryResult<T>> results = fetchNodeList(key); 286 287 if (results.isEmpty()) 288 { 289 return null; 290 } 291 final NodeHandler<T> handler = getModel().getNodeHandler(); 292 final List<Object> list = new ArrayList<>(); 293 for (final QueryResult<T> result : results) 294 { 295 final Object value = valueFromResult(result, handler); 296 if (value != null) 297 { 298 list.add(value); 299 } 300 } 301 302 if (list.size() < 1) 303 { 304 return null; 305 } 306 return list.size() == 1 ? list.get(0) : list; 307 } 308 309 /** 310 * Adds the property with the specified key. This task will be delegated to 311 * the associated {@code ExpressionEngine}, so the passed in key 312 * must match the requirements of this implementation. 313 * 314 * @param key the key of the new property 315 * @param obj the value of the new property 316 */ 317 @Override 318 protected void addPropertyInternal(final String key, final Object obj) 319 { 320 addPropertyToModel(key, getListDelimiterHandler().parse(obj)); 321 } 322 323 /** 324 * {@inheritDoc} This method is not called in the normal way (via 325 * {@code addProperty()} for hierarchical configurations because all values 326 * to be added for the property have to be passed to the model in a single 327 * step. However, to allow derived classes to add an arbitrary value as an 328 * object, a special implementation is provided here. The passed in object 329 * is not parsed as a list, but passed directly as only value to the model. 330 */ 331 @Override 332 protected void addPropertyDirect(final String key, final Object value) 333 { 334 addPropertyToModel(key, Collections.singleton(value)); 335 } 336 337 /** 338 * Helper method for executing an add property operation on the model. 339 * 340 * @param key the key of the new property 341 * @param values the values to be added for this property 342 */ 343 private void addPropertyToModel(final String key, final Iterable<?> values) 344 { 345 getModel().addProperty(key, values, this); 346 } 347 348 /** 349 * Adds a collection of nodes at the specified position of the configuration 350 * tree. This method works similar to {@code addProperty()}, but 351 * instead of a single property a whole collection of nodes can be added - 352 * and thus complete configuration sub trees. E.g. with this method it is 353 * possible to add parts of another {@code BaseHierarchicalConfiguration} 354 * object to this object. If the passed in key refers to 355 * an existing and unique node, the new nodes are added to this node. 356 * Otherwise a new node will be created at the specified position in the 357 * hierarchy. Implementation node: This method performs some book-keeping 358 * and then delegates to {@code addNodesInternal()}. 359 * 360 * @param key the key where the nodes are to be added; can be <b>null</b>, 361 * then they are added to the root node 362 * @param nodes a collection with the {@code Node} objects to be 363 * added 364 */ 365 @Override 366 public final void addNodes(final String key, final Collection<? extends T> nodes) 367 { 368 if (nodes == null || nodes.isEmpty()) 369 { 370 return; 371 } 372 373 beginWrite(false); 374 try 375 { 376 fireEvent(ConfigurationEvent.ADD_NODES, key, nodes, true); 377 addNodesInternal(key, nodes); 378 fireEvent(ConfigurationEvent.ADD_NODES, key, nodes, false); 379 } 380 finally 381 { 382 endWrite(); 383 } 384 } 385 386 /** 387 * Actually adds a collection of new nodes to this configuration. This 388 * method is called by {@code addNodes()}. It can be overridden by 389 * subclasses that need to adapt this operation. 390 * 391 * @param key the key where the nodes are to be added; can be <b>null</b>, 392 * then they are added to the root node 393 * @param nodes a collection with the {@code Node} objects to be added 394 * @since 2.0 395 */ 396 protected void addNodesInternal(final String key, final Collection<? extends T> nodes) 397 { 398 getModel().addNodes(key, nodes, this); 399 } 400 401 /** 402 * Checks if this configuration is empty. Empty means that there are no keys 403 * with any values, though there can be some (empty) nodes. 404 * 405 * @return a flag if this configuration is empty 406 */ 407 @Override 408 protected boolean isEmptyInternal() 409 { 410 return !nodeDefined(getModel().getNodeHandler().getRootNode()); 411 } 412 413 /** 414 * Checks if the specified key is contained in this configuration. Note that 415 * for this configuration the term "contained" means that the key 416 * has an associated value. If there is a node for this key that has no 417 * value but children (either defined or undefined), this method will still 418 * return <b>false </b>. 419 * 420 * @param key the key to be checked 421 * @return a flag if this key is contained in this configuration 422 */ 423 @Override 424 protected boolean containsKeyInternal(final String key) 425 { 426 return getPropertyInternal(key) != null; 427 } 428 429 /** 430 * Sets the value of the specified property. 431 * 432 * @param key the key of the property to set 433 * @param value the new value of this property 434 */ 435 @Override 436 protected void setPropertyInternal(final String key, final Object value) 437 { 438 getModel().setProperty(key, value, this); 439 } 440 441 /** 442 * {@inheritDoc} This implementation delegates to the expression engine. 443 */ 444 @Override 445 public List<QueryResult<T>> resolveKey(final T root, final String key, 446 final NodeHandler<T> handler) 447 { 448 return getExpressionEngine().query(root, key, handler); 449 } 450 451 /** 452 * {@inheritDoc} This implementation delegates to {@code resolveKey()} and 453 * then filters out attribute results. 454 */ 455 @Override 456 public List<T> resolveNodeKey(final T root, final String key, final NodeHandler<T> handler) 457 { 458 final List<QueryResult<T>> results = resolveKey(root, key, handler); 459 final List<T> targetNodes = new LinkedList<>(); 460 for (final QueryResult<T> result : results) 461 { 462 if (!result.isAttributeResult()) 463 { 464 targetNodes.add(result.getNode()); 465 } 466 } 467 return targetNodes; 468 } 469 470 /** 471 * {@inheritDoc} This implementation delegates to the expression engine. 472 */ 473 @Override 474 public NodeAddData<T> resolveAddKey(final T root, final String key, 475 final NodeHandler<T> handler) 476 { 477 return getExpressionEngine().prepareAdd(root, key, handler); 478 } 479 480 /** 481 * {@inheritDoc} This implementation executes a query for the given key and 482 * constructs a {@code NodeUpdateData} object based on the results. It 483 * determines which nodes need to be changed and whether new ones need to be 484 * added or existing ones need to be removed. 485 */ 486 @Override 487 public NodeUpdateData<T> resolveUpdateKey(final T root, final String key, 488 final Object newValue, final NodeHandler<T> handler) 489 { 490 final Iterator<QueryResult<T>> itNodes = fetchNodeList(key).iterator(); 491 final Iterator<?> itValues = getListDelimiterHandler().parse(newValue).iterator(); 492 final Map<QueryResult<T>, Object> changedValues = 493 new HashMap<>(); 494 Collection<Object> additionalValues = null; 495 Collection<QueryResult<T>> removedItems = null; 496 497 while (itNodes.hasNext() && itValues.hasNext()) 498 { 499 changedValues.put(itNodes.next(), itValues.next()); 500 } 501 502 // Add additional nodes if necessary 503 if (itValues.hasNext()) 504 { 505 additionalValues = new LinkedList<>(); 506 while (itValues.hasNext()) 507 { 508 additionalValues.add(itValues.next()); 509 } 510 } 511 512 // Remove remaining nodes 513 if (itNodes.hasNext()) 514 { 515 removedItems = new LinkedList<>(); 516 while (itNodes.hasNext()) 517 { 518 removedItems.add(itNodes.next()); 519 } 520 } 521 522 return new NodeUpdateData<>(changedValues, additionalValues, 523 removedItems, key); 524 } 525 526 /** 527 * {@inheritDoc} This implementation uses the expression engine to generate a 528 * canonical key for the passed in node. For this purpose, the path to the 529 * root node has to be traversed. The cache is used to store and access keys 530 * for nodes encountered on the path. 531 */ 532 @Override 533 public String nodeKey(final T node, final Map<T, String> cache, final NodeHandler<T> handler) 534 { 535 final List<T> path = new LinkedList<>(); 536 T currentNode = node; 537 String key = cache.get(node); 538 while (key == null && currentNode != null) 539 { 540 path.add(0, currentNode); 541 currentNode = handler.getParent(currentNode); 542 key = cache.get(currentNode); 543 } 544 545 for (final T n : path) 546 { 547 final String currentKey = getExpressionEngine().canonicalKey(n, key, handler); 548 cache.put(n, currentKey); 549 key = currentKey; 550 } 551 552 return key; 553 } 554 555 /** 556 * Clears this configuration. This is a more efficient implementation than 557 * the one inherited from the base class. It delegates to the node model. 558 */ 559 @Override 560 protected void clearInternal() 561 { 562 getModel().clear(this); 563 } 564 565 /** 566 * Removes all values of the property with the given name and of keys that 567 * start with this name. So if there is a property with the key 568 * "foo" and a property with the key "foo.bar", a call 569 * of {@code clearTree("foo")} would remove both properties. 570 * 571 * @param key the key of the property to be removed 572 */ 573 @Override 574 public final void clearTree(final String key) 575 { 576 beginWrite(false); 577 try 578 { 579 fireEvent(ConfigurationEvent.CLEAR_TREE, key, null, true); 580 final Object nodes = clearTreeInternal(key); 581 fireEvent(ConfigurationEvent.CLEAR_TREE, key, nodes, false); 582 } 583 finally 584 { 585 endWrite(); 586 } 587 } 588 589 /** 590 * Actually clears the tree of elements referenced by the given key. This 591 * method is called by {@code clearTree()}. Subclasses that need to adapt 592 * this operation can override this method. This base implementation 593 * delegates to the node model. 594 * 595 * @param key the key of the property to be removed 596 * @return an object with information about the nodes that have been removed 597 * (this is needed for firing a meaningful event of type 598 * CLEAR_TREE) 599 * @since 2.0 600 */ 601 protected Object clearTreeInternal(final String key) 602 { 603 return getModel().clearTree(key, this); 604 } 605 606 /** 607 * Removes the property with the given key. Properties with names that start 608 * with the given key (i.e. properties below the specified key in the 609 * hierarchy) won't be affected. This implementation delegates to the node+ 610 * model. 611 * 612 * @param key the key of the property to be removed 613 */ 614 @Override 615 protected void clearPropertyDirect(final String key) 616 { 617 getModel().clearProperty(key, this); 618 } 619 620 /** 621 * {@inheritDoc} This implementation is slightly more efficient than the 622 * default implementation. It does not iterate over the key set, but 623 * directly queries its size after it has been constructed. Note that 624 * constructing the key set is still an O(n) operation. 625 */ 626 @Override 627 protected int sizeInternal() 628 { 629 return visitDefinedKeys().getKeyList().size(); 630 } 631 632 /** 633 * Returns an iterator with all keys defined in this configuration. 634 * Note that the keys returned by this method will not contain any 635 * indices. This means that some structure will be lost. 636 * 637 * @return an iterator with the defined keys in this configuration 638 */ 639 @Override 640 protected Iterator<String> getKeysInternal() 641 { 642 return visitDefinedKeys().getKeyList().iterator(); 643 } 644 645 /** 646 * Creates a {@code DefinedKeysVisitor} and visits all defined keys with it. 647 * 648 * @return the visitor after all keys have been visited 649 */ 650 private DefinedKeysVisitor visitDefinedKeys() 651 { 652 final DefinedKeysVisitor visitor = new DefinedKeysVisitor(); 653 final NodeHandler<T> nodeHandler = getModel().getNodeHandler(); 654 NodeTreeWalker.INSTANCE.walkDFS(nodeHandler.getRootNode(), visitor, 655 nodeHandler); 656 return visitor; 657 } 658 659 /** 660 * Returns an iterator with all keys defined in this configuration that 661 * start with the given prefix. The returned keys will not contain any 662 * indices. This implementation tries to locate a node whose key is the same 663 * as the passed in prefix. Then the subtree of this node is traversed, and 664 * the keys of all nodes encountered (including attributes) are added to the 665 * result set. 666 * 667 * @param prefix the prefix of the keys to start with 668 * @return an iterator with the found keys 669 */ 670 @Override 671 protected Iterator<String> getKeysInternal(final String prefix) 672 { 673 final DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix); 674 if (containsKey(prefix)) 675 { 676 // explicitly add the prefix 677 visitor.getKeyList().add(prefix); 678 } 679 680 final List<QueryResult<T>> results = fetchNodeList(prefix); 681 final NodeHandler<T> handler = getModel().getNodeHandler(); 682 683 for (final QueryResult<T> result : results) 684 { 685 if (!result.isAttributeResult()) 686 { 687 for (final T c : handler.getChildren(result.getNode())) 688 { 689 NodeTreeWalker.INSTANCE.walkDFS(c, visitor, handler); 690 } 691 visitor.handleAttributeKeys(prefix, result.getNode(), handler); 692 } 693 } 694 695 return visitor.getKeyList().iterator(); 696 } 697 698 /** 699 * Returns the maximum defined index for the given key. This is useful if 700 * there are multiple values for this key. They can then be addressed 701 * separately by specifying indices from 0 to the return value of this 702 * method. If the passed in key is not contained in this configuration, 703 * result is -1. 704 * 705 * @param key the key to be checked 706 * @return the maximum defined index for this key 707 */ 708 @Override 709 public final int getMaxIndex(final String key) 710 { 711 beginRead(false); 712 try 713 { 714 return getMaxIndexInternal(key); 715 } 716 finally 717 { 718 endRead(); 719 } 720 } 721 722 /** 723 * Actually retrieves the maximum defined index for the given key. This 724 * method is called by {@code getMaxIndex()}. Subclasses that need to adapt 725 * this operation have to override this method. 726 * 727 * @param key the key to be checked 728 * @return the maximum defined index for this key 729 * @since 2.0 730 */ 731 protected int getMaxIndexInternal(final String key) 732 { 733 return fetchNodeList(key).size() - 1; 734 } 735 736 /** 737 * Creates a copy of this object. This new configuration object will contain 738 * copies of all nodes in the same structure. Registered event listeners 739 * won't be cloned; so they are not registered at the returned copy. 740 * 741 * @return the copy 742 * @since 1.2 743 */ 744 @Override 745 public Object clone() 746 { 747 beginRead(false); 748 try 749 { 750 @SuppressWarnings("unchecked") // clone returns the same type 751 final 752 AbstractHierarchicalConfiguration<T> copy = 753 (AbstractHierarchicalConfiguration<T>) super.clone(); 754 copy.setSynchronizer(NoOpSynchronizer.INSTANCE); 755 copy.cloneInterpolator(this); 756 copy.setSynchronizer(ConfigurationUtils.cloneSynchronizer(getSynchronizer())); 757 copy.model = cloneNodeModel(); 758 759 return copy; 760 } 761 catch (final CloneNotSupportedException cex) 762 { 763 // should not happen 764 throw new ConfigurationRuntimeException(cex); 765 } 766 finally 767 { 768 endRead(); 769 } 770 } 771 772 /** 773 * Creates a clone of the node model. This method is called by 774 * {@code clone()}. 775 * 776 * @return the clone of the {@code NodeModel} 777 * @since 2.0 778 */ 779 protected abstract NodeModel<T> cloneNodeModel(); 780 781 /** 782 * Helper method for resolving the specified key. 783 * 784 * @param key the key 785 * @return a list with all results selected by this key 786 */ 787 protected List<QueryResult<T>> fetchNodeList(final String key) 788 { 789 final NodeHandler<T> nodeHandler = getModel().getNodeHandler(); 790 return resolveKey(nodeHandler.getRootNode(), key, nodeHandler); 791 } 792 793 /** 794 * Checks if the specified node is defined. 795 * 796 * @param node the node to be checked 797 * @return a flag if this node is defined 798 */ 799 protected boolean nodeDefined(final T node) 800 { 801 final DefinedVisitor<T> visitor = new DefinedVisitor<>(); 802 NodeTreeWalker.INSTANCE.walkBFS(node, visitor, getModel().getNodeHandler()); 803 return visitor.isDefined(); 804 } 805 806 /** 807 * Returns the {@code NodeModel} used by this configuration. This method is 808 * intended for internal use only. Access to the model is granted without 809 * any synchronization. This is in contrast to the "official" 810 * {@code getNodeModel()} method which is guarded by the configuration's 811 * {@code Synchronizer}. 812 * 813 * @return the node model 814 */ 815 protected NodeModel<T> getModel() 816 { 817 return model; 818 } 819 820 /** 821 * Extracts the value from a query result. 822 * 823 * @param result the {@code QueryResult} 824 * @param handler the {@code NodeHandler} 825 * @return the value of this result (may be <b>null</b>) 826 */ 827 private Object valueFromResult(final QueryResult<T> result, final NodeHandler<T> handler) 828 { 829 return result.isAttributeResult() ? result.getAttributeValue(handler) 830 : handler.getValue(result.getNode()); 831 } 832 833 /** 834 * A specialized visitor that checks if a node is defined. 835 * "Defined" in this terms means that the node or at least one of 836 * its sub nodes is associated with a value. 837 * 838 * @param <T> the type of the nodes managed by this hierarchical configuration 839 */ 840 private static class DefinedVisitor<T> extends 841 ConfigurationNodeVisitorAdapter<T> 842 { 843 /** Stores the defined flag. */ 844 private boolean defined; 845 846 /** 847 * Checks if iteration should be stopped. This can be done if the first 848 * defined node is found. 849 * 850 * @return a flag if iteration should be stopped 851 */ 852 @Override 853 public boolean terminate() 854 { 855 return isDefined(); 856 } 857 858 /** 859 * Visits the node. Checks if a value is defined. 860 * 861 * @param node the actual node 862 */ 863 @Override 864 public void visitBeforeChildren(final T node, final NodeHandler<T> handler) 865 { 866 defined = 867 handler.getValue(node) != null 868 || !handler.getAttributes(node).isEmpty(); 869 } 870 871 /** 872 * Returns the defined flag. 873 * 874 * @return the defined flag 875 */ 876 public boolean isDefined() 877 { 878 return defined; 879 } 880 } 881 882 /** 883 * A specialized visitor that fills a list with keys that are defined in a 884 * node hierarchy. 885 */ 886 private class DefinedKeysVisitor extends 887 ConfigurationNodeVisitorAdapter<T> 888 { 889 /** Stores the list to be filled. */ 890 private final Set<String> keyList; 891 892 /** A stack with the keys of the already processed nodes. */ 893 private final Stack<String> parentKeys; 894 895 /** 896 * Default constructor. 897 */ 898 public DefinedKeysVisitor() 899 { 900 keyList = new LinkedHashSet<>(); 901 parentKeys = new Stack<>(); 902 } 903 904 /** 905 * Creates a new {@code DefinedKeysVisitor} instance and sets the 906 * prefix for the keys to fetch. 907 * 908 * @param prefix the prefix 909 */ 910 public DefinedKeysVisitor(final String prefix) 911 { 912 this(); 913 parentKeys.push(prefix); 914 } 915 916 /** 917 * Returns the list with all defined keys. 918 * 919 * @return the list with the defined keys 920 */ 921 public Set<String> getKeyList() 922 { 923 return keyList; 924 } 925 926 /** 927 * {@inheritDoc} This implementation removes this 928 * node's key from the stack. 929 */ 930 @Override 931 public void visitAfterChildren(final T node, final NodeHandler<T> handler) 932 { 933 parentKeys.pop(); 934 } 935 936 /** 937 * {@inheritDoc} If this node has a value, its key is added 938 * to the internal list. 939 */ 940 @Override 941 public void visitBeforeChildren(final T node, final NodeHandler<T> handler) 942 { 943 final String parentKey = parentKeys.isEmpty() ? null 944 : parentKeys.peek(); 945 final String key = getExpressionEngine().nodeKey(node, parentKey, handler); 946 parentKeys.push(key); 947 if (handler.getValue(node) != null) 948 { 949 keyList.add(key); 950 } 951 handleAttributeKeys(key, node, handler); 952 } 953 954 /** 955 * Appends all attribute keys of the current node. 956 * 957 * @param parentKey the parent key 958 * @param node the current node 959 * @param handler the {@code NodeHandler} 960 */ 961 public void handleAttributeKeys(final String parentKey, final T node, 962 final NodeHandler<T> handler) 963 { 964 for (final String attr : handler.getAttributes(node)) 965 { 966 keyList.add(getExpressionEngine().attributeKey(parentKey, attr)); 967 } 968 } 969 } 970 971 @Override 972 public String toString() 973 { 974 return super.toString() + "(" + getRootElementNameInternal() + ")"; 975 } 976}