View Javadoc
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 }