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

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang3.builder.ToStringBuilder;

A class for selecting a specific node based on a key or a set of keys.

An instance of this class is initialized with the key of a node. It is also possible to concatenate multiple keys - e.g. if a sub key is to be constructed from another sub key. NodeSelector provides the select() method which evaluates the wrapped keys on a specified root node and returns the resulting unique target node. The class expects that the key(s) stored in an instance select exactly one target node. If this is not the case, result is null indicating that the selection criteria are not sufficient.

Implementation node: Instances of this class are immutable. They can be shared between arbitrary components.

Since:2.0
/** * <p> * A class for selecting a specific node based on a key or a set of keys. * </p> * <p> * An instance of this class is initialized with the key of a node. It is also * possible to concatenate multiple keys - e.g. if a sub key is to be * constructed from another sub key. {@code NodeSelector} provides the * {@code select()} method which evaluates the wrapped keys on a specified root * node and returns the resulting unique target node. The class expects that the * key(s) stored in an instance select exactly one target node. If this is not * the case, result is <b>null</b> indicating that the selection criteria are * not sufficient. * </p> * <p> * Implementation node: Instances of this class are immutable. They can be * shared between arbitrary components. * </p> * * @since 2.0 */
public class NodeSelector {
Stores the wrapped keys.
/** Stores the wrapped keys. */
private final List<String> nodeKeys;
Creates a new instance of NodeSelector and initializes it with the key to the target node.
Params:
  • key – the key
/** * Creates a new instance of {@code NodeSelector} and initializes it with * the key to the target node. * * @param key the key */
public NodeSelector(final String key) { this(Collections.singletonList(key)); }
Creates a new instance of NodeSelector and initializes it with the list of keys to be used as selection criteria.
Params:
  • keys – the keys for selecting nodes
/** * Creates a new instance of {@code NodeSelector} and initializes it with * the list of keys to be used as selection criteria. * * @param keys the keys for selecting nodes */
private NodeSelector(final List<String> keys) { nodeKeys = keys; }
Applies this NodeSelector on the specified root node. This method applies the selection criteria stored in this object and tries to determine a single target node. If this is successful, the target node is returned. Otherwise, result is null.
Params:
  • root – the root node on which to apply this selector
  • resolver – the NodeKeyResolver
  • handler – the NodeHandler
Returns:the selected target node or null
/** * Applies this {@code NodeSelector} on the specified root node. This method * applies the selection criteria stored in this object and tries to * determine a single target node. If this is successful, the target node is * returned. Otherwise, result is <b>null</b>. * * @param root the root node on which to apply this selector * @param resolver the {@code NodeKeyResolver} * @param handler the {@code NodeHandler} * @return the selected target node or <b>null</b> */
public ImmutableNode select(final ImmutableNode root, final NodeKeyResolver<ImmutableNode> resolver, final NodeHandler<ImmutableNode> handler) { List<ImmutableNode> nodes = new LinkedList<>(); final Iterator<String> itKeys = nodeKeys.iterator(); getFilteredResults(root, resolver, handler, itKeys.next(), nodes); while (itKeys.hasNext()) { final String currentKey = itKeys.next(); final List<ImmutableNode> currentResults = new LinkedList<>(); for (final ImmutableNode currentRoot : nodes) { getFilteredResults(currentRoot, resolver, handler, currentKey, currentResults); } nodes = currentResults; } return nodes.size() == 1 ? nodes.get(0) : null; }
Creates a sub NodeSelector object which uses the key(s) of this selector plus the specified key as selection criteria. This is useful when another selection is to be performed on the results of a first selector.
Params:
  • subKey – the additional key for the sub selector
Returns:the sub NodeSelector instance
/** * Creates a sub {@code NodeSelector} object which uses the key(s) of this * selector plus the specified key as selection criteria. This is useful * when another selection is to be performed on the results of a first * selector. * * @param subKey the additional key for the sub selector * @return the sub {@code NodeSelector} instance */
public NodeSelector subSelector(final String subKey) { final List<String> keys = new ArrayList<>(nodeKeys.size() + 1); keys.addAll(nodeKeys); keys.add(subKey); return new NodeSelector(keys); }
Compares this object with another one. Two instances of NodeSelector are considered equal if they have the same keys as selection criteria.
Params:
  • obj – the object to be compared
Returns:a flag whether these objects are equal
/** * Compares this object with another one. Two instances of * {@code NodeSelector} are considered equal if they have the same keys as * selection criteria. * * @param obj the object to be compared * @return a flag whether these objects are equal */
@Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof NodeSelector)) { return false; } final NodeSelector c = (NodeSelector) obj; return nodeKeys.equals(c.nodeKeys); }
Returns a hash code for this object.
Returns:a hash code
/** * Returns a hash code for this object. * * @return a hash code */
@Override public int hashCode() { return nodeKeys.hashCode(); }
Returns a string representation for this object. This string contains the keys to be used as selection criteria.
Returns:a string for this object
/** * Returns a string representation for this object. This string contains the * keys to be used as selection criteria. * * @return a string for this object */
@Override public String toString() { return new ToStringBuilder(this).append("keys", nodeKeys).toString(); }
Executes a query for a given key and filters the results for nodes only.
Params:
  • root – the root node for the query
  • resolver – the NodeKeyResolver
  • handler – the NodeHandler
  • key – the key
  • nodes – here the results are stored
/** * Executes a query for a given key and filters the results for nodes only. * * @param root the root node for the query * @param resolver the {@code NodeKeyResolver} * @param handler the {@code NodeHandler} * @param key the key * @param nodes here the results are stored */
private void getFilteredResults(final ImmutableNode root, final NodeKeyResolver<ImmutableNode> resolver, final NodeHandler<ImmutableNode> handler, final String key, final List<ImmutableNode> nodes) { final List<QueryResult<ImmutableNode>> results = resolver.resolveKey(root, key, handler); for (final QueryResult<ImmutableNode> result : results) { if (!result.isAttributeResult()) { nodes.add(result.getNode()); } } } }