001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: ExtensionHandlerJavaPackage.java 1225574 2011-12-29 15:49:16Z mrglavas $
020 */
021 package org.apache.xalan.extensions;
022
023 import java.io.IOException;
024 import java.lang.reflect.Constructor;
025 import java.lang.reflect.InvocationTargetException;
026 import java.lang.reflect.Method;
027 import java.lang.reflect.Modifier;
028 import java.util.Vector;
029
030 import javax.xml.transform.TransformerException;
031
032 import org.apache.xalan.res.XSLMessages;
033 import org.apache.xalan.res.XSLTErrorResources;
034 import org.apache.xalan.templates.ElemTemplateElement;
035 import org.apache.xalan.templates.Stylesheet;
036 import org.apache.xalan.trace.ExtensionEvent;
037 import org.apache.xalan.transformer.TransformerImpl;
038 import org.apache.xpath.functions.FuncExtFunction;
039 import org.apache.xpath.objects.XObject;
040
041 /**
042 * Represents an extension namespace for XPath that handles java packages
043 * that may be fully or partially specified.
044 * It is recommended that the class URI be of one of the following forms:
045 * <pre>
046 * xalan://partial.class.name
047 * xalan://
048 * http://xml.apache.org/xalan/java (which is the same as xalan://)
049 * </pre>
050 * However, we do not enforce this. If the class name contains a
051 * a /, we only use the part to the right of the rightmost slash.
052 * In addition, we ignore any "class:" prefix.
053 * Provides functions to test a function's existence and call a function.
054 * Also provides functions to test an element's existence and call an
055 * element.
056 *
057 * @author <a href="mailto:garyp@firstech.com">Gary L Peskin</a>
058 *
059 * @xsl.usage internal
060 */
061
062
063 public class ExtensionHandlerJavaPackage extends ExtensionHandlerJava
064 {
065
066 /**
067 * Construct a new extension namespace handler given all the information
068 * needed.
069 *
070 * @param namespaceUri the extension namespace URI that I'm implementing
071 * @param scriptLang language of code implementing the extension
072 * @param className the beginning of the class name of the class. This
073 * should be followed by a dot (.)
074 */
075 public ExtensionHandlerJavaPackage(String namespaceUri,
076 String scriptLang,
077 String className)
078 {
079 super(namespaceUri, scriptLang, className);
080 }
081
082
083 /**
084 * Tests whether a certain function name is known within this namespace.
085 * Since this is for a package, we concatenate the package name used when
086 * this handler was created and the function name specified in the argument.
087 * There is
088 * no information regarding the arguments to the function call or
089 * whether the method implementing the function is a static method or
090 * an instance method.
091 * @param function name of the function being tested
092 * @return true if its known, false if not.
093 */
094
095 public boolean isFunctionAvailable(String function)
096 {
097 try
098 {
099 String fullName = m_className + function;
100 int lastDot = fullName.lastIndexOf('.');
101 if (lastDot >= 0)
102 {
103 Class myClass = getClassForName(fullName.substring(0, lastDot));
104 Method[] methods = myClass.getMethods();
105 int nMethods = methods.length;
106 function = fullName.substring(lastDot + 1);
107 for (int i = 0; i < nMethods; i++)
108 {
109 if (methods[i].getName().equals(function))
110 return true;
111 }
112 }
113 }
114 catch (ClassNotFoundException cnfe) {}
115
116 return false;
117 }
118
119
120 /**
121 * Tests whether a certain element name is known within this namespace.
122 * Looks for a method with the appropriate name and signature.
123 * This method examines both static and instance methods.
124 * @param element name of the element being tested
125 * @return true if its known, false if not.
126 */
127
128 public boolean isElementAvailable(String element)
129 {
130 try
131 {
132 String fullName = m_className + element;
133 int lastDot = fullName.lastIndexOf('.');
134 if (lastDot >= 0)
135 {
136 Class myClass = getClassForName(fullName.substring(0, lastDot));
137 Method[] methods = myClass.getMethods();
138 int nMethods = methods.length;
139 element = fullName.substring(lastDot + 1);
140 for (int i = 0; i < nMethods; i++)
141 {
142 if (methods[i].getName().equals(element))
143 {
144 Class[] paramTypes = methods[i].getParameterTypes();
145 if ( (paramTypes.length == 2)
146 && paramTypes[0].isAssignableFrom(
147 org.apache.xalan.extensions.XSLProcessorContext.class)
148 && paramTypes[1].isAssignableFrom(
149 org.apache.xalan.templates.ElemExtensionCall.class) )
150 {
151 return true;
152 }
153 }
154 }
155 }
156 }
157 catch (ClassNotFoundException cnfe) {}
158
159 return false;
160 }
161
162
163 /**
164 * Process a call to a function in the package java namespace.
165 * There are three possible types of calls:
166 * <pre>
167 * Constructor:
168 * packagens:class.name.new(arg1, arg2, ...)
169 *
170 * Static method:
171 * packagens:class.name.method(arg1, arg2, ...)
172 *
173 * Instance method:
174 * packagens:method(obj, arg1, arg2, ...)
175 * </pre>
176 * We use the following rules to determine the type of call made:
177 * <ol type="1">
178 * <li>If the function name ends with a ".new", call the best constructor for
179 * class whose name is formed by concatenating the value specified on
180 * the namespace with the value specified in the function invocation
181 * before ".new".</li>
182 * <li>If the function name contains a period, call the best static method "method"
183 * in the class whose name is formed by concatenating the value specified on
184 * the namespace with the value specified in the function invocation.</li>
185 * <li>Otherwise, call the best instance method "method"
186 * in the class whose name is formed by concatenating the value specified on
187 * the namespace with the value specified in the function invocation.
188 * Note that a static method of the same
189 * name will <i>not</i> be called in the current implementation. This
190 * module does not verify that the obj argument is a member of the
191 * package namespace.</li>
192 * </ol>
193 *
194 * @param funcName Function name.
195 * @param args The arguments of the function call.
196 * @param methodKey A key that uniquely identifies this class and method call.
197 * @param exprContext The context in which this expression is being executed.
198 * @return the return value of the function evaluation.
199 *
200 * @throws TransformerException if parsing trouble
201 */
202
203 public Object callFunction (String funcName,
204 Vector args,
205 Object methodKey,
206 ExpressionContext exprContext)
207 throws TransformerException
208 {
209
210 String className;
211 String methodName;
212 Class classObj;
213 Object targetObject;
214 int lastDot = funcName.lastIndexOf('.');
215 Object[] methodArgs;
216 Object[][] convertedArgs;
217 Class[] paramTypes;
218
219 try
220 {
221 TransformerImpl trans = (exprContext != null) ?
222 (TransformerImpl)exprContext.getXPathContext().getOwnerObject() : null;
223 if (funcName.endsWith(".new")) { // Handle constructor call
224
225 methodArgs = new Object[args.size()];
226 convertedArgs = new Object[1][];
227 for (int i = 0; i < methodArgs.length; i++)
228 {
229 methodArgs[i] = args.get(i);
230 }
231
232 Constructor c = (methodKey != null) ?
233 (Constructor) getFromCache(methodKey, null, methodArgs) : null;
234
235 if (c != null)
236 {
237 try
238 {
239 paramTypes = c.getParameterTypes();
240 MethodResolver.convertParams(methodArgs, convertedArgs, paramTypes, exprContext);
241 return c.newInstance(convertedArgs[0]);
242 }
243 catch (InvocationTargetException ite)
244 {
245 throw ite;
246 }
247 catch(Exception e)
248 {
249 // Must not have been the right one
250 }
251 }
252 className = m_className + funcName.substring(0, lastDot);
253 try
254 {
255 classObj = getClassForName(className);
256 }
257 catch (ClassNotFoundException e)
258 {
259 throw new TransformerException(e);
260 }
261 c = MethodResolver.getConstructor(classObj,
262 methodArgs,
263 convertedArgs,
264 exprContext);
265 if (methodKey != null)
266 putToCache(methodKey, null, methodArgs, c);
267
268 if (trans != null && trans.getDebug()) {
269 trans.getTraceManager().fireExtensionEvent(new ExtensionEvent(trans, c, convertedArgs[0]));
270 Object result;
271 try {
272 result = c.newInstance(convertedArgs[0]);
273 } catch (Exception e) {
274 throw e;
275 } finally {
276 trans.getTraceManager().fireExtensionEndEvent(new ExtensionEvent(trans, c, convertedArgs[0]));
277 }
278 return result;
279 } else
280 return c.newInstance(convertedArgs[0]);
281 }
282
283 else if (-1 != lastDot) { // Handle static method call
284
285 methodArgs = new Object[args.size()];
286 convertedArgs = new Object[1][];
287 for (int i = 0; i < methodArgs.length; i++)
288 {
289 methodArgs[i] = args.get(i);
290 }
291 Method m = (methodKey != null) ?
292 (Method) getFromCache(methodKey, null, methodArgs) : null;
293
294 if (m != null && !trans.getDebug())
295 {
296 try
297 {
298 paramTypes = m.getParameterTypes();
299 MethodResolver.convertParams(methodArgs, convertedArgs, paramTypes, exprContext);
300 return m.invoke(null, convertedArgs[0]);
301 }
302 catch (InvocationTargetException ite)
303 {
304 throw ite;
305 }
306 catch(Exception e)
307 {
308 // Must not have been the right one
309 }
310 }
311 className = m_className + funcName.substring(0, lastDot);
312 methodName = funcName.substring(lastDot + 1);
313 try
314 {
315 classObj = getClassForName(className);
316 }
317 catch (ClassNotFoundException e)
318 {
319 throw new TransformerException(e);
320 }
321 m = MethodResolver.getMethod(classObj,
322 methodName,
323 methodArgs,
324 convertedArgs,
325 exprContext,
326 MethodResolver.STATIC_ONLY);
327 if (methodKey != null)
328 putToCache(methodKey, null, methodArgs, m);
329
330 if (trans != null && trans.getDebug()) {
331 trans.getTraceManager().fireExtensionEvent(m, null, convertedArgs[0]);
332 Object result;
333 try {
334 result = m.invoke(null, convertedArgs[0]);
335 } catch (Exception e) {
336 throw e;
337 } finally {
338 trans.getTraceManager().fireExtensionEndEvent(m, null, convertedArgs[0]);
339 }
340 return result;
341 }
342 else
343 return m.invoke(null, convertedArgs[0]);
344 }
345
346 else { // Handle instance method call
347
348 if (args.size() < 1)
349 {
350 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_INSTANCE_MTHD_CALL_REQUIRES, new Object[]{funcName })); //"Instance method call to method " + funcName
351 //+ " requires an Object instance as first argument");
352 }
353 targetObject = args.get(0);
354 if (targetObject instanceof XObject) // Next level down for XObjects
355 targetObject = ((XObject) targetObject).object();
356 methodArgs = new Object[args.size() - 1];
357 convertedArgs = new Object[1][];
358 for (int i = 0; i < methodArgs.length; i++)
359 {
360 methodArgs[i] = args.get(i+1);
361 }
362 Method m = (methodKey != null) ?
363 (Method) getFromCache(methodKey, targetObject, methodArgs) : null;
364
365 if (m != null)
366 {
367 try
368 {
369 paramTypes = m.getParameterTypes();
370 MethodResolver.convertParams(methodArgs, convertedArgs, paramTypes, exprContext);
371 return m.invoke(targetObject, convertedArgs[0]);
372 }
373 catch (InvocationTargetException ite)
374 {
375 throw ite;
376 }
377 catch(Exception e)
378 {
379 // Must not have been the right one
380 }
381 }
382 classObj = targetObject.getClass();
383 m = MethodResolver.getMethod(classObj,
384 funcName,
385 methodArgs,
386 convertedArgs,
387 exprContext,
388 MethodResolver.INSTANCE_ONLY);
389 if (methodKey != null)
390 putToCache(methodKey, targetObject, methodArgs, m);
391
392 if (trans != null && trans.getDebug()) {
393 trans.getTraceManager().fireExtensionEvent(m, targetObject, convertedArgs[0]);
394 Object result;
395 try {
396 result = m.invoke(targetObject, convertedArgs[0]);
397 } catch (Exception e) {
398 throw e;
399 } finally {
400 trans.getTraceManager().fireExtensionEndEvent(m, targetObject, convertedArgs[0]);
401 }
402 return result;
403 } else
404 return m.invoke(targetObject, convertedArgs[0]);
405 }
406 }
407 catch (InvocationTargetException ite)
408 {
409 Throwable resultException = ite;
410 Throwable targetException = ite.getTargetException();
411
412 if (targetException instanceof TransformerException)
413 throw ((TransformerException)targetException);
414 else if (targetException != null)
415 resultException = targetException;
416
417 throw new TransformerException(resultException);
418 }
419 catch (Exception e)
420 {
421 // e.printStackTrace();
422 throw new TransformerException(e);
423 }
424 }
425
426 /**
427 * Process a call to an XPath extension function
428 *
429 * @param extFunction The XPath extension function
430 * @param args The arguments of the function call.
431 * @param exprContext The context in which this expression is being executed.
432 * @return the return value of the function evaluation.
433 * @throws TransformerException
434 */
435 public Object callFunction(FuncExtFunction extFunction,
436 Vector args,
437 ExpressionContext exprContext)
438 throws TransformerException
439 {
440 return callFunction(extFunction.getFunctionName(), args,
441 extFunction.getMethodKey(), exprContext);
442 }
443
444 /**
445 * Process a call to this extension namespace via an element. As a side
446 * effect, the results are sent to the TransformerImpl's result tree.
447 * For this namespace, only static element methods are currently supported.
448 * If instance methods are needed, please let us know your requirements.
449 * @param localPart Element name's local part.
450 * @param element The extension element being processed.
451 * @param transformer Handle to TransformerImpl.
452 * @param stylesheetTree The compiled stylesheet tree.
453 * @param methodKey A key that uniquely identifies this element call.
454 * @throws IOException if loading trouble
455 * @throws TransformerException if parsing trouble
456 */
457
458 public void processElement (String localPart,
459 ElemTemplateElement element,
460 TransformerImpl transformer,
461 Stylesheet stylesheetTree,
462 Object methodKey)
463 throws TransformerException, IOException
464 {
465 Object result = null;
466 Class classObj;
467
468 Method m = (Method) getFromCache(methodKey, null, null);
469 if (null == m)
470 {
471 try
472 {
473 String fullName = m_className + localPart;
474 int lastDot = fullName.lastIndexOf('.');
475 if (lastDot < 0)
476 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_ELEMENT_NAME, new Object[]{fullName })); //"Invalid element name specified " + fullName);
477 try
478 {
479 classObj = getClassForName(fullName.substring(0, lastDot));
480 }
481 catch (ClassNotFoundException e)
482 {
483 throw new TransformerException(e);
484 }
485 localPart = fullName.substring(lastDot + 1);
486 m = MethodResolver.getElementMethod(classObj, localPart);
487 if (!Modifier.isStatic(m.getModifiers()))
488 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_ELEMENT_NAME_METHOD_STATIC, new Object[]{fullName })); //"Element name method must be static " + fullName);
489 }
490 catch (Exception e)
491 {
492 // e.printStackTrace ();
493 throw new TransformerException (e);
494 }
495 putToCache(methodKey, null, null, m);
496 }
497
498 XSLProcessorContext xpc = new XSLProcessorContext(transformer,
499 stylesheetTree);
500
501 try
502 {
503 if (transformer.getDebug()) {
504 transformer.getTraceManager().fireExtensionEvent(m, null, new Object[] {xpc, element});
505 try {
506 result = m.invoke(null, new Object[] {xpc, element});
507 } catch (Exception e) {
508 throw e;
509 } finally {
510 transformer.getTraceManager().fireExtensionEndEvent(m, null, new Object[] {xpc, element});
511 }
512 } else
513 result = m.invoke(null, new Object[] {xpc, element});
514 }
515 catch (InvocationTargetException ite)
516 {
517 Throwable resultException = ite;
518 Throwable targetException = ite.getTargetException();
519
520 if (targetException instanceof TransformerException)
521 throw ((TransformerException)targetException);
522 else if (targetException != null)
523 resultException = targetException;
524
525 throw new TransformerException(resultException);
526 }
527 catch (Exception e)
528 {
529 // e.printStackTrace ();
530 throw new TransformerException (e);
531 }
532
533 if (result != null)
534 {
535 xpc.outputToResultTree (stylesheetTree, result);
536 }
537
538 }
539
540 }