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: */
017: package org.apache.cocoon.components.modules.input;
018:
019: import java.util.Iterator;
020: import java.util.Map;
021:
022: import org.apache.avalon.framework.configuration.Configuration;
023: import org.apache.avalon.framework.configuration.ConfigurationException;
024: import org.apache.avalon.framework.thread.ThreadSafe;
025:
026: /**
027: * JXPathModule allows to access properties of any object in generic
028: * way. JXPath provides APIs for the traversal of graphs of
029: * JavaBeans, DOM and other types of objects using the XPath
030: * syntax.
031: *
032: * <p><strong>Note:</strong> This is based on the AbstractJXPathModule
033: * and duplicates the code since multiple inheritance is not possible.
034: * Please keep both classes in sync.</p>
035: *
036: * <p>Configuration example:</p>
037: * <table>
038: * <tr>
039: * <td><code><lenient>false</lenient></td>
040: * <td>When set to true, non-existing attributes return null, when set to false,
041: * an exception is thrown. Default is true.</td>
042: * </tr>
043: * <tr>
044: * <td><code><parameter>false</parameter></td>
045: * <td>Attribute name to be used instead of passed attribute name.</td>
046: * </tr>
047: * <tr>
048: * <td><code><from-parameter>false</from-parameter></td>
049: * <td>Attribute name to pass to configured input module</td>
050: * </tr>
051: * <tr>
052: * <td><code><input-module name="request-attr"/></td>
053: * <td>Uses the "request-attr" input module to obtain a value and
054: * applies the given JXPath expression to it.</td>
055: * </tr>
056: * <tr>
057: * <td><code><function name="java.lang.String" prefix="str"/></td>
058: * <td>Imports the class "String" as extension class to the JXPathContext using
059: * the prefix "str". Thus "str:length(xpath)" would apply the method "length" to
060: * the string object obtained from the xpath expression. Please note that the class
061: * needs to be fully qualified.</td>
062: * </tr>
063: * <tr>
064: * <td><code><package name="java.util" prefix="util"/></td>
065: * <td>Imports all classes in the package "java.util" as extension classes to the
066: * JXPathContext using the prefix "util". Thus "util:Date.new()" would create a
067: * new java.util.Date object.</td>
068: * </tr>
069: * <tr>
070: * <td><code><namespace uri="uri:foo" prefix="bar"/></td>
071: * <td>Registers the namespace identified by URI <code>uri:foo</code>
072: * with the JXPathContext using the prefix <code>bar</code>. Thus
073: * expressions can query XML with nodes in this namespace using
074: * registered prefix.</td>
075: * </tr>
076: * </table>
077: *
078: * <p>In addition, it accepts the attributes "parameter" to override
079: * the attribute name and "from-parameter" to pass as attribute name
080: * to the configured input module.</p>
081: *
082: * @author <a href="mailto:kpiroumian@apache.org">Konstantin Piroumian</a>
083: * @author <a href="mailto:haul@apache.org">Christian Haul</a>
084: * @author <a href="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
085: * @version $Id: JXPathMetaModule.java 433543 2006-08-22 06:22:54Z crossley $
086: */
087: public class JXPathMetaModule extends AbstractMetaModule implements
088: ThreadSafe {
089:
090: /**
091: * Contains all globally registered extension classes and
092: * packages. Thus the lookup and loading of globally registered
093: * extensions is done only once.
094: */
095: protected JXPathHelperConfiguration configuration;
096:
097: /**
098: * Overrides attribute name
099: */
100: protected String parameter = "";
101:
102: public JXPathMetaModule() {
103: // this value has a default in the super class
104: super .defaultInput = "request-attr";
105: }
106:
107: /**
108: * Configure component. Preprocess list of packages and functions
109: * to add to JXPath context later.
110: *
111: * @param config a <code>Configuration</code> value
112: * @exception ConfigurationException if an error occurs
113: */
114: public void configure(Configuration config)
115: throws ConfigurationException {
116:
117: this .inputConf = config.getChild("input-module");
118: this .defaultInput = this .inputConf.getAttribute("name",
119: this .defaultInput);
120: this .parameter = config.getChild("parameter").getValue(
121: this .parameter);
122:
123: this .configuration = JXPathHelper.setup(config);
124: }
125:
126: public Object getAttribute(String name, Configuration modeConf,
127: Map objectModel) throws ConfigurationException {
128:
129: Object contextObj = getContextObject(modeConf, objectModel);
130: if (modeConf != null) {
131: name = modeConf.getChild("parameter").getValue(
132: !this .parameter.equals("") ? this .parameter : name);
133: }
134: return JXPathHelper.getAttribute(name, modeConf,
135: this .configuration, contextObj);
136: }
137:
138: public Iterator getAttributeNames(Configuration modeConf,
139: Map objectModel) throws ConfigurationException {
140:
141: Object contextObj = getContextObject(modeConf, objectModel);
142: return JXPathHelper.getAttributeNames(this .configuration,
143: contextObj);
144: }
145:
146: public Object[] getAttributeValues(String name,
147: Configuration modeConf, Map objectModel)
148: throws ConfigurationException {
149:
150: Object contextObj = getContextObject(modeConf, objectModel);
151: if (modeConf != null) {
152: name = modeConf.getChild("parameter").getValue(
153: !this .parameter.equals("") ? this .parameter : name);
154: }
155: return JXPathHelper.getAttributeValues(name, modeConf,
156: this .configuration, contextObj);
157: }
158:
159: /**
160: * Looks up object from configured InputModule.
161: *
162: * @param modeConf a <code>Configuration</code> value
163: * @param objectModel a <code>Map</code> value
164: * @return an <code>Object</code> value
165: */
166: protected Object getContextObject(Configuration modeConf,
167: Map objectModel) throws ConfigurationException {
168:
169: if (!this .initialized) {
170: lazy_initialize();
171: }
172:
173: Configuration mConf = null;
174: String inputName = null;
175: String parameter = this .parameter;
176: if (modeConf != null) {
177: mConf = modeConf.getChild("input-module");
178: inputName = mConf.getAttribute("name", null);
179: parameter = modeConf.getChild("from-parameter").getValue(
180: parameter);
181: }
182:
183: if (getLogger().isDebugEnabled()) {
184: getLogger().debug(
185: "modeConf is " + modeConf + " this.inputConf is "
186: + this .inputConf + " mConf is " + mConf
187: + " this.input is " + this .input
188: + " this.defaultInput is "
189: + this .defaultInput + " inputName is "
190: + inputName + " parameter is " + parameter);
191: }
192:
193: Object obj = getValue(parameter, objectModel, this .input,
194: this .defaultInput, this .inputConf, null, inputName,
195: mConf);
196:
197: if (getLogger().isDebugEnabled()) {
198: getLogger().debug(
199: "returning an "
200: + (obj == null ? "null" : obj.getClass()
201: .getName()) + " as " + obj);
202: }
203:
204: return obj;
205: }
206: }
|