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 * https://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.QName;
23 import org.apache.commons.jxpath.ri.model.NodePointer;
24 import org.apache.commons.jxpath.ri.model.NodePointerFactory;
25
26 /**
27 * <p>
28 * Implements the {@code NodePointerFactory} interface for configuration nodes.
29 * </p>
30 * <p>
31 * This class is able to create {@code NodePointer}s for the nodes of hierarchical configurations. Because there is no
32 * common base class for configuration nodes (any specific configuration implementation can use its own node class) a
33 * trick is needed for activating this factory for a concrete JXPath query: The {@code wrapNode()} method has to be
34 * called with the node object and its corresponding {@code NodeHandler}. This creates a wrapper object containing all
35 * information required by the factory for processing a query. Then this wrapper object has to be passed to the query
36 * methods of the JXPath context.
37 * </p>
38 *
39 * @since 1.3
40 */
41 public class ConfigurationNodePointerFactory implements NodePointerFactory {
42
43 /**
44 * An internally used wrapper class that holds all information for processing a query for a specific node.
45 *
46 * @param <T> the type of the nodes this class deals with
47 */
48 static class NodeWrapper<T> {
49
50 /** Stores the node. */
51 private final T node;
52
53 /** Stores the corresponding node handler. */
54 private final NodeHandler<T> nodeHandler;
55
56 /**
57 * Creates a new instance of {@code NodeWrapper} and initializes it.
58 *
59 * @param nd the node
60 * @param handler the node handler
61 */
62 public NodeWrapper(final T nd, final NodeHandler<T> handler) {
63 node = nd;
64 nodeHandler = handler;
65 }
66
67 /**
68 * Gets the wrapped node.
69 *
70 * @return the node
71 */
72 public T getNode() {
73 return node;
74 }
75
76 /**
77 * Gets the node handler for the wrapped node.
78 *
79 * @return the node handler
80 */
81 public NodeHandler<T> getNodeHandler() {
82 return nodeHandler;
83 }
84 }
85
86 /** Constant for the order of this factory. */
87 public static final int CONFIGURATION_NODE_POINTER_FACTORY_ORDER = 200;
88
89 /**
90 * Creates a node wrapper for the specified node and its handler. This wrapper has to be passed to the JXPath context
91 * instead of the original node.
92 *
93 * @param <T> the type of the node
94 * @param node the node
95 * @param handler the corresponding node handler
96 * @return a wrapper for this node
97 */
98 public static <T> Object wrapNode(final T node, final NodeHandler<T> handler) {
99 return new NodeWrapper<>(node, handler);
100 }
101
102 /**
103 * Constructs a new instance.
104 */
105 public ConfigurationNodePointerFactory() {
106 // empty
107 }
108
109 /**
110 * Creates a node pointer for the specified bean. If the bean is a configuration node, a corresponding pointer is
111 * returned.
112 *
113 * @param parent the parent node
114 * @param qName the name
115 * @param bean the bean
116 * @return a pointer for a configuration node if the bean is such a node
117 */
118 @Override
119 @SuppressWarnings("unchecked")
120 /*
121 * Type casts are safe here, see above. Also, the hierarchy of node pointers is consistent, so a parent is compatible to
122 * a child.
123 */
124 public NodePointer createNodePointer(final NodePointer parent, final QName qName, final Object bean) {
125 if (bean instanceof NodeWrapper) {
126 final NodeWrapper<Object> wrapper = (NodeWrapper<Object>) bean;
127 return new ConfigurationNodePointer<>((ConfigurationNodePointer<Object>) parent, wrapper.getNode(), wrapper.getNodeHandler());
128 }
129 return null;
130 }
131
132 /**
133 * Creates a node pointer for the specified bean. If the bean is a configuration node (indicated by a wrapper object), a
134 * corresponding pointer is returned.
135 *
136 * @param qName the name of the node
137 * @param bean the bean
138 * @param locale the locale
139 * @return a pointer for a configuration node if the bean is such a node
140 */
141 @Override
142 @SuppressWarnings("unchecked")
143 /*
144 * Type casts are safe here; because of the way the NodeWrapper was constructed the node handler must be compatible with
145 * the node.
146 */
147 public NodePointer createNodePointer(final QName qName, final Object bean, final Locale locale) {
148 if (bean instanceof NodeWrapper) {
149 final NodeWrapper<Object> wrapper = (NodeWrapper<Object>) bean;
150 return new ConfigurationNodePointer<>(wrapper.getNode(), locale, wrapper.getNodeHandler());
151 }
152 return null;
153 }
154
155 /**
156 * Gets the order of this factory between other factories.
157 *
158 * @return this order's factory
159 */
160 @Override
161 public int getOrder() {
162 return CONFIGURATION_NODE_POINTER_FACTORY_ORDER;
163 }
164 }