001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.configuration2.tree.xpath;
018
019import java.util.Locale;
020
021import org.apache.commons.configuration2.tree.NodeHandler;
022import org.apache.commons.jxpath.ri.QName;
023import org.apache.commons.jxpath.ri.model.NodePointer;
024import org.apache.commons.jxpath.ri.model.NodePointerFactory;
025
026/**
027 * <p>
028 * Implementation of the {@code NodePointerFactory} interface for configuration
029 * nodes.
030 * </p>
031 * <p>
032 * This class is able to create {@code NodePointer}s for the nodes of
033 * hierarchical configurations. Because there is no common base class for
034 * configuration nodes (any specific configuration implementation can use its
035 * own node class) a trick is needed for activating this factory for a concrete
036 * JXPath query: The {@code wrapNode()} method has to be called with the node
037 * object and its corresponding {@code NodeHandler}. This creates a wrapper
038 * object containing all information required by the factory for processing a
039 * query. Then this wrapper object has to be passed to the query methods of the
040 * JXPath context.
041 * </p>
042 *
043 * @since 1.3
044 * @version $Id: ConfigurationNodePointerFactory.java 1790899 2017-04-10 21:56:46Z ggregory $
045 */
046public class ConfigurationNodePointerFactory implements NodePointerFactory
047{
048    /** Constant for the order of this factory. */
049    public static final int CONFIGURATION_NODE_POINTER_FACTORY_ORDER = 200;
050
051    /**
052     * Returns the order of this factory between other factories.
053     *
054     * @return this order's factory
055     */
056    @Override
057    public int getOrder()
058    {
059        return CONFIGURATION_NODE_POINTER_FACTORY_ORDER;
060    }
061
062    /**
063     * Creates a node pointer for the specified bean. If the bean is a
064     * configuration node (indicated by a wrapper object), a corresponding
065     * pointer is returned.
066     *
067     * @param name the name of the node
068     * @param bean the bean
069     * @param locale the locale
070     * @return a pointer for a configuration node if the bean is such a node
071     */
072    @Override
073    @SuppressWarnings("unchecked")
074    /* Type casts are safe here; because of the way the NodeWrapper was
075       constructed the node handler must be compatible with the node.
076     */
077    public NodePointer createNodePointer(QName name, Object bean, Locale locale)
078    {
079        if (bean instanceof NodeWrapper)
080        {
081            NodeWrapper<?> wrapper = (NodeWrapper<?>) bean;
082            return new ConfigurationNodePointer(wrapper.getNode(),
083                    locale, wrapper.getNodeHandler());
084        }
085        return null;
086    }
087
088    /**
089     * Creates a node pointer for the specified bean. If the bean is a
090     * configuration node, a corresponding pointer is returned.
091     *
092     * @param parent the parent node
093     * @param name the name
094     * @param bean the bean
095     * @return a pointer for a configuration node if the bean is such a node
096     */
097    @Override
098    @SuppressWarnings("unchecked")
099    /* Type casts are safe here, see above. Also, the hierarchy of node
100       pointers is consistent, so a parent is compatible to a child.
101     */
102    public NodePointer createNodePointer(NodePointer parent, QName name,
103            Object bean)
104    {
105        if (bean instanceof NodeWrapper)
106        {
107            NodeWrapper<?> wrapper = (NodeWrapper<?>) bean;
108            return new ConfigurationNodePointer((ConfigurationNodePointer) parent,
109                    wrapper.getNode(), wrapper.getNodeHandler());
110        }
111        return null;
112    }
113
114    /**
115     * Creates a node wrapper for the specified node and its handler. This
116     * wrapper has to be passed to the JXPath context instead of the original
117     * node.
118     *
119     * @param <T> the type of the node
120     * @param node the node
121     * @param handler the corresponding node handler
122     * @return a wrapper for this node
123     */
124    public static <T> Object wrapNode(T node, NodeHandler<T> handler)
125    {
126        return new NodeWrapper<>(node, handler);
127    }
128
129    /**
130     * An internally used wrapper class that holds all information for
131     * processing a query for a specific node.
132     *
133     * @param <T> the type of the nodes this class deals with
134     */
135    static class NodeWrapper<T>
136    {
137        /** Stores the node. */
138        private final T node;
139
140        /** Stores the corresponding node handler. */
141        private final NodeHandler<T> nodeHandler;
142
143        /**
144         * Creates a new instance of {@code NodeWrapper} and initializes it.
145         *
146         * @param nd the node
147         * @param handler the node handler
148         */
149        public NodeWrapper(T nd, NodeHandler<T> handler)
150        {
151            node = nd;
152            nodeHandler = handler;
153        }
154
155        /**
156         * Returns the wrapped node.
157         *
158         * @return the node
159         */
160        public T getNode()
161        {
162            return node;
163        }
164
165        /**
166         * Returns the node handler for the wrapped node.
167         *
168         * @return the node handler
169         */
170        public NodeHandler<T> getNodeHandler()
171        {
172            return nodeHandler;
173        }
174    }
175}