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 * @version $Id: AbstractHierarchicalConfiguration.java 1842194 2018-09-27 22:24:23Z ggregory $ 173 * @since 2.0 174 * @param <T> the type of the nodes managed by this hierarchical configuration 175 */ 176public abstract class AbstractHierarchicalConfiguration<T> extends AbstractConfiguration 177 implements Cloneable, NodeKeyResolver<T>, HierarchicalConfiguration<T> 178{ 179 /** The model for managing the data stored in this configuration. */ 180 private NodeModel<T> model; 181 182 /** Stores the expression engine for this instance.*/ 183 private ExpressionEngine expressionEngine; 184 185 /** 186 * Creates a new instance of {@code AbstractHierarchicalConfiguration} and 187 * sets the {@code NodeModel} to be used. 188 * 189 * @param nodeModel the {@code NodeModel} 190 */ 191 protected AbstractHierarchicalConfiguration(final NodeModel<T> nodeModel) 192 { 193 model = nodeModel; 194 } 195 196 /** 197 * {@inheritDoc} This implementation handles synchronization and delegates 198 * to {@code getRootElementNameInternal()}. 199 */ 200 @Override 201 public final String getRootElementName() 202 { 203 beginRead(false); 204 try 205 { 206 return getRootElementNameInternal(); 207 } 208 finally 209 { 210 endRead(); 211 } 212 } 213 214 /** 215 * Actually obtains the name of the root element. This method is called by 216 * {@code getRootElementName()}. It just returns the name of the root node. 217 * Subclasses that treat the root element name differently can override this 218 * method. 219 * 220 * @return the name of this configuration's root element 221 */ 222 protected String getRootElementNameInternal() 223 { 224 final NodeHandler<T> nodeHandler = getModel().getNodeHandler(); 225 return nodeHandler.nodeName(nodeHandler.getRootNode()); 226 } 227 228 /** 229 * {@inheritDoc} This implementation returns the configuration's 230 * {@code NodeModel}. It is guarded by the current {@code Synchronizer}. 231 */ 232 @Override 233 public NodeModel<T> getNodeModel() 234 { 235 beginRead(false); 236 try 237 { 238 return getModel(); 239 } 240 finally 241 { 242 endRead(); 243 } 244 } 245 246 /** 247 * Returns the expression engine used by this configuration. This method 248 * will never return <b>null</b>; if no specific expression engine was set, 249 * the default expression engine will be returned. 250 * 251 * @return the current expression engine 252 * @since 1.3 253 */ 254 @Override 255 public ExpressionEngine getExpressionEngine() 256 { 257 return (expressionEngine != null) ? expressionEngine 258 : DefaultExpressionEngine.INSTANCE; 259 } 260 261 /** 262 * Sets the expression engine to be used by this configuration. All property 263 * keys this configuration has to deal with will be interpreted by this 264 * engine. 265 * 266 * @param expressionEngine the new expression engine; can be <b>null</b>, 267 * then the default expression engine will be used 268 * @since 1.3 269 */ 270 @Override 271 public void setExpressionEngine(final ExpressionEngine expressionEngine) 272 { 273 this.expressionEngine = expressionEngine; 274 } 275 276 /** 277 * Fetches the specified property. This task is delegated to the associated 278 * expression engine. 279 * 280 * @param key the key to be looked up 281 * @return the found value 282 */ 283 @Override 284 protected Object getPropertyInternal(final String key) 285 { 286 final List<QueryResult<T>> results = fetchNodeList(key); 287 288 if (results.isEmpty()) 289 { 290 return null; 291 } 292 final NodeHandler<T> handler = getModel().getNodeHandler(); 293 final List<Object> list = new ArrayList<>(); 294 for (final QueryResult<T> result : results) 295 { 296 final Object value = valueFromResult(result, handler); 297 if (value != null) 298 { 299 list.add(value); 300 } 301 } 302 303 if (list.size() < 1) 304 { 305 return null; 306 } 307 return (list.size() == 1) ? list.get(0) : list; 308 } 309 310 /** 311 * Adds the property with the specified key. This task will be delegated to 312 * the associated {@code ExpressionEngine}, so the passed in key 313 * must match the requirements of this implementation. 314 * 315 * @param key the key of the new property 316 * @param obj the value of the new property 317 */ 318 @Override 319 protected void addPropertyInternal(final String key, final Object obj) 320 { 321 addPropertyToModel(key, getListDelimiterHandler().parse(obj)); 322 } 323 324 /** 325 * {@inheritDoc} This method is not called in the normal way (via 326 * {@code addProperty()} for hierarchical configurations because all values 327 * to be added for the property have to be passed to the model in a single 328 * step. However, to allow derived classes to add an arbitrary value as an 329 * object, a special implementation is provided here. The passed in object 330 * is not parsed as a list, but passed directly as only value to the model. 331 */ 332 @Override 333 protected void addPropertyDirect(final String key, final Object value) 334 { 335 addPropertyToModel(key, Collections.singleton(value)); 336 } 337 338 /** 339 * Helper method for executing an add property operation on the model. 340 * 341 * @param key the key of the new property 342 * @param values the values to be added for this property 343 */ 344 private void addPropertyToModel(final String key, final Iterable<?> values) 345 { 346 getModel().addProperty(key, values, this); 347 } 348 349 /** 350 * Adds a collection of nodes at the specified position of the configuration 351 * tree. This method works similar to {@code addProperty()}, but 352 * instead of a single property a whole collection of nodes can be added - 353 * and thus complete configuration sub trees. E.g. with this method it is 354 * possible to add parts of another {@code BaseHierarchicalConfiguration} 355 * object to this object. If the passed in key refers to 356 * an existing and unique node, the new nodes are added to this node. 357 * Otherwise a new node will be created at the specified position in the 358 * hierarchy. Implementation node: This method performs some book-keeping 359 * and then delegates to {@code addNodesInternal()}. 360 * 361 * @param key the key where the nodes are to be added; can be <b>null</b>, 362 * then they are added to the root node 363 * @param nodes a collection with the {@code Node} objects to be 364 * added 365 */ 366 @Override 367 public final void addNodes(final String key, final Collection<? extends T> nodes) 368 { 369 if (nodes == null || nodes.isEmpty()) 370 { 371 return; 372 } 373 374 beginWrite(false); 375 try 376 { 377 fireEvent(ConfigurationEvent.ADD_NODES, key, nodes, true); 378 addNodesInternal(key, nodes); 379 fireEvent(ConfigurationEvent.ADD_NODES, key, nodes, false); 380 } 381 finally 382 { 383 endWrite(); 384 } 385 } 386 387 /** 388 * Actually adds a collection of new nodes to this configuration. This 389 * method is called by {@code addNodes()}. It can be overridden by 390 * subclasses that need to adapt this operation. 391 * 392 * @param key the key where the nodes are to be added; can be <b>null</b>, 393 * then they are added to the root node 394 * @param nodes a collection with the {@code Node} objects to be added 395 * @since 2.0 396 */ 397 protected void addNodesInternal(final String key, final Collection<? extends T> nodes) 398 { 399 getModel().addNodes(key, nodes, this); 400 } 401 402 /** 403 * Checks if this configuration is empty. Empty means that there are no keys 404 * with any values, though there can be some (empty) nodes. 405 * 406 * @return a flag if this configuration is empty 407 */ 408 @Override 409 protected boolean isEmptyInternal() 410 { 411 return !nodeDefined(getModel().getNodeHandler().getRootNode()); 412 } 413 414 /** 415 * Checks if the specified key is contained in this configuration. Note that 416 * for this configuration the term "contained" means that the key 417 * has an associated value. If there is a node for this key that has no 418 * value but children (either defined or undefined), this method will still 419 * return <b>false </b>. 420 * 421 * @param key the key to be checked 422 * @return a flag if this key is contained in this configuration 423 */ 424 @Override 425 protected boolean containsKeyInternal(final String key) 426 { 427 return getPropertyInternal(key) != null; 428 } 429 430 /** 431 * Sets the value of the specified property. 432 * 433 * @param key the key of the property to set 434 * @param value the new value of this property 435 */ 436 @Override 437 protected void setPropertyInternal(final String key, final Object value) 438 { 439 getModel().setProperty(key, value, this); 440 } 441 442 /** 443 * {@inheritDoc} This implementation delegates to the expression engine. 444 */ 445 @Override 446 public List<QueryResult<T>> resolveKey(final T root, final String key, 447 final NodeHandler<T> handler) 448 { 449 return getExpressionEngine().query(root, key, handler); 450 } 451 452 /** 453 * {@inheritDoc} This implementation delegates to {@code resolveKey()} and 454 * then filters out attribute results. 455 */ 456 @Override 457 public List<T> resolveNodeKey(final T root, final String key, final NodeHandler<T> handler) 458 { 459 final List<QueryResult<T>> results = resolveKey(root, key, handler); 460 final List<T> targetNodes = new LinkedList<>(); 461 for (final QueryResult<T> result : results) 462 { 463 if (!result.isAttributeResult()) 464 { 465 targetNodes.add(result.getNode()); 466 } 467 } 468 return targetNodes; 469 } 470 471 /** 472 * {@inheritDoc} This implementation delegates to the expression engine. 473 */ 474 @Override 475 public NodeAddData<T> resolveAddKey(final T root, final String key, 476 final NodeHandler<T> handler) 477 { 478 return getExpressionEngine().prepareAdd(root, key, handler); 479 } 480 481 /** 482 * {@inheritDoc} This implementation executes a query for the given key and 483 * constructs a {@code NodeUpdateData} object based on the results. It 484 * determines which nodes need to be changed and whether new ones need to be 485 * added or existing ones need to be removed. 486 */ 487 @Override 488 public NodeUpdateData<T> resolveUpdateKey(final T root, final String key, 489 final Object newValue, final NodeHandler<T> handler) 490 { 491 final Iterator<QueryResult<T>> itNodes = fetchNodeList(key).iterator(); 492 final Iterator<?> itValues = getListDelimiterHandler().parse(newValue).iterator(); 493 final Map<QueryResult<T>, Object> changedValues = 494 new HashMap<>(); 495 Collection<Object> additionalValues = null; 496 Collection<QueryResult<T>> removedItems = null; 497 498 while (itNodes.hasNext() && itValues.hasNext()) 499 { 500 changedValues.put(itNodes.next(), itValues.next()); 501 } 502 503 // Add additional nodes if necessary 504 if (itValues.hasNext()) 505 { 506 additionalValues = new LinkedList<>(); 507 while (itValues.hasNext()) 508 { 509 additionalValues.add(itValues.next()); 510 } 511 } 512 513 // Remove remaining nodes 514 if (itNodes.hasNext()) 515 { 516 removedItems = new LinkedList<>(); 517 while (itNodes.hasNext()) 518 { 519 removedItems.add(itNodes.next()); 520 } 521 } 522 523 return new NodeUpdateData<>(changedValues, additionalValues, 524 removedItems, key); 525 } 526 527 /** 528 * {@inheritDoc} This implementation uses the expression engine to generate a 529 * canonical key for the passed in node. For this purpose, the path to the 530 * root node has to be traversed. The cache is used to store and access keys 531 * for nodes encountered on the path. 532 */ 533 @Override 534 public String nodeKey(final T node, final Map<T, String> cache, final NodeHandler<T> handler) 535 { 536 final List<T> path = new LinkedList<>(); 537 T currentNode = node; 538 String key = cache.get(node); 539 while (key == null && currentNode != null) 540 { 541 path.add(0, currentNode); 542 currentNode = handler.getParent(currentNode); 543 key = cache.get(currentNode); 544 } 545 546 for (final T n : path) 547 { 548 final String currentKey = getExpressionEngine().canonicalKey(n, key, handler); 549 cache.put(n, currentKey); 550 key = currentKey; 551 } 552 553 return key; 554 } 555 556 /** 557 * Clears this configuration. This is a more efficient implementation than 558 * the one inherited from the base class. It delegates to the node model. 559 */ 560 @Override 561 protected void clearInternal() 562 { 563 getModel().clear(this); 564 } 565 566 /** 567 * Removes all values of the property with the given name and of keys that 568 * start with this name. So if there is a property with the key 569 * "foo" and a property with the key "foo.bar", a call 570 * of {@code clearTree("foo")} would remove both properties. 571 * 572 * @param key the key of the property to be removed 573 */ 574 @Override 575 public final void clearTree(final String key) 576 { 577 beginWrite(false); 578 try 579 { 580 fireEvent(ConfigurationEvent.CLEAR_TREE, key, null, true); 581 final Object nodes = clearTreeInternal(key); 582 fireEvent(ConfigurationEvent.CLEAR_TREE, key, nodes, false); 583 } 584 finally 585 { 586 endWrite(); 587 } 588 } 589 590 /** 591 * Actually clears the tree of elements referenced by the given key. This 592 * method is called by {@code clearTree()}. Subclasses that need to adapt 593 * this operation can override this method. This base implementation 594 * delegates to the node model. 595 * 596 * @param key the key of the property to be removed 597 * @return an object with information about the nodes that have been removed 598 * (this is needed for firing a meaningful event of type 599 * CLEAR_TREE) 600 * @since 2.0 601 */ 602 protected Object clearTreeInternal(final String key) 603 { 604 return getModel().clearTree(key, this); 605 } 606 607 /** 608 * Removes the property with the given key. Properties with names that start 609 * with the given key (i.e. properties below the specified key in the 610 * hierarchy) won't be affected. This implementation delegates to the node+ 611 * model. 612 * 613 * @param key the key of the property to be removed 614 */ 615 @Override 616 protected void clearPropertyDirect(final String key) 617 { 618 getModel().clearProperty(key, this); 619 } 620 621 /** 622 * {@inheritDoc} This implementation is slightly more efficient than the 623 * default implementation. It does not iterate over the key set, but 624 * directly queries its size after it has been constructed. Note that 625 * constructing the key set is still an O(n) operation. 626 */ 627 @Override 628 protected int sizeInternal() 629 { 630 return visitDefinedKeys().getKeyList().size(); 631 } 632 633 /** 634 * Returns an iterator with all keys defined in this configuration. 635 * Note that the keys returned by this method will not contain any 636 * indices. This means that some structure will be lost. 637 * 638 * @return an iterator with the defined keys in this configuration 639 */ 640 @Override 641 protected Iterator<String> getKeysInternal() 642 { 643 return visitDefinedKeys().getKeyList().iterator(); 644 } 645 646 /** 647 * Creates a {@code DefinedKeysVisitor} and visits all defined keys with it. 648 * 649 * @return the visitor after all keys have been visited 650 */ 651 private DefinedKeysVisitor visitDefinedKeys() 652 { 653 final DefinedKeysVisitor visitor = new DefinedKeysVisitor(); 654 final NodeHandler<T> nodeHandler = getModel().getNodeHandler(); 655 NodeTreeWalker.INSTANCE.walkDFS(nodeHandler.getRootNode(), visitor, 656 nodeHandler); 657 return visitor; 658 } 659 660 /** 661 * Returns an iterator with all keys defined in this configuration that 662 * start with the given prefix. The returned keys will not contain any 663 * indices. This implementation tries to locate a node whose key is the same 664 * as the passed in prefix. Then the subtree of this node is traversed, and 665 * the keys of all nodes encountered (including attributes) are added to the 666 * result set. 667 * 668 * @param prefix the prefix of the keys to start with 669 * @return an iterator with the found keys 670 */ 671 @Override 672 protected Iterator<String> getKeysInternal(final String prefix) 673 { 674 final DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix); 675 if (containsKey(prefix)) 676 { 677 // explicitly add the prefix 678 visitor.getKeyList().add(prefix); 679 } 680 681 final List<QueryResult<T>> results = fetchNodeList(prefix); 682 final NodeHandler<T> handler = getModel().getNodeHandler(); 683 684 for (final QueryResult<T> result : results) 685 { 686 if (!result.isAttributeResult()) 687 { 688 for (final T c : handler.getChildren(result.getNode())) 689 { 690 NodeTreeWalker.INSTANCE.walkDFS(c, visitor, handler); 691 } 692 visitor.handleAttributeKeys(prefix, result.getNode(), handler); 693 } 694 } 695 696 return visitor.getKeyList().iterator(); 697 } 698 699 /** 700 * Returns the maximum defined index for the given key. This is useful if 701 * there are multiple values for this key. They can then be addressed 702 * separately by specifying indices from 0 to the return value of this 703 * method. If the passed in key is not contained in this configuration, 704 * result is -1. 705 * 706 * @param key the key to be checked 707 * @return the maximum defined index for this key 708 */ 709 @Override 710 public final int getMaxIndex(final String key) 711 { 712 beginRead(false); 713 try 714 { 715 return getMaxIndexInternal(key); 716 } 717 finally 718 { 719 endRead(); 720 } 721 } 722 723 /** 724 * Actually retrieves the maximum defined index for the given key. This 725 * method is called by {@code getMaxIndex()}. Subclasses that need to adapt 726 * this operation have to override this method. 727 * 728 * @param key the key to be checked 729 * @return the maximum defined index for this key 730 * @since 2.0 731 */ 732 protected int getMaxIndexInternal(final String key) 733 { 734 return fetchNodeList(key).size() - 1; 735 } 736 737 /** 738 * Creates a copy of this object. This new configuration object will contain 739 * copies of all nodes in the same structure. Registered event listeners 740 * won't be cloned; so they are not registered at the returned copy. 741 * 742 * @return the copy 743 * @since 1.2 744 */ 745 @Override 746 public Object clone() 747 { 748 beginRead(false); 749 try 750 { 751 @SuppressWarnings("unchecked") // clone returns the same type 752 final 753 AbstractHierarchicalConfiguration<T> copy = 754 (AbstractHierarchicalConfiguration<T>) super.clone(); 755 copy.setSynchronizer(NoOpSynchronizer.INSTANCE); 756 copy.cloneInterpolator(this); 757 copy.setSynchronizer(ConfigurationUtils.cloneSynchronizer(getSynchronizer())); 758 copy.model = cloneNodeModel(); 759 760 return copy; 761 } 762 catch (final CloneNotSupportedException cex) 763 { 764 // should not happen 765 throw new ConfigurationRuntimeException(cex); 766 } 767 finally 768 { 769 endRead(); 770 } 771 } 772 773 /** 774 * Creates a clone of the node model. This method is called by 775 * {@code clone()}. 776 * 777 * @return the clone of the {@code NodeModel} 778 * @since 2.0 779 */ 780 protected abstract NodeModel<T> cloneNodeModel(); 781 782 /** 783 * Helper method for resolving the specified key. 784 * 785 * @param key the key 786 * @return a list with all results selected by this key 787 */ 788 protected List<QueryResult<T>> fetchNodeList(final String key) 789 { 790 final NodeHandler<T> nodeHandler = getModel().getNodeHandler(); 791 return resolveKey(nodeHandler.getRootNode(), key, nodeHandler); 792 } 793 794 /** 795 * Checks if the specified node is defined. 796 * 797 * @param node the node to be checked 798 * @return a flag if this node is defined 799 */ 800 protected boolean nodeDefined(final T node) 801 { 802 final DefinedVisitor<T> visitor = new DefinedVisitor<>(); 803 NodeTreeWalker.INSTANCE.walkBFS(node, visitor, getModel().getNodeHandler()); 804 return visitor.isDefined(); 805 } 806 807 /** 808 * Returns the {@code NodeModel} used by this configuration. This method is 809 * intended for internal use only. Access to the model is granted without 810 * any synchronization. This is in contrast to the "official" 811 * {@code getNodeModel()} method which is guarded by the configuration's 812 * {@code Synchronizer}. 813 * 814 * @return the node model 815 */ 816 protected NodeModel<T> getModel() 817 { 818 return model; 819 } 820 821 /** 822 * Extracts the value from a query result. 823 * 824 * @param result the {@code QueryResult} 825 * @param handler the {@code NodeHandler} 826 * @return the value of this result (may be <b>null</b>) 827 */ 828 private Object valueFromResult(final QueryResult<T> result, final NodeHandler<T> handler) 829 { 830 return result.isAttributeResult() ? result.getAttributeValue(handler) 831 : handler.getValue(result.getNode()); 832 } 833 834 /** 835 * A specialized visitor that checks if a node is defined. 836 * "Defined" in this terms means that the node or at least one of 837 * its sub nodes is associated with a value. 838 * 839 * @param <T> the type of the nodes managed by this hierarchical configuration 840 */ 841 private static class DefinedVisitor<T> extends 842 ConfigurationNodeVisitorAdapter<T> 843 { 844 /** Stores the defined flag. */ 845 private boolean defined; 846 847 /** 848 * Checks if iteration should be stopped. This can be done if the first 849 * defined node is found. 850 * 851 * @return a flag if iteration should be stopped 852 */ 853 @Override 854 public boolean terminate() 855 { 856 return isDefined(); 857 } 858 859 /** 860 * Visits the node. Checks if a value is defined. 861 * 862 * @param node the actual node 863 */ 864 @Override 865 public void visitBeforeChildren(final T node, final NodeHandler<T> handler) 866 { 867 defined = 868 handler.getValue(node) != null 869 || !handler.getAttributes(node).isEmpty(); 870 } 871 872 /** 873 * Returns the defined flag. 874 * 875 * @return the defined flag 876 */ 877 public boolean isDefined() 878 { 879 return defined; 880 } 881 } 882 883 /** 884 * A specialized visitor that fills a list with keys that are defined in a 885 * node hierarchy. 886 */ 887 private class DefinedKeysVisitor extends 888 ConfigurationNodeVisitorAdapter<T> 889 { 890 /** Stores the list to be filled. */ 891 private final Set<String> keyList; 892 893 /** A stack with the keys of the already processed nodes. */ 894 private final Stack<String> parentKeys; 895 896 /** 897 * Default constructor. 898 */ 899 public DefinedKeysVisitor() 900 { 901 keyList = new LinkedHashSet<>(); 902 parentKeys = new Stack<>(); 903 } 904 905 /** 906 * Creates a new {@code DefinedKeysVisitor} instance and sets the 907 * prefix for the keys to fetch. 908 * 909 * @param prefix the prefix 910 */ 911 public DefinedKeysVisitor(final String prefix) 912 { 913 this(); 914 parentKeys.push(prefix); 915 } 916 917 /** 918 * Returns the list with all defined keys. 919 * 920 * @return the list with the defined keys 921 */ 922 public Set<String> getKeyList() 923 { 924 return keyList; 925 } 926 927 /** 928 * {@inheritDoc} This implementation removes this 929 * node's key from the stack. 930 */ 931 @Override 932 public void visitAfterChildren(final T node, final NodeHandler<T> handler) 933 { 934 parentKeys.pop(); 935 } 936 937 /** 938 * {@inheritDoc} If this node has a value, its key is added 939 * to the internal list. 940 */ 941 @Override 942 public void visitBeforeChildren(final T node, final NodeHandler<T> handler) 943 { 944 final String parentKey = parentKeys.isEmpty() ? null 945 : parentKeys.peek(); 946 final String key = getExpressionEngine().nodeKey(node, parentKey, handler); 947 parentKeys.push(key); 948 if (handler.getValue(node) != null) 949 { 950 keyList.add(key); 951 } 952 handleAttributeKeys(key, node, handler); 953 } 954 955 /** 956 * Appends all attribute keys of the current node. 957 * 958 * @param parentKey the parent key 959 * @param node the current node 960 * @param handler the {@code NodeHandler} 961 */ 962 public void handleAttributeKeys(final String parentKey, final T node, 963 final NodeHandler<T> handler) 964 { 965 for (final String attr : handler.getAttributes(node)) 966 { 967 keyList.add(getExpressionEngine().attributeKey(parentKey, attr)); 968 } 969 } 970 } 971 972 @Override 973 public String toString() 974 { 975 return super.toString() + "(" + getRootElementNameInternal() + ")"; 976 } 977}