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}