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 */
045public class ConfigurationNodePointerFactory implements NodePointerFactory
046{
047    /** Constant for the order of this factory. */
048    public static final int CONFIGURATION_NODE_POINTER_FACTORY_ORDER = 200;
049
050    /**
051     * Returns the order of this factory between other factories.
052     *
053     * @return this order's factory
054     */
055    @Override
056    public int getOrder()
057    {
058        return CONFIGURATION_NODE_POINTER_FACTORY_ORDER;
059    }
060
061    /**
062     * Creates a node pointer for the specified bean. If the bean is a
063     * configuration node (indicated by a wrapper object), a corresponding
064     * pointer is returned.
065     *
066     * @param name the name of the node
067     * @param bean the bean
068     * @param locale the locale
069     * @return a pointer for a configuration node if the bean is such a node
070     */
071    @Override
072    @SuppressWarnings("unchecked")
073    /* Type casts are safe here; because of the way the NodeWrapper was
074       constructed the node handler must be compatible with the node.
075     */
076    public NodePointer createNodePointer(final QName name, final Object bean, final Locale locale)
077    {
078        if (bean instanceof NodeWrapper)
079        {
080            final NodeWrapper<?> wrapper = (NodeWrapper<?>) bean;
081            return new ConfigurationNodePointer(wrapper.getNode(),
082                    locale, wrapper.getNodeHandler());
083        }
084        return null;
085    }
086
087    /**
088     * Creates a node pointer for the specified bean. If the bean is a
089     * configuration node, a corresponding pointer is returned.
090     *
091     * @param parent the parent node
092     * @param name the name
093     * @param bean the bean
094     * @return a pointer for a configuration node if the bean is such a node
095     */
096    @Override
097    @SuppressWarnings("unchecked")
098    /* Type casts are safe here, see above. Also, the hierarchy of node
099       pointers is consistent, so a parent is compatible to a child.
100     */
101    public NodePointer createNodePointer(final NodePointer parent, final QName name,
102            final Object bean)
103    {
104        if (bean instanceof NodeWrapper)
105        {
106            final NodeWrapper<?> wrapper = (NodeWrapper<?>) bean;
107            return new ConfigurationNodePointer((ConfigurationNodePointer) parent,
108                    wrapper.getNode(), wrapper.getNodeHandler());
109        }
110        return null;
111    }
112
113    /**
114     * Creates a node wrapper for the specified node and its handler. This
115     * wrapper has to be passed to the JXPath context instead of the original
116     * node.
117     *
118     * @param <T> the type of the node
119     * @param node the node
120     * @param handler the corresponding node handler
121     * @return a wrapper for this node
122     */
123    public static <T> Object wrapNode(final T node, final NodeHandler<T> handler)
124    {
125        return new NodeWrapper<>(node, handler);
126    }
127
128    /**
129     * An internally used wrapper class that holds all information for
130     * processing a query for a specific node.
131     *
132     * @param <T> the type of the nodes this class deals with
133     */
134    static class NodeWrapper<T>
135    {
136        /** Stores the node. */
137        private final T node;
138
139        /** Stores the corresponding node handler. */
140        private final NodeHandler<T> nodeHandler;
141
142        /**
143         * Creates a new instance of {@code NodeWrapper} and initializes it.
144         *
145         * @param nd the node
146         * @param handler the node handler
147         */
148        public NodeWrapper(final T nd, final NodeHandler<T> handler)
149        {
150            node = nd;
151            nodeHandler = handler;
152        }
153
154        /**
155         * Returns the wrapped node.
156         *
157         * @return the node
158         */
159        public T getNode()
160        {
161            return node;
162        }
163
164        /**
165         * Returns the node handler for the wrapped node.
166         *
167         * @return the node handler
168         */
169        public NodeHandler<T> getNodeHandler()
170        {
171            return nodeHandler;
172        }
173    }
174}