    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}