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: MethodResolver.java 1225263 2011-12-28 18:36:36Z mrglavas $ 020 */ 021 package org.apache.xalan.extensions; 022 023 import java.lang.reflect.Constructor; 024 import java.lang.reflect.Method; 025 import java.lang.reflect.Modifier; 026 027 import javax.xml.transform.TransformerException; 028 029 import org.apache.xalan.res.XSLMessages; 030 import org.apache.xalan.res.XSLTErrorResources; 031 import org.apache.xml.dtm.DTM; 032 import org.apache.xml.dtm.DTMIterator; 033 import org.apache.xml.dtm.ref.DTMNodeIterator; 034 import org.apache.xpath.objects.XObject; 035 import org.apache.xpath.objects.XRTreeFrag; 036 import org.apache.xpath.objects.XString; 037 import org.w3c.dom.Node; 038 import org.w3c.dom.NodeList; 039 import org.w3c.dom.traversal.NodeIterator; 040 041 /** 042 * Utility class to help resolve method overloading with Xalan XSLT 043 * argument types. 044 */ 045 public class MethodResolver 046 { 047 048 /** 049 * Specifies a search for static methods only. 050 */ 051 public static final int STATIC_ONLY = 1; 052 053 /** 054 * Specifies a search for instance methods only. 055 */ 056 public static final int INSTANCE_ONLY = 2; 057 058 /** 059 * Specifies a search for both static and instance methods. 060 */ 061 public static final int STATIC_AND_INSTANCE = 3; 062 063 /** 064 * Specifies a Dynamic method search. If the method being 065 * evaluated is a static method, all arguments are used. 066 * Otherwise, it is an instance method and only arguments 067 * beginning with the second argument are used. 068 */ 069 public static final int DYNAMIC = 4; 070 071 /** 072 * Given a class, figure out the resolution of 073 * the Java Constructor from the XSLT argument types, and perform the 074 * conversion of the arguments. 075 * @param classObj the Class of the object to be constructed. 076 * @param argsIn An array of XSLT/XPath arguments. 077 * @param argsOut An array of the exact size as argsIn, which will be 078 * populated with converted arguments if a suitable method is found. 079 * @return A constructor that will work with the argsOut array. 080 * @throws TransformerException may be thrown for Xalan conversion 081 * exceptions. 082 */ 083 public static Constructor getConstructor(Class classObj, 084 Object[] argsIn, 085 Object[][] argsOut, 086 ExpressionContext exprContext) 087 throws NoSuchMethodException, 088 SecurityException, 089 TransformerException 090 { 091 Constructor bestConstructor = null; 092 Class[] bestParamTypes = null; 093 Constructor[] constructors = classObj.getConstructors(); 094 int nMethods = constructors.length; 095 int bestScore = Integer.MAX_VALUE; 096 int bestScoreCount = 0; 097 for(int i = 0; i < nMethods; i++) 098 { 099 Constructor ctor = constructors[i]; 100 Class[] paramTypes = ctor.getParameterTypes(); 101 int numberMethodParams = paramTypes.length; 102 int paramStart = 0; 103 boolean isFirstExpressionContext = false; 104 int scoreStart; 105 // System.out.println("numberMethodParams: "+numberMethodParams); 106 // System.out.println("argsIn.length: "+argsIn.length); 107 // System.out.println("exprContext: "+exprContext); 108 if(numberMethodParams == (argsIn.length+1)) 109 { 110 Class javaClass = paramTypes[0]; 111 // System.out.println("first javaClass: "+javaClass.getName()); 112 if(ExpressionContext.class.isAssignableFrom(javaClass)) 113 { 114 isFirstExpressionContext = true; 115 scoreStart = 0; 116 paramStart++; 117 // System.out.println("Incrementing paramStart: "+paramStart); 118 } 119 else 120 continue; 121 } 122 else 123 scoreStart = 1000; 124 125 if(argsIn.length == (numberMethodParams - paramStart)) 126 { 127 // then we have our candidate. 128 int score = scoreMatch(paramTypes, paramStart, argsIn, scoreStart); 129 // System.out.println("score: "+score); 130 if(-1 == score) 131 continue; 132 if(score < bestScore) 133 { 134 // System.out.println("Assigning best ctor: "+ctor); 135 bestConstructor = ctor; 136 bestParamTypes = paramTypes; 137 bestScore = score; 138 bestScoreCount = 1; 139 } 140 else if (score == bestScore) 141 bestScoreCount++; 142 } 143 } 144 145 if(null == bestConstructor) 146 { 147 throw new NoSuchMethodException(errString("function", "constructor", classObj, 148 "", 0, argsIn)); 149 } 150 /*** This is commented out until we can do a better object -> object scoring 151 else if (bestScoreCount > 1) 152 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_MORE_MATCH_CONSTRUCTOR, new Object[]{classObj.getName()})); //"More than one best match for constructor for " 153 + classObj.getName()); 154 ***/ 155 else 156 convertParams(argsIn, argsOut, bestParamTypes, exprContext); 157 158 return bestConstructor; 159 } 160 161 162 /** 163 * Given the name of a method, figure out the resolution of 164 * the Java Method from the XSLT argument types, and perform the 165 * conversion of the arguments. 166 * @param classObj The Class of the object that should have the method. 167 * @param name The name of the method to be invoked. 168 * @param argsIn An array of XSLT/XPath arguments. 169 * @param argsOut An array of the exact size as argsIn, which will be 170 * populated with converted arguments if a suitable method is found. 171 * @return A method that will work with the argsOut array. 172 * @throws TransformerException may be thrown for Xalan conversion 173 * exceptions. 174 */ 175 public static Method getMethod(Class classObj, 176 String name, 177 Object[] argsIn, 178 Object[][] argsOut, 179 ExpressionContext exprContext, 180 int searchMethod) 181 throws NoSuchMethodException, 182 SecurityException, 183 TransformerException 184 { 185 // System.out.println("---> Looking for method: "+name); 186 // System.out.println("---> classObj: "+classObj); 187 if (name.indexOf("-")>0) 188 name = replaceDash(name); 189 Method bestMethod = null; 190 Class[] bestParamTypes = null; 191 Method[] methods = classObj.getMethods(); 192 int nMethods = methods.length; 193 int bestScore = Integer.MAX_VALUE; 194 int bestScoreCount = 0; 195 boolean isStatic; 196 for(int i = 0; i < nMethods; i++) 197 { 198 Method method = methods[i]; 199 // System.out.println("looking at method: "+method); 200 int xsltParamStart = 0; 201 if(method.getName().equals(name)) 202 { 203 isStatic = Modifier.isStatic(method.getModifiers()); 204 switch(searchMethod) 205 { 206 case STATIC_ONLY: 207 if (!isStatic) 208 { 209 continue; 210 } 211 break; 212 213 case INSTANCE_ONLY: 214 if (isStatic) 215 { 216 continue; 217 } 218 break; 219 220 case STATIC_AND_INSTANCE: 221 break; 222 223 case DYNAMIC: 224 if (!isStatic) 225 xsltParamStart = 1; 226 } 227 int javaParamStart = 0; 228 Class[] paramTypes = method.getParameterTypes(); 229 int numberMethodParams = paramTypes.length; 230 boolean isFirstExpressionContext = false; 231 int scoreStart; 232 // System.out.println("numberMethodParams: "+numberMethodParams); 233 // System.out.println("argsIn.length: "+argsIn.length); 234 // System.out.println("exprContext: "+exprContext); 235 int argsLen = (null != argsIn) ? argsIn.length : 0; 236 if(numberMethodParams == (argsLen-xsltParamStart+1)) 237 { 238 Class javaClass = paramTypes[0]; 239 if(ExpressionContext.class.isAssignableFrom(javaClass)) 240 { 241 isFirstExpressionContext = true; 242 scoreStart = 0; 243 javaParamStart++; 244 } 245 else 246 { 247 continue; 248 } 249 } 250 else 251 scoreStart = 1000; 252 253 if((argsLen - xsltParamStart) == (numberMethodParams - javaParamStart)) 254 { 255 // then we have our candidate. 256 int score = scoreMatch(paramTypes, javaParamStart, argsIn, scoreStart); 257 // System.out.println("score: "+score); 258 if(-1 == score) 259 continue; 260 if(score < bestScore) 261 { 262 // System.out.println("Assigning best method: "+method); 263 bestMethod = method; 264 bestParamTypes = paramTypes; 265 bestScore = score; 266 bestScoreCount = 1; 267 } 268 else if (score == bestScore) 269 bestScoreCount++; 270 } 271 } 272 } 273 274 if (null == bestMethod) 275 { 276 throw new NoSuchMethodException(errString("function", "method", classObj, 277 name, searchMethod, argsIn)); 278 } 279 /*** This is commented out until we can do a better object -> object scoring 280 else if (bestScoreCount > 1) 281 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_MORE_MATCH_METHOD, new Object[]{name})); //"More than one best match for method " + name); 282 ***/ 283 else 284 convertParams(argsIn, argsOut, bestParamTypes, exprContext); 285 286 return bestMethod; 287 } 288 289 /** 290 * To support EXSLT extensions, convert names with dash to allowable Java names: 291 * e.g., convert abc-xyz to abcXyz. 292 * Note: dashes only appear in middle of an EXSLT function or element name. 293 */ 294 private static String replaceDash(String name) 295 { 296 char dash = '-'; 297 StringBuffer buff = new StringBuffer(""); 298 for (int i=0; i<name.length(); i++) 299 { 300 if (name.charAt(i) == dash) 301 {} 302 else if (i > 0 && name.charAt(i-1) == dash) 303 buff.append(Character.toUpperCase(name.charAt(i))); 304 else 305 buff.append(name.charAt(i)); 306 } 307 return buff.toString(); 308 } 309 310 /** 311 * Given the name of a method, figure out the resolution of 312 * the Java Method 313 * @param classObj The Class of the object that should have the method. 314 * @param name The name of the method to be invoked. 315 * @return A method that will work to be called as an element. 316 * @throws TransformerException may be thrown for Xalan conversion 317 * exceptions. 318 */ 319 public static Method getElementMethod(Class classObj, 320 String name) 321 throws NoSuchMethodException, 322 SecurityException, 323 TransformerException 324 { 325 // System.out.println("---> Looking for element method: "+name); 326 // System.out.println("---> classObj: "+classObj); 327 Method bestMethod = null; 328 Method[] methods = classObj.getMethods(); 329 int nMethods = methods.length; 330 int bestScoreCount = 0; 331 for(int i = 0; i < nMethods; i++) 332 { 333 Method method = methods[i]; 334 // System.out.println("looking at method: "+method); 335 if(method.getName().equals(name)) 336 { 337 Class[] paramTypes = method.getParameterTypes(); 338 if ( (paramTypes.length == 2) 339 && paramTypes[1].isAssignableFrom(org.apache.xalan.templates.ElemExtensionCall.class) 340 && paramTypes[0].isAssignableFrom(org.apache.xalan.extensions.XSLProcessorContext.class) ) 341 { 342 if ( ++bestScoreCount == 1 ) 343 bestMethod = method; 344 else 345 break; 346 } 347 } 348 } 349 350 if (null == bestMethod) 351 { 352 throw new NoSuchMethodException(errString("element", "method", classObj, 353 name, 0, null)); 354 } 355 else if (bestScoreCount > 1) 356 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_MORE_MATCH_ELEMENT, new Object[]{name})); //"More than one best match for element method " + name); 357 358 return bestMethod; 359 } 360 361 362 /** 363 * Convert a set of parameters based on a set of paramTypes. 364 * @param argsIn An array of XSLT/XPath arguments. 365 * @param argsOut An array of the exact size as argsIn, which will be 366 * populated with converted arguments. 367 * @param paramTypes An array of class objects, of the exact same 368 * size as argsIn and argsOut. 369 * @throws TransformerException may be thrown for Xalan conversion 370 * exceptions. 371 */ 372 public static void convertParams(Object[] argsIn, 373 Object[][] argsOut, Class[] paramTypes, 374 ExpressionContext exprContext) 375 throws javax.xml.transform.TransformerException 376 { 377 // System.out.println("In convertParams"); 378 if (paramTypes == null) 379 argsOut[0] = null; 380 else 381 { 382 int nParams = paramTypes.length; 383 argsOut[0] = new Object[nParams]; 384 int paramIndex = 0; 385 if((nParams > 0) 386 && ExpressionContext.class.isAssignableFrom(paramTypes[0])) 387 { 388 argsOut[0][0] = exprContext; 389 // System.out.println("Incrementing paramIndex in convertParams: "+paramIndex); 390 paramIndex++; 391 } 392 393 if (argsIn != null) 394 { 395 for(int i = argsIn.length - nParams + paramIndex ; paramIndex < nParams; i++, paramIndex++) 396 { 397 // System.out.println("paramTypes[i]: "+paramTypes[i]); 398 argsOut[0][paramIndex] = convert(argsIn[i], paramTypes[paramIndex]); 399 } 400 } 401 } 402 } 403 404 /** 405 * Simple class to hold information about allowed conversions 406 * and their relative scores, for use by the table below. 407 */ 408 static class ConversionInfo 409 { 410 ConversionInfo(Class cl, int score) 411 { 412 m_class = cl; 413 m_score = score; 414 } 415 416 Class m_class; // Java class to convert to. 417 int m_score; // Match score, closer to zero is more matched. 418 } 419 420 private static final int SCOREBASE=1; 421 422 /** 423 * Specification of conversions from XSLT type CLASS_UNKNOWN 424 * (i.e. some unknown Java object) to allowed Java types. 425 */ 426 private final static ConversionInfo[] m_javaObjConversions = { 427 new ConversionInfo(Double.TYPE, 11), 428 new ConversionInfo(Float.TYPE, 12), 429 new ConversionInfo(Long.TYPE, 13), 430 new ConversionInfo(Integer.TYPE, 14), 431 new ConversionInfo(Short.TYPE, 15), 432 new ConversionInfo(Character.TYPE, 16), 433 new ConversionInfo(Byte.TYPE, 17), 434 new ConversionInfo(java.lang.String.class, 18) 435 }; 436 437 /** 438 * Specification of conversions from XSLT type CLASS_BOOLEAN 439 * to allowed Java types. 440 */ 441 private final static ConversionInfo[] m_booleanConversions = { 442 new ConversionInfo(Boolean.TYPE, 0), 443 new ConversionInfo(java.lang.Boolean.class, 1), 444 new ConversionInfo(java.lang.Object.class, 2), 445 new ConversionInfo(java.lang.String.class, 3) 446 }; 447 448 /** 449 * Specification of conversions from XSLT type CLASS_NUMBER 450 * to allowed Java types. 451 */ 452 private final static ConversionInfo[] m_numberConversions = { 453 new ConversionInfo(Double.TYPE, 0), 454 new ConversionInfo(java.lang.Double.class, 1), 455 new ConversionInfo(Float.TYPE, 3), 456 new ConversionInfo(Long.TYPE, 4), 457 new ConversionInfo(Integer.TYPE, 5), 458 new ConversionInfo(Short.TYPE, 6), 459 new ConversionInfo(Character.TYPE, 7), 460 new ConversionInfo(Byte.TYPE, 8), 461 new ConversionInfo(Boolean.TYPE, 9), 462 new ConversionInfo(java.lang.String.class, 10), 463 new ConversionInfo(java.lang.Object.class, 11) 464 }; 465 466 /** 467 * Specification of conversions from XSLT type CLASS_STRING 468 * to allowed Java types. 469 */ 470 private final static ConversionInfo[] m_stringConversions = { 471 new ConversionInfo(java.lang.String.class, 0), 472 new ConversionInfo(java.lang.Object.class, 1), 473 new ConversionInfo(Character.TYPE, 2), 474 new ConversionInfo(Double.TYPE, 3), 475 new ConversionInfo(Float.TYPE, 3), 476 new ConversionInfo(Long.TYPE, 3), 477 new ConversionInfo(Integer.TYPE, 3), 478 new ConversionInfo(Short.TYPE, 3), 479 new ConversionInfo(Byte.TYPE, 3), 480 new ConversionInfo(Boolean.TYPE, 4) 481 }; 482 483 /** 484 * Specification of conversions from XSLT type CLASS_RTREEFRAG 485 * to allowed Java types. 486 */ 487 private final static ConversionInfo[] m_rtfConversions = { 488 new ConversionInfo(org.w3c.dom.traversal.NodeIterator.class, 0), 489 new ConversionInfo(org.w3c.dom.NodeList.class, 1), 490 new ConversionInfo(org.w3c.dom.Node.class, 2), 491 new ConversionInfo(java.lang.String.class, 3), 492 new ConversionInfo(java.lang.Object.class, 5), 493 new ConversionInfo(Character.TYPE, 6), 494 new ConversionInfo(Double.TYPE, 7), 495 new ConversionInfo(Float.TYPE, 7), 496 new ConversionInfo(Long.TYPE, 7), 497 new ConversionInfo(Integer.TYPE, 7), 498 new ConversionInfo(Short.TYPE, 7), 499 new ConversionInfo(Byte.TYPE, 7), 500 new ConversionInfo(Boolean.TYPE, 8) 501 }; 502 503 /** 504 * Specification of conversions from XSLT type CLASS_NODESET 505 * to allowed Java types. (This is the same as for CLASS_RTREEFRAG) 506 */ 507 private final static ConversionInfo[] m_nodesetConversions = { 508 new ConversionInfo(org.w3c.dom.traversal.NodeIterator.class, 0), 509 new ConversionInfo(org.w3c.dom.NodeList.class, 1), 510 new ConversionInfo(org.w3c.dom.Node.class, 2), 511 new ConversionInfo(java.lang.String.class, 3), 512 new ConversionInfo(java.lang.Object.class, 5), 513 new ConversionInfo(Character.TYPE, 6), 514 new ConversionInfo(Double.TYPE, 7), 515 new ConversionInfo(Float.TYPE, 7), 516 new ConversionInfo(Long.TYPE, 7), 517 new ConversionInfo(Integer.TYPE, 7), 518 new ConversionInfo(Short.TYPE, 7), 519 new ConversionInfo(Byte.TYPE, 7), 520 new ConversionInfo(Boolean.TYPE, 8) 521 }; 522 523 /** 524 * Order is significant in the list below, based on 525 * XObject.CLASS_XXX values. 526 */ 527 private final static ConversionInfo[][] m_conversions = 528 { 529 m_javaObjConversions, // CLASS_UNKNOWN = 0; 530 m_booleanConversions, // CLASS_BOOLEAN = 1; 531 m_numberConversions, // CLASS_NUMBER = 2; 532 m_stringConversions, // CLASS_STRING = 3; 533 m_nodesetConversions, // CLASS_NODESET = 4; 534 m_rtfConversions // CLASS_RTREEFRAG = 5; 535 }; 536 537 /** 538 * Score the conversion of a set of XSLT arguments to a 539 * given set of Java parameters. 540 * If any invocations of this function for a method with 541 * the same name return the same positive value, then a conflict 542 * has occured, and an error should be signaled. 543 * @param javaParamTypes Must be filled with valid class names, and 544 * of the same length as xsltArgs. 545 * @param xsltArgs Must be filled with valid object instances, and 546 * of the same length as javeParamTypes. 547 * @return -1 for no allowed conversion, or a positive score 548 * that is closer to zero for more preferred, or further from 549 * zero for less preferred. 550 */ 551 public static int scoreMatch(Class[] javaParamTypes, int javaParamsStart, 552 Object[] xsltArgs, int score) 553 { 554 if ((xsltArgs == null) || (javaParamTypes == null)) 555 return score; 556 int nParams = xsltArgs.length; 557 for(int i = nParams - javaParamTypes.length + javaParamsStart, javaParamTypesIndex = javaParamsStart; 558 i < nParams; 559 i++, javaParamTypesIndex++) 560 { 561 Object xsltObj = xsltArgs[i]; 562 int xsltClassType = (xsltObj instanceof XObject) 563 ? ((XObject)xsltObj).getType() 564 : XObject.CLASS_UNKNOWN; 565 Class javaClass = javaParamTypes[javaParamTypesIndex]; 566 567 // System.out.println("Checking xslt: "+xsltObj.getClass().getName()+ 568 // " against java: "+javaClass.getName()); 569 570 if(xsltClassType == XObject.CLASS_NULL) 571 { 572 // In Xalan I have objects of CLASS_NULL, though I'm not 573 // sure they're used any more. For now, do something funky. 574 if(!javaClass.isPrimitive()) 575 { 576 // Then assume that a null can be used, but give it a low score. 577 score += 10; 578 continue; 579 } 580 else 581 return -1; // no match. 582 } 583 584 ConversionInfo[] convInfo = m_conversions[xsltClassType]; 585 int nConversions = convInfo.length; 586 int k; 587 for(k = 0; k < nConversions; k++) 588 { 589 ConversionInfo cinfo = convInfo[k]; 590 if(javaClass.isAssignableFrom(cinfo.m_class)) 591 { 592 score += cinfo.m_score; 593 break; // from k loop 594 } 595 } 596 597 if (k == nConversions) 598 { 599 // If we get here, we haven't made a match on this parameter using 600 // the ConversionInfo array. We now try to handle the object -> object 601 // mapping which we can't handle through the array mechanism. To do this, 602 // we must determine the class of the argument passed from the stylesheet. 603 604 // If we were passed a subclass of XObject, representing one of the actual 605 // XSLT types, and we are here, we reject this extension method as a candidate 606 // because a match should have been made using the ConversionInfo array. If we 607 // were passed an XObject that encapsulates a non-XSLT type or we 608 // were passed a non-XSLT type directly, we continue. 609 610 // The current implementation (contributed by Kelly Campbell <camk@channelpoint.com>) 611 // checks to see if we were passed an XObject from the XSLT stylesheet. If not, 612 // we use the class of the object that was passed and make sure that it will 613 // map to the class type of the parameter in the extension function. 614 // If we were passed an XObject, we attempt to get the class of the actual 615 // object encapsulated inside the XObject. If the encapsulated object is null, 616 // we judge this method as a match but give it a low score. 617 // If the encapsulated object is not null, we use its type to determine 618 // whether this java method is a valid match for this extension function call. 619 // This approach eliminates the NullPointerException in the earlier implementation 620 // that resulted from passing an XObject encapsulating the null java object. 621 622 // TODO: This needs to be improved to assign relative scores to subclasses, 623 // etc. 624 625 if (XObject.CLASS_UNKNOWN == xsltClassType) 626 { 627 Class realClass = null; 628 629 if (xsltObj instanceof XObject) 630 { 631 Object realObj = ((XObject) xsltObj).object(); 632 if (null != realObj) 633 { 634 realClass = realObj.getClass(); 635 } 636 else 637 { 638 // do the same as if we were passed XObject.CLASS_NULL 639 score += 10; 640 continue; 641 } 642 } 643 else 644 { 645 realClass = xsltObj.getClass(); 646 } 647 648 if (javaClass.isAssignableFrom(realClass)) 649 { 650 score += 0; // TODO: To be assigned based on subclass "distance" 651 } 652 else 653 return -1; 654 } 655 else 656 return -1; 657 } 658 } 659 return score; 660 } 661 662 /** 663 * Convert the given XSLT object to an object of 664 * the given class. 665 * @param xsltObj The XSLT object that needs conversion. 666 * @param javaClass The type of object to convert to. 667 * @returns An object suitable for passing to the Method.invoke 668 * function in the args array, which may be null in some cases. 669 * @throws TransformerException may be thrown for Xalan conversion 670 * exceptions. 671 */ 672 static Object convert(Object xsltObj, Class javaClass) 673 throws javax.xml.transform.TransformerException 674 { 675 if(xsltObj instanceof XObject) 676 { 677 XObject xobj = ((XObject)xsltObj); 678 int xsltClassType = xobj.getType(); 679 680 switch(xsltClassType) 681 { 682 case XObject.CLASS_NULL: 683 return null; 684 685 case XObject.CLASS_BOOLEAN: 686 { 687 if(javaClass == java.lang.String.class) 688 return xobj.str(); 689 else 690 return xobj.bool() ? Boolean.TRUE : Boolean.FALSE; 691 } 692 // break; Unreachable 693 case XObject.CLASS_NUMBER: 694 { 695 if(javaClass == java.lang.String.class) 696 return xobj.str(); 697 else if(javaClass == Boolean.TYPE) 698 return xobj.bool() ? Boolean.TRUE : Boolean.FALSE; 699 else 700 { 701 return convertDoubleToNumber(xobj.num(), javaClass); 702 } 703 } 704 // break; Unreachable 705 706 case XObject.CLASS_STRING: 707 { 708 if((javaClass == java.lang.String.class) || 709 (javaClass == java.lang.Object.class)) 710 return xobj.str(); 711 else if(javaClass == Character.TYPE) 712 { 713 String str = xobj.str(); 714 if(str.length() > 0) 715 return new Character(str.charAt(0)); 716 else 717 return null; // ?? 718 } 719 else if(javaClass == Boolean.TYPE) 720 return xobj.bool() ? Boolean.TRUE : Boolean.FALSE; 721 else 722 { 723 return convertDoubleToNumber(xobj.num(), javaClass); 724 } 725 } 726 // break; Unreachable 727 728 case XObject.CLASS_RTREEFRAG: 729 { 730 // GLP: I don't see the reason for the isAssignableFrom call 731 // instead of an == test as is used everywhere else. 732 // Besides, if the javaClass is a subclass of NodeIterator 733 // the condition will be true and we'll create a NodeIterator 734 // which may not match the javaClass, causing a RuntimeException. 735 // if((NodeIterator.class.isAssignableFrom(javaClass)) || 736 if ( (javaClass == NodeIterator.class) || 737 (javaClass == java.lang.Object.class) ) 738 { 739 DTMIterator dtmIter = ((XRTreeFrag) xobj).asNodeIterator(); 740 return new DTMNodeIterator(dtmIter); 741 } 742 else if (javaClass == NodeList.class) 743 { 744 return ((XRTreeFrag) xobj).convertToNodeset(); 745 } 746 // Same comment as above 747 // else if(Node.class.isAssignableFrom(javaClass)) 748 else if(javaClass == Node.class) 749 { 750 DTMIterator iter = ((XRTreeFrag) xobj).asNodeIterator(); 751 int rootHandle = iter.nextNode(); 752 DTM dtm = iter.getDTM(rootHandle); 753 return dtm.getNode(dtm.getFirstChild(rootHandle)); 754 } 755 else if(javaClass == java.lang.String.class) 756 { 757 return xobj.str(); 758 } 759 else if(javaClass == Boolean.TYPE) 760 { 761 return xobj.bool() ? Boolean.TRUE : Boolean.FALSE; 762 } 763 else if(javaClass.isPrimitive()) 764 { 765 return convertDoubleToNumber(xobj.num(), javaClass); 766 } 767 else 768 { 769 DTMIterator iter = ((XRTreeFrag) xobj).asNodeIterator(); 770 int rootHandle = iter.nextNode(); 771 DTM dtm = iter.getDTM(rootHandle); 772 Node child = dtm.getNode(dtm.getFirstChild(rootHandle)); 773 774 if(javaClass.isAssignableFrom(child.getClass())) 775 return child; 776 else 777 return null; 778 } 779 } 780 // break; Unreachable 781 782 case XObject.CLASS_NODESET: 783 { 784 // GLP: I don't see the reason for the isAssignableFrom call 785 // instead of an == test as is used everywhere else. 786 // Besides, if the javaClass is a subclass of NodeIterator 787 // the condition will be true and we'll create a NodeIterator 788 // which may not match the javaClass, causing a RuntimeException. 789 // if((NodeIterator.class.isAssignableFrom(javaClass)) || 790 if ( (javaClass == NodeIterator.class) || 791 (javaClass == java.lang.Object.class) ) 792 { 793 return xobj.nodeset(); 794 } 795 // Same comment as above 796 // else if(NodeList.class.isAssignableFrom(javaClass)) 797 else if(javaClass == NodeList.class) 798 { 799 return xobj.nodelist(); 800 } 801 // Same comment as above 802 // else if(Node.class.isAssignableFrom(javaClass)) 803 else if(javaClass == Node.class) 804 { 805 // Xalan ensures that iter() always returns an 806 // iterator positioned at the beginning. 807 DTMIterator ni = xobj.iter(); 808 int handle = ni.nextNode(); 809 if (handle != DTM.NULL) 810 return ni.getDTM(handle).getNode(handle); // may be null. 811 else 812 return null; 813 } 814 else if(javaClass == java.lang.String.class) 815 { 816 return xobj.str(); 817 } 818 else if(javaClass == Boolean.TYPE) 819 { 820 return xobj.bool() ? Boolean.TRUE : Boolean.FALSE; 821 } 822 else if(javaClass.isPrimitive()) 823 { 824 return convertDoubleToNumber(xobj.num(), javaClass); 825 } 826 else 827 { 828 DTMIterator iter = xobj.iter(); 829 int childHandle = iter.nextNode(); 830 DTM dtm = iter.getDTM(childHandle); 831 Node child = dtm.getNode(childHandle); 832 if(javaClass.isAssignableFrom(child.getClass())) 833 return child; 834 else 835 return null; 836 } 837 } 838 // break; Unreachable 839 840 // No default:, fall-through on purpose 841 } // end switch 842 xsltObj = xobj.object(); 843 844 } // end if if(xsltObj instanceof XObject) 845 846 // At this point, we have a raw java object, not an XObject. 847 if (null != xsltObj) 848 { 849 if(javaClass == java.lang.String.class) 850 { 851 return xsltObj.toString(); 852 } 853 else if(javaClass.isPrimitive()) 854 { 855 // Assume a number conversion 856 XString xstr = new XString(xsltObj.toString()); 857 double num = xstr.num(); 858 return convertDoubleToNumber(num, javaClass); 859 } 860 else if(javaClass == java.lang.Class.class) 861 { 862 return xsltObj.getClass(); 863 } 864 else 865 { 866 // Just pass the object directly, and hope for the best. 867 return xsltObj; 868 } 869 } 870 else 871 { 872 // Just pass the object directly, and hope for the best. 873 return xsltObj; 874 } 875 } 876 877 /** 878 * Do a standard conversion of a double to the specified type. 879 * @param num The number to be converted. 880 * @param javaClass The class type to be converted to. 881 * @return An object specified by javaClass, or a Double instance. 882 */ 883 static Object convertDoubleToNumber(double num, Class javaClass) 884 { 885 // In the code below, I don't check for NaN, etc., instead 886 // using the standard Java conversion, as I think we should 887 // specify. See issue-runtime-errors. 888 if((javaClass == Double.TYPE) || 889 (javaClass == java.lang.Double.class)) 890 return new Double(num); 891 else if(javaClass == Float.TYPE) 892 return new Float(num); 893 else if(javaClass == Long.TYPE) 894 { 895 // Use standard Java Narrowing Primitive Conversion 896 // See http://java.sun.com/docs/books/jls/html/5.doc.html#175672 897 return new Long((long)num); 898 } 899 else if(javaClass == Integer.TYPE) 900 { 901 // Use standard Java Narrowing Primitive Conversion 902 // See http://java.sun.com/docs/books/jls/html/5.doc.html#175672 903 return new Integer((int)num); 904 } 905 else if(javaClass == Short.TYPE) 906 { 907 // Use standard Java Narrowing Primitive Conversion 908 // See http://java.sun.com/docs/books/jls/html/5.doc.html#175672 909 return new Short((short)num); 910 } 911 else if(javaClass == Character.TYPE) 912 { 913 // Use standard Java Narrowing Primitive Conversion 914 // See http://java.sun.com/docs/books/jls/html/5.doc.html#175672 915 return new Character((char)num); 916 } 917 else if(javaClass == Byte.TYPE) 918 { 919 // Use standard Java Narrowing Primitive Conversion 920 // See http://java.sun.com/docs/books/jls/html/5.doc.html#175672 921 return new Byte((byte)num); 922 } 923 else // Some other type of object 924 { 925 return new Double(num); 926 } 927 } 928 929 930 /** 931 * Format the message for the NoSuchMethodException containing 932 * all the information about the method we're looking for. 933 */ 934 private static String errString(String callType, // "function" or "element" 935 String searchType, // "method" or "constructor" 936 Class classObj, 937 String funcName, 938 int searchMethod, 939 Object[] xsltArgs) 940 { 941 String resultString = "For extension " + callType 942 + ", could not find " + searchType + " "; 943 switch (searchMethod) 944 { 945 case STATIC_ONLY: 946 return resultString + "static " + classObj.getName() + "." 947 + funcName + "([ExpressionContext,] " + errArgs(xsltArgs, 0) + ")."; 948 949 case INSTANCE_ONLY: 950 return resultString + classObj.getName() + "." 951 + funcName + "([ExpressionContext,] " + errArgs(xsltArgs, 0) + ")."; 952 953 case STATIC_AND_INSTANCE: 954 return resultString + classObj.getName() + "." + funcName + "([ExpressionContext,] " + errArgs(xsltArgs, 0) + ").\n" 955 + "Checked both static and instance methods."; 956 957 case DYNAMIC: 958 return resultString + "static " + classObj.getName() + "." + funcName 959 + "([ExpressionContext, ]" + errArgs(xsltArgs, 0) + ") nor\n" 960 + classObj + "." + funcName + "([ExpressionContext,] " + errArgs(xsltArgs, 1) + ")."; 961 962 default: 963 if (callType.equals("function")) // must be a constructor 964 { 965 return resultString + classObj.getName() 966 + "([ExpressionContext,] " + errArgs(xsltArgs, 0) + ")."; 967 } 968 else // must be an element call 969 { 970 return resultString + classObj.getName() + "." + funcName 971 + "(org.apache.xalan.extensions.XSLProcessorContext, " 972 + "org.apache.xalan.templates.ElemExtensionCall)."; 973 } 974 } 975 976 } 977 978 979 private static String errArgs(Object[] xsltArgs, int startingArg) 980 { 981 StringBuffer returnArgs = new StringBuffer(); 982 for (int i = startingArg; i < xsltArgs.length; i++) 983 { 984 if (i != startingArg) 985 returnArgs.append(", "); 986 if (xsltArgs[i] instanceof XObject) 987 returnArgs.append(((XObject) xsltArgs[i]).getTypeString()); 988 else 989 returnArgs.append(xsltArgs[i].getClass().getName()); 990 } 991 return returnArgs.toString(); 992 } 993 994 }