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.xpath; 18 19 import java.util.Locale; 20 21 import org.apache.commons.configuration2.tree.NodeHandler; 22 import org.apache.commons.jxpath.ri.Compiler; 23 import org.apache.commons.jxpath.ri.QName; 24 import org.apache.commons.jxpath.ri.compiler.NodeTest; 25 import org.apache.commons.jxpath.ri.compiler.NodeTypeTest; 26 import org.apache.commons.jxpath.ri.model.NodeIterator; 27 import org.apache.commons.jxpath.ri.model.NodePointer; 28 29 /** 30 * <p> 31 * A specific {@code NodePointer} implementation for configuration nodes. 32 * </p> 33 * <p> 34 * This is needed for queries using JXPath. 35 * </p> 36 * 37 * @since 1.3 38 * @param <T> the type of the nodes this pointer deals with 39 */ 40 final class ConfigurationNodePointer<T> extends NodePointer { 41 /** 42 * The serial version UID. 43 */ 44 private static final long serialVersionUID = -1087475639680007713L; 45 46 /** The node handler. */ 47 private final NodeHandler<T> handler; 48 49 /** Stores the associated node. */ 50 private final T node; 51 52 /** 53 * Creates a new instance of {@code ConfigurationNodePointer} pointing to the specified node. 54 * 55 * @param node the wrapped node 56 * @param locale the locale 57 * @param handler the {@code NodeHandler} 58 */ 59 public ConfigurationNodePointer(final T node, final Locale locale, final NodeHandler<T> handler) { 60 super(null, locale); 61 this.node = node; 62 this.handler = handler; 63 } 64 65 /** 66 * Creates a new instance of {@code ConfigurationNodePointer} and initializes it with its parent pointer. 67 * 68 * @param parent the parent pointer 69 * @param node the associated node 70 * @param handler the {@code NodeHandler} 71 */ 72 public ConfigurationNodePointer(final ConfigurationNodePointer<T> parent, final T node, final NodeHandler<T> handler) { 73 super(parent); 74 this.node = node; 75 this.handler = handler; 76 } 77 78 /** 79 * Returns a flag whether this node is a leaf. This is the case if there are no child nodes. 80 * 81 * @return a flag if this node is a leaf 82 */ 83 @Override 84 public boolean isLeaf() { 85 return getNodeHandler().getChildrenCount(node, null) < 1; 86 } 87 88 /** 89 * Returns a flag if this node is a collection. This is not the case. 90 * 91 * @return the collection flag 92 */ 93 @Override 94 public boolean isCollection() { 95 return false; 96 } 97 98 /** 99 * Gets this node's length. This is always 1. 100 * 101 * @return the node's length 102 */ 103 @Override 104 public int getLength() { 105 return 1; 106 } 107 108 /** 109 * Checks whether this node pointer refers to an attribute node. This is not the case. 110 * 111 * @return the attribute flag 112 */ 113 @Override 114 public boolean isAttribute() { 115 return false; 116 } 117 118 /** 119 * Gets this node's name. 120 * 121 * @return the name 122 */ 123 @Override 124 public QName getName() { 125 return new QName(null, getNodeHandler().nodeName(node)); 126 } 127 128 /** 129 * Gets this node's base value. This is the associated configuration node. 130 * 131 * @return the base value 132 */ 133 @Override 134 public Object getBaseValue() { 135 return node; 136 } 137 138 /** 139 * Gets the immediate node. This is the associated configuration node. 140 * 141 * @return the immediate node 142 */ 143 @Override 144 public Object getImmediateNode() { 145 return node; 146 } 147 148 /** 149 * Gets the value of this node. 150 * 151 * @return the represented node's value 152 */ 153 @Override 154 public Object getValue() { 155 return getNodeHandler().getValue(node); 156 } 157 158 /** 159 * Sets the value of this node. This is not supported, so always an exception is thrown. 160 * 161 * @param value the new value 162 */ 163 @Override 164 public void setValue(final Object value) { 165 throw new UnsupportedOperationException("Node value cannot be set!"); 166 } 167 168 /** 169 * Compares two child node pointers. 170 * 171 * @param pointer1 one pointer 172 * @param pointer2 another pointer 173 * @return a flag, which pointer should be sorted first 174 */ 175 @Override 176 public int compareChildNodePointers(final NodePointer pointer1, final NodePointer pointer2) { 177 final Object node1 = pointer1.getBaseValue(); 178 final Object node2 = pointer2.getBaseValue(); 179 180 // sort based on the occurrence in the sub node list 181 for (final T child : getNodeHandler().getChildren(node)) { 182 if (child == node1) { 183 return -1; 184 } 185 if (child == node2) { 186 return 1; 187 } 188 } 189 return 0; // should not happen 190 } 191 192 /** 193 * Returns an iterator for the attributes that match the given name. 194 * 195 * @param name the attribute name 196 * @return the iterator for the attributes 197 */ 198 @Override 199 public NodeIterator attributeIterator(final QName name) { 200 return new ConfigurationNodeIteratorAttribute<>(this, name); 201 } 202 203 /** 204 * Returns an iterator for the children of this pointer that match the given test object. 205 * 206 * @param test the test object 207 * @param reverse the reverse flag 208 * @param startWith the start value of the iteration 209 */ 210 @Override 211 public NodeIterator childIterator(final NodeTest test, final boolean reverse, final NodePointer startWith) { 212 return new ConfigurationNodeIteratorChildren<>(this, test, reverse, castPointer(startWith)); 213 } 214 215 /** 216 * Tests if this node matches the given test. Configuration nodes are text nodes, too because they can contain a value. 217 * 218 * @param test the test object 219 * @return a flag if this node corresponds to the test 220 */ 221 @Override 222 public boolean testNode(final NodeTest test) { 223 if (test instanceof NodeTypeTest && ((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_TEXT) { 224 return true; 225 } 226 return super.testNode(test); 227 } 228 229 /** 230 * Gets the {@code NodeHandler} used by this instance. 231 * 232 * @return the {@code NodeHandler} 233 */ 234 public NodeHandler<T> getNodeHandler() { 235 return handler; 236 } 237 238 /** 239 * Gets the wrapped configuration node. 240 * 241 * @return the wrapped node 242 */ 243 public T getConfigurationNode() { 244 return node; 245 } 246 247 /** 248 * Casts the given child pointer to a node pointer of this type. This is a bit dangerous. However, in a typical setup, 249 * child node pointers can only be created by this instance which ensures that they are of the correct type. Therefore, 250 * this cast is safe. 251 * 252 * @param p the {@code NodePointer} to cast 253 * @return the resulting {@code ConfigurationNodePointer} 254 */ 255 private ConfigurationNodePointer<T> castPointer(final NodePointer p) { 256 @SuppressWarnings("unchecked") // see method comment 257 final ConfigurationNodePointer<T> result = (ConfigurationNodePointer<T>) p; 258 return result; 259 } 260 }