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 }