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 *     https://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 * Implements the {@code NodePointerFactory} interface for configuration nodes.
029 * </p>
030 * <p>
031 * This class is able to create {@code NodePointer}s for the nodes of hierarchical configurations. Because there is no
032 * common base class for configuration nodes (any specific configuration implementation can use its own node class) a
033 * trick is needed for activating this factory for a concrete JXPath query: The {@code wrapNode()} method has to be
034 * called with the node object and its corresponding {@code NodeHandler}. This creates a wrapper object containing all
035 * information required by the factory for processing a query. Then this wrapper object has to be passed to the query
036 * methods of the JXPath context.
037 * </p>
038 *
039 * @since 1.3
040 */
041public class ConfigurationNodePointerFactory implements NodePointerFactory {
042
043    /**
044     * An internally used wrapper class that holds all information for processing a query for a specific node.
045     *
046     * @param <T> the type of the nodes this class deals with
047     */
048    static class NodeWrapper<T> {
049
050        /** Stores the node. */
051        private final T node;
052
053        /** Stores the corresponding node handler. */
054        private final NodeHandler<T> nodeHandler;
055
056        /**
057         * Creates a new instance of {@code NodeWrapper} and initializes it.
058         *
059         * @param nd the node
060         * @param handler the node handler
061         */
062        public NodeWrapper(final T nd, final NodeHandler<T> handler) {
063            node = nd;
064            nodeHandler = handler;
065        }
066
067        /**
068         * Gets the wrapped node.
069         *
070         * @return the node
071         */
072        public T getNode() {
073            return node;
074        }
075
076        /**
077         * Gets the node handler for the wrapped node.
078         *
079         * @return the node handler
080         */
081        public NodeHandler<T> getNodeHandler() {
082            return nodeHandler;
083        }
084    }
085
086    /** Constant for the order of this factory. */
087    public static final int CONFIGURATION_NODE_POINTER_FACTORY_ORDER = 200;
088
089    /**
090     * Creates a node wrapper for the specified node and its handler. This wrapper has to be passed to the JXPath context
091     * instead of the original node.
092     *
093     * @param <T> the type of the node
094     * @param node the node
095     * @param handler the corresponding node handler
096     * @return a wrapper for this node
097     */
098    public static <T> Object wrapNode(final T node, final NodeHandler<T> handler) {
099        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}