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