NodeTreeWalker.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.configuration2.tree;

  18. import java.util.LinkedList;
  19. import java.util.List;

  20. /**
  21.  * <p>
  22.  * A class providing different algorithms for traversing a hierarchy of configuration nodes.
  23.  * </p>
  24.  * <p>
  25.  * The methods provided by this class accept a {@link ConfigurationNodeVisitor} and visit all nodes in a hierarchy
  26.  * starting from a given root node. Because a {@link NodeHandler} has to be passed in, too, arbitrary types of nodes can
  27.  * be processed. The {@code walk()} methods differ in the order in which nodes are visited. Details can be found in the
  28.  * method documentation.
  29.  * </p>
  30.  * <p>
  31.  * An instance of this class does not define any state; therefore, it can be shared and used concurrently. The
  32.  * {@code INSTANCE} member field can be used for accessing a default instance. If desired (for example for testing purposes),
  33.  * new instances can be created.
  34.  * </p>
  35.  *
  36.  * @since 2.0
  37.  */
  38. public class NodeTreeWalker {
  39.     /** The default instance of this class. */
  40.     public static final NodeTreeWalker INSTANCE = new NodeTreeWalker();

  41.     /**
  42.      * Helper method for performing a BFS traversal. Implementation node: This method organizes the nodes to be visited in
  43.      * structures on the heap. Therefore, it can deal with larger structures than would be the case in a recursive approach
  44.      * (where the stack size limits the size of the structures which can be traversed).
  45.      *
  46.      * @param root the root node to be navigated
  47.      * @param visitor the visitor
  48.      * @param handler the handler
  49.      * @param <T> the type of the nodes involved
  50.      */
  51.     private static <T> void bfs(final T root, final ConfigurationNodeVisitor<T> visitor, final NodeHandler<T> handler) {
  52.         final List<T> pendingNodes = new LinkedList<>();
  53.         pendingNodes.add(root);
  54.         boolean cancel = false;

  55.         while (!pendingNodes.isEmpty() && !cancel) {
  56.             final T node = pendingNodes.remove(0);
  57.             visitor.visitBeforeChildren(node, handler);
  58.             cancel = visitor.terminate();
  59.             pendingNodes.addAll(handler.getChildren(node));
  60.         }
  61.     }

  62.     /**
  63.      * Helper method for checking the parameters for the walk() methods. If mandatory parameters are missing, an exception
  64.      * is thrown. The return value indicates whether an operation can be performed.
  65.      *
  66.      * @param root the root node
  67.      * @param visitor the visitor
  68.      * @param handler the handler
  69.      * @param <T> the type of the nodes involved
  70.      * @return <strong>true</strong> if a walk operation can be performed, <strong>false</strong> otherwise
  71.      * @throws IllegalArgumentException if a required parameter is missing
  72.      */
  73.     private static <T> boolean checkParameters(final T root, final ConfigurationNodeVisitor<T> visitor, final NodeHandler<T> handler) {
  74.         if (visitor == null) {
  75.             throw new IllegalArgumentException("Visitor must not be null!");
  76.         }
  77.         if (handler == null) {
  78.             throw new IllegalArgumentException("NodeHandler must not be null!");
  79.         }
  80.         return root != null;
  81.     }

  82.     /**
  83.      * Recursive helper method for performing a DFS traversal.
  84.      *
  85.      * @param node the current node
  86.      * @param visitor the visitor
  87.      * @param handler the handler
  88.      * @param <T> the type of the nodes involved
  89.      */
  90.     private static <T> void dfs(final T node, final ConfigurationNodeVisitor<T> visitor, final NodeHandler<T> handler) {
  91.         if (!visitor.terminate()) {
  92.             visitor.visitBeforeChildren(node, handler);
  93.             handler.getChildren(node).forEach(c -> dfs(c, visitor, handler));
  94.             if (!visitor.terminate()) {
  95.                 visitor.visitAfterChildren(node, handler);
  96.             }
  97.         }
  98.     }

  99.     /**
  100.      * Visits all nodes in the hierarchy represented by the given root node in <em>breadth first search</em> manner. This
  101.      * means that the nodes are visited in an order corresponding to the distance from the root node: first the root node is
  102.      * visited, then all direct children of the root node, then all direct children of the first child of the root node,
  103.      * etc. In this mode of traversal, there is no direct connection between the encounter of a node and its children.
  104.      * <strong>Therefore, on the visitor object only the {@code visitBeforeChildren()} method gets called!</strong>.
  105.      *
  106.      * @param root the root node of the hierarchy to be processed (may be <strong>null</strong>, then this call has no effect)
  107.      * @param visitor the {@code ConfigurationNodeVisitor} (must not be <strong>null</strong>)
  108.      * @param handler the {@code NodeHandler} (must not be <strong>null</strong>)
  109.      * @param <T> the type of the nodes involved
  110.      * @throws IllegalArgumentException if a required parameter is <strong>null</strong>
  111.      */
  112.     public <T> void walkBFS(final T root, final ConfigurationNodeVisitor<T> visitor, final NodeHandler<T> handler) {
  113.         if (checkParameters(root, visitor, handler)) {
  114.             bfs(root, visitor, handler);
  115.         }
  116.     }

  117.     /**
  118.      * Visits all nodes in the hierarchy represented by the given root node in <em>depth first search</em> manner. This
  119.      * means that first {@link ConfigurationNodeVisitor#visitBeforeChildren(Object, NodeHandler)} is called on a node, then
  120.      * recursively all of its children are processed, and eventually
  121.      * {@link ConfigurationNodeVisitor#visitAfterChildren(Object, NodeHandler)} gets invoked.
  122.      *
  123.      * @param root the root node of the hierarchy to be processed (may be <strong>null</strong>, then this call has no effect)
  124.      * @param visitor the {@code ConfigurationNodeVisitor} (must not be <strong>null</strong>)
  125.      * @param handler the {@code NodeHandler} (must not be <strong>null</strong>)
  126.      * @param <T> the type of the nodes involved
  127.      * @throws IllegalArgumentException if a required parameter is <strong>null</strong>
  128.      */
  129.     public <T> void walkDFS(final T root, final ConfigurationNodeVisitor<T> visitor, final NodeHandler<T> handler) {
  130.         if (checkParameters(root, visitor, handler)) {
  131.             dfs(root, visitor, handler);
  132.         }
  133.     }
  134. }