/******************************************************************************* | |
* Copyright (c) 2007 Standards for Technology in Automotive Retail | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* David Carver - STAR - bug 224197 - initial API and implementation | |
* based on work from Apache Xalan 2.7.0 | |
*******************************************************************************/ | |
/* | |
* Copyright 1999-2004 The Apache Software Foundation. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
/* | |
* $Id: ExtensionHandlerJavaPackage.java,v 1.3 2008/03/28 02:38:17 dacarver Exp $ | |
*/ | |
package org.eclipse.wst.xsl.core.internal.compiler.xslt10.extensions; | |
import java.io.IOException; | |
import java.lang.reflect.Constructor; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Modifier; | |
import java.util.Vector; | |
import javax.xml.transform.TransformerException; | |
import org.eclipse.wst.xsl.core.compiler.xslt10.res.Messages; | |
import org.eclipse.wst.xsl.core.compiler.xslt10.res.XSLTErrorResources; | |
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.templates.ElemTemplateElement; | |
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.templates.Stylesheet; | |
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.trace.ExtensionEvent; | |
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.transformer.TransformerImpl; | |
import org.apache.xpath.functions.FuncExtFunction; | |
import org.apache.xpath.objects.XObject; | |
/** | |
* Represents an extension namespace for XPath that handles java packages that | |
* may be fully or partially specified. It is recommended that the class URI be | |
* of one of the following forms: | |
* | |
* <pre> | |
* xalan://partial.class.name | |
* xalan:// | |
* http://xml.apache.org/xalan/java (which is the same as xalan://) | |
* </pre> | |
* | |
* However, we do not enforce this. If the class name contains a a /, we only | |
* use the part to the right of the rightmost slash. In addition, we ignore any | |
* "class:" prefix. Provides functions to test a function's existence and call a | |
* function. Also provides functions to test an element's existence and call an | |
* element. | |
* | |
* @author <a href="mailto:garyp@firstech.com">Gary L Peskin</a> | |
* | |
* @xsl.usage internal | |
*/ | |
public class ExtensionHandlerJavaPackage extends ExtensionHandlerJava { | |
/** | |
* Construct a new extension namespace handler given all the information | |
* needed. | |
* | |
* @param namespaceUri | |
* the extension namespace URI that I'm implementing | |
* @param scriptLang | |
* language of code implementing the extension | |
* @param className | |
* the beginning of the class name of the class. This should be | |
* followed by a dot (.) | |
*/ | |
public ExtensionHandlerJavaPackage(String namespaceUri, String scriptLang, | |
String className) { | |
super(namespaceUri, scriptLang, className); | |
} | |
/** | |
* Tests whether a certain function name is known within this namespace. | |
* Since this is for a package, we concatenate the package name used when | |
* this handler was created and the function name specified in the argument. | |
* There is no information regarding the arguments to the function call or | |
* whether the method implementing the function is a static method or an | |
* instance method. | |
* | |
* @param function | |
* name of the function being tested | |
* @return true if its known, false if not. | |
*/ | |
@Override | |
public boolean isFunctionAvailable(String function) { | |
try { | |
String fullName = m_className + function; | |
int lastDot = fullName.lastIndexOf("."); | |
if (lastDot >= 0) { | |
Class myClass = getClassForName(fullName.substring(0, lastDot)); | |
Method[] methods = myClass.getMethods(); | |
int nMethods = methods.length; | |
function = fullName.substring(lastDot + 1); | |
for (int i = 0; i < nMethods; i++) { | |
if (methods[i].getName().equals(function)) | |
return true; | |
} | |
} | |
} catch (ClassNotFoundException cnfe) { | |
} | |
return false; | |
} | |
/** | |
* Tests whether a certain element name is known within this namespace. | |
* Looks for a method with the appropriate name and signature. This method | |
* examines both static and instance methods. | |
* | |
* @param element | |
* name of the element being tested | |
* @return true if its known, false if not. | |
*/ | |
@Override | |
public boolean isElementAvailable(String element) { | |
try { | |
String fullName = m_className + element; | |
int lastDot = fullName.lastIndexOf("."); | |
if (lastDot >= 0) { | |
Class myClass = getClassForName(fullName.substring(0, lastDot)); | |
Method[] methods = myClass.getMethods(); | |
int nMethods = methods.length; | |
element = fullName.substring(lastDot + 1); | |
for (int i = 0; i < nMethods; i++) { | |
if (methods[i].getName().equals(element)) { | |
Class[] paramTypes = methods[i].getParameterTypes(); | |
if ((paramTypes.length == 2) | |
&& paramTypes[0] | |
.isAssignableFrom(org.apache.xalan.extensions.XSLProcessorContext.class) | |
&& paramTypes[1] | |
.isAssignableFrom(org.eclipse.wst.xsl.core.internal.compiler.xslt10.templates.ElemExtensionCall.class)) { | |
return true; | |
} | |
} | |
} | |
} | |
} catch (ClassNotFoundException cnfe) { | |
} | |
return false; | |
} | |
/** | |
* Process a call to a function in the package java namespace. There are | |
* three possible types of calls: | |
* | |
* <pre> | |
* Constructor: | |
* packagens:class.name.new(arg1, arg2, ...) | |
* | |
* Static method: | |
* packagens:class.name.method(arg1, arg2, ...) | |
* | |
* Instance method: | |
* packagens:method(obj, arg1, arg2, ...) | |
* </pre> | |
* | |
* We use the following rules to determine the type of call made: | |
* <ol type="1"> | |
* <li>If the function name ends with a ".new", call the best constructor | |
* for class whose name is formed by concatenating the value specified on | |
* the namespace with the value specified in the function invocation before | |
* ".new".</li> | |
* <li>If the function name contains a period, call the best static method | |
* "method" in the class whose name is formed by concatenating the value | |
* specified on the namespace with the value specified in the function | |
* invocation.</li> | |
* <li>Otherwise, call the best instance method "method" in the class whose | |
* name is formed by concatenating the value specified on the namespace with | |
* the value specified in the function invocation. Note that a static method | |
* of the same name will <i>not</i> be called in the current | |
* implementation. This module does not verify that the obj argument is a | |
* member of the package namespace.</li> | |
* </ol> | |
* | |
* @param funcName | |
* Function name. | |
* @param args | |
* The arguments of the function call. | |
* @param methodKey | |
* A key that uniquely identifies this class and method call. | |
* @param exprContext | |
* The context in which this expression is being executed. | |
* @return the return value of the function evaluation. | |
* | |
* @throws TransformerException | |
* if parsing trouble | |
*/ | |
@Override | |
public Object callFunction(String funcName, Vector args, Object methodKey, | |
ExpressionContext exprContext) throws TransformerException { | |
String className; | |
String methodName; | |
Class classObj; | |
Object targetObject; | |
int lastDot = funcName.lastIndexOf("."); | |
Object[] methodArgs; | |
Object[][] convertedArgs; | |
Class[] paramTypes; | |
try { | |
TransformerImpl trans = (exprContext != null) ? (TransformerImpl) exprContext | |
.getXPathContext().getOwnerObject() | |
: null; | |
if (funcName.endsWith(".new")) { // Handle constructor call | |
methodArgs = new Object[args.size()]; | |
convertedArgs = new Object[1][]; | |
for (int i = 0; i < methodArgs.length; i++) { | |
methodArgs[i] = args.elementAt(i); | |
} | |
Constructor c = (methodKey != null) ? (Constructor) getFromCache( | |
methodKey, null, methodArgs) | |
: null; | |
if (c != null) { | |
try { | |
paramTypes = c.getParameterTypes(); | |
MethodResolver.convertParams(methodArgs, convertedArgs, | |
paramTypes, exprContext); | |
return c.newInstance(convertedArgs[0]); | |
} catch (InvocationTargetException ite) { | |
throw ite; | |
} catch (Exception e) { | |
// Must not have been the right one | |
} | |
} | |
className = m_className + funcName.substring(0, lastDot); | |
try { | |
classObj = getClassForName(className); | |
} catch (ClassNotFoundException e) { | |
throw new TransformerException(e); | |
} | |
c = MethodResolver.getConstructor(classObj, methodArgs, | |
convertedArgs, exprContext); | |
if (methodKey != null) | |
putToCache(methodKey, null, methodArgs, c); | |
if (trans != null && trans.getDebug()) { | |
trans.getTraceManager().fireExtensionEvent( | |
new ExtensionEvent(trans, c, convertedArgs[0])); | |
Object result; | |
try { | |
result = c.newInstance(convertedArgs[0]); | |
} catch (Exception e) { | |
throw e; | |
} finally { | |
trans.getTraceManager().fireExtensionEndEvent( | |
new ExtensionEvent(trans, c, convertedArgs[0])); | |
} | |
return result; | |
} else | |
return c.newInstance(convertedArgs[0]); | |
} | |
else if (-1 != lastDot) { // Handle static method call | |
methodArgs = new Object[args.size()]; | |
convertedArgs = new Object[1][]; | |
for (int i = 0; i < methodArgs.length; i++) { | |
methodArgs[i] = args.elementAt(i); | |
} | |
Method m = (methodKey != null) ? (Method) getFromCache( | |
methodKey, null, methodArgs) : null; | |
if (m != null && !trans.getDebug()) { | |
try { | |
paramTypes = m.getParameterTypes(); | |
MethodResolver.convertParams(methodArgs, convertedArgs, | |
paramTypes, exprContext); | |
return m.invoke(null, convertedArgs[0]); | |
} catch (InvocationTargetException ite) { | |
throw ite; | |
} catch (Exception e) { | |
// Must not have been the right one | |
} | |
} | |
className = m_className + funcName.substring(0, lastDot); | |
methodName = funcName.substring(lastDot + 1); | |
try { | |
classObj = getClassForName(className); | |
} catch (ClassNotFoundException e) { | |
throw new TransformerException(e); | |
} | |
m = MethodResolver.getMethod(classObj, methodName, methodArgs, | |
convertedArgs, exprContext, MethodResolver.STATIC_ONLY); | |
if (methodKey != null) | |
putToCache(methodKey, null, methodArgs, m); | |
if (trans != null && trans.getDebug()) { | |
trans.getTraceManager().fireExtensionEvent(m, null, | |
convertedArgs[0]); | |
Object result; | |
try { | |
result = m.invoke(null, convertedArgs[0]); | |
} catch (Exception e) { | |
throw e; | |
} finally { | |
trans.getTraceManager().fireExtensionEndEvent(m, null, | |
convertedArgs[0]); | |
} | |
return result; | |
} else | |
return m.invoke(null, convertedArgs[0]); | |
} | |
else { // Handle instance method call | |
if (args.size() < 1) { | |
throw new TransformerException(Messages.createMessage( | |
XSLTErrorResources.ER_INSTANCE_MTHD_CALL_REQUIRES, | |
new Object[] { funcName })); // "Instance method | |
// call to method " | |
// + funcName | |
// + " requires an Object instance as first argument"); | |
} | |
targetObject = args.elementAt(0); | |
if (targetObject instanceof XObject) // Next level down for | |
// XObjects | |
targetObject = ((XObject) targetObject).object(); | |
methodArgs = new Object[args.size() - 1]; | |
convertedArgs = new Object[1][]; | |
for (int i = 0; i < methodArgs.length; i++) { | |
methodArgs[i] = args.elementAt(i + 1); | |
} | |
Method m = (methodKey != null) ? (Method) getFromCache( | |
methodKey, targetObject, methodArgs) : null; | |
if (m != null) { | |
try { | |
paramTypes = m.getParameterTypes(); | |
MethodResolver.convertParams(methodArgs, convertedArgs, | |
paramTypes, exprContext); | |
return m.invoke(targetObject, convertedArgs[0]); | |
} catch (InvocationTargetException ite) { | |
throw ite; | |
} catch (Exception e) { | |
// Must not have been the right one | |
} | |
} | |
classObj = targetObject.getClass(); | |
m = MethodResolver.getMethod(classObj, funcName, methodArgs, | |
convertedArgs, exprContext, | |
MethodResolver.INSTANCE_ONLY); | |
if (methodKey != null) | |
putToCache(methodKey, targetObject, methodArgs, m); | |
if (trans != null && trans.getDebug()) { | |
trans.getTraceManager().fireExtensionEvent(m, targetObject, | |
convertedArgs[0]); | |
Object result; | |
try { | |
result = m.invoke(targetObject, convertedArgs[0]); | |
} catch (Exception e) { | |
throw e; | |
} finally { | |
trans.getTraceManager().fireExtensionEndEvent(m, | |
targetObject, convertedArgs[0]); | |
} | |
return result; | |
} else | |
return m.invoke(targetObject, convertedArgs[0]); | |
} | |
} catch (InvocationTargetException ite) { | |
Throwable resultException = ite; | |
Throwable targetException = ite.getTargetException(); | |
if (targetException instanceof TransformerException) | |
throw ((TransformerException) targetException); | |
else if (targetException != null) | |
resultException = targetException; | |
throw new TransformerException(resultException); | |
} catch (Exception e) { | |
// e.printStackTrace(); | |
throw new TransformerException(e); | |
} | |
} | |
/** | |
* Process a call to an XPath extension function | |
* | |
* @param extFunction | |
* The XPath extension function | |
* @param args | |
* The arguments of the function call. | |
* @param exprContext | |
* The context in which this expression is being executed. | |
* @return the return value of the function evaluation. | |
* @throws TransformerException | |
*/ | |
@Override | |
public Object callFunction(FuncExtFunction extFunction, Vector args, | |
ExpressionContext exprContext) throws TransformerException { | |
return callFunction(extFunction.getFunctionName(), args, extFunction | |
.getMethodKey(), exprContext); | |
} | |
/** | |
* Process a call to this extension namespace via an element. As a side | |
* effect, the results are sent to the TransformerImpl's result tree. For | |
* this namespace, only static element methods are currently supported. If | |
* instance methods are needed, please let us know your requirements. | |
* | |
* @param localPart | |
* Element name's local part. | |
* @param element | |
* The extension element being processed. | |
* @param transformer | |
* Handle to TransformerImpl. | |
* @param stylesheetTree | |
* The compiled stylesheet tree. | |
* @param methodKey | |
* A key that uniquely identifies this element call. | |
* @throws IOException | |
* if loading trouble | |
* @throws TransformerException | |
* if parsing trouble | |
*/ | |
@Override | |
public void processElement(String localPart, ElemTemplateElement element, | |
TransformerImpl transformer, Stylesheet stylesheetTree, | |
Object methodKey) throws TransformerException, IOException { | |
Object result = null; | |
Class classObj; | |
Method m = (Method) getFromCache(methodKey, null, null); | |
if (null == m) { | |
try { | |
String fullName = m_className + localPart; | |
int lastDot = fullName.lastIndexOf("."); | |
if (lastDot < 0) | |
throw new TransformerException(Messages.createMessage( | |
XSLTErrorResources.ER_INVALID_ELEMENT_NAME, | |
new Object[] { fullName })); // "Invalid element | |
// name specified " | |
// + fullName); | |
try { | |
classObj = getClassForName(fullName.substring(0, lastDot)); | |
} catch (ClassNotFoundException e) { | |
throw new TransformerException(e); | |
} | |
localPart = fullName.substring(lastDot + 1); | |
m = MethodResolver.getElementMethod(classObj, localPart); | |
if (!Modifier.isStatic(m.getModifiers())) | |
throw new TransformerException(Messages.createMessage( | |
XSLTErrorResources.ER_ELEMENT_NAME_METHOD_STATIC, | |
new Object[] { fullName })); // "Element name | |
// method must be | |
// static " + | |
// fullName); | |
} catch (Exception e) { | |
// e.printStackTrace (); | |
throw new TransformerException(e); | |
} | |
putToCache(methodKey, null, null, m); | |
} | |
XSLProcessorContext xpc = new XSLProcessorContext(transformer, | |
stylesheetTree); | |
try { | |
if (transformer.getDebug()) { | |
transformer.getTraceManager().fireExtensionEvent(m, null, | |
new Object[] { xpc, element }); | |
try { | |
result = m.invoke(null, new Object[] { xpc, element }); | |
} catch (Exception e) { | |
throw e; | |
} finally { | |
transformer.getTraceManager().fireExtensionEndEvent(m, | |
null, new Object[] { xpc, element }); | |
} | |
} else | |
result = m.invoke(null, new Object[] { xpc, element }); | |
} catch (InvocationTargetException ite) { | |
Throwable resultException = ite; | |
Throwable targetException = ite.getTargetException(); | |
if (targetException instanceof TransformerException) | |
throw ((TransformerException) targetException); | |
else if (targetException != null) | |
resultException = targetException; | |
throw new TransformerException(resultException); | |
} catch (Exception e) { | |
// e.printStackTrace (); | |
throw new TransformerException(e); | |
} | |
if (result != null) { | |
xpc.outputToResultTree(stylesheetTree, result); | |
} | |
} | |
} |