1 package com.icl.saxon.expr; 2 import com.icl.saxon.*; 3 import com.icl.saxon.om.Namespace; 4 import com.icl.saxon.om.NodeInfo; 5 import com.icl.saxon.om.NodeEnumeration; 6 import java.util.Vector; 7 import javax.xml.transform.TransformerException; 8 import java.lang.reflect.*; 9 import org.w3c.dom.*; 10 import org.w3c.xsl.XSLTContext; 11 12 13 14 18 19 public class FunctionProxy extends Function { 20 21 private Class theClass; 22 private Vector candidateMethods = new Vector(); 23 private XPathException theException = null; 24 private String name; 25 private Class resultClass = null; 26 27 30 31 public FunctionProxy() {} 32 33 48 49 public boolean setFunctionName(Class reqClass, String localName) { 50 boolean isAvailable = false; 51 53 name = localName; 54 int numArgs = getNumberOfArguments(); 55 int significantArgs = numArgs; 56 57 theClass = reqClass; 58 59 61 if (name.equals("new")) { 62 63 int mod = theClass.getModifiers(); 64 if (Modifier.isAbstract(mod)) { 65 theException = new XPathException("Class " + theClass + " is abstract"); 66 return false; 67 } 68 if (Modifier.isInterface(mod)) { 69 theException = new XPathException(theClass + " is an interface"); 70 return false; 71 } 72 if (Modifier.isPrivate(mod)) { 73 theException = new XPathException("Class " + theClass + " is private"); 74 return false; 75 } 76 if (Modifier.isProtected(mod)) { 77 theException = new XPathException("Class " + theClass + " is protected"); 78 return false; 79 } 80 81 resultClass = theClass; 82 Constructor[] constructors = theClass.getConstructors(); 83 for (int c=0; c<constructors.length; c++) { 84 isAvailable = true; 85 Constructor theConstructor = constructors[c]; 86 if (theConstructor.getParameterTypes().length == numArgs) { 87 isAvailable = true; 88 candidateMethods.addElement(theConstructor); 89 } 90 } 91 if (isAvailable) { 92 return true; 93 } else { 94 theException = new XPathException("No constructor with " + numArgs + 95 (numArgs==1 ? " parameter" : " parameters") + 96 " found in class " + theClass.getName()); 97 return false; 98 } 99 } else { 100 101 103 StringBuffer buff = new StringBuffer(); 104 boolean afterHyphen = false; 105 for (int n=0; n<name.length(); n++) { 106 char c = name.charAt(n); 107 if (c=='-') { 108 afterHyphen = true; 109 } else { 110 if (afterHyphen) { 111 buff.append(Character.toUpperCase(c)); 112 } else { 113 buff.append(c); 114 } 115 afterHyphen = false; 116 } 117 } 118 119 name = buff.toString(); 120 121 123 if (name.equals("if")) { 124 name = "IF"; 125 } 126 127 129 Method[] methods = theClass.getMethods(); 130 boolean consistentReturnType = true; 131 for (int m=0; m<methods.length; m++) { 132 133 Method theMethod = methods[m]; 134 135 if (theMethod.getName().equals(name) && 136 Modifier.isPublic(theMethod.getModifiers())) { 137 isAvailable = true; 138 if (consistentReturnType) { 139 if (resultClass==null) { 140 resultClass = theMethod.getReturnType(); 141 } else { 142 consistentReturnType = 143 (theMethod.getReturnType()==resultClass); 144 } 145 } 146 Class[] theParameterTypes = theMethod.getParameterTypes(); 147 boolean isStatic = Modifier.isStatic(theMethod.getModifiers()); 148 149 152 significantArgs = (isStatic ? numArgs : numArgs - 1); 153 154 if (significantArgs>=0) { 155 156 159 if (theParameterTypes.length == significantArgs && 160 (significantArgs==0 || theParameterTypes[0]!=Context.class)) 161 { 163 isAvailable = true; 164 candidateMethods.addElement(theMethod); 165 } 166 167 169 if (theParameterTypes.length == significantArgs+1 && 170 (theParameterTypes[0]==Context.class || 171 theParameterTypes[0]==XSLTContext.class)) { 172 isAvailable = true; 173 candidateMethods.addElement(theMethod); 174 } 175 } 176 } 177 } 178 179 if (!consistentReturnType) { 180 resultClass = null; } 182 183 185 if (isAvailable) { 186 return true; 187 } else { 188 theException = new XPathException("No method matching " + name + 189 " with " + significantArgs + 190 (significantArgs==1 ? " parameter" : " parameters") + 191 " found in class " + theClass.getName()); 192 return false; 193 } 194 } 195 196 } 197 198 202 203 public int getDataType() { 204 if (resultClass==null || resultClass==Value.class) { 205 return Value.ANY; 206 } else if (resultClass.toString().equals("void")) { 207 return Value.NODESET; 208 } else if (resultClass==String.class || resultClass==StringValue.class) { 209 return Value.STRING; 210 } else if (resultClass==Boolean.class || resultClass==boolean.class || 211 resultClass==BooleanValue.class) { 212 return Value.BOOLEAN; 213 } else if (resultClass==Double.class || resultClass==double.class || 214 resultClass==Float.class || resultClass==float.class || 215 resultClass==Long.class || resultClass==long.class || 216 resultClass==Integer.class || resultClass==int.class || 217 resultClass==Short.class || resultClass==short.class || 218 resultClass==Byte.class || resultClass==byte.class || 219 resultClass==NumericValue.class) { 220 return Value.NUMBER; 221 } else if (NodeSetValue.class.isAssignableFrom(resultClass) || 222 NodeEnumeration.class.isAssignableFrom(resultClass) || 223 NodeList.class.isAssignableFrom(resultClass) || 224 Node.class.isAssignableFrom(resultClass)) { 225 return Value.NODESET; 226 } else { 227 return Value.OBJECT; 228 } 229 } 230 231 234 235 public String getName() { 236 return name; 237 } 238 239 242 243 public Expression simplify() throws XPathException { 244 for (int i=0; i<getNumberOfArguments(); i++) { 245 argument[i] = argument[i].simplify(); 246 } 247 248 251 if (candidateMethods.size() > 1) { 252 boolean allKnown = true; 253 for (int i=0; i<getNumberOfArguments(); i++) { 254 int type = argument[i].getDataType(); 255 if (type==Value.ANY || type==Value.OBJECT) { 256 allKnown = false; 257 break; 258 } 259 } 260 if (allKnown) { 261 Value[] argValues = new Value[getNumberOfArguments()]; 263 for (int k=0; k<getNumberOfArguments(); k++) { 264 switch (argument[k].getDataType()) { 265 case Value.BOOLEAN: 266 argValues[k] = new BooleanValue(true); 267 break; 268 case Value.NUMBER: 269 argValues[k] = new NumericValue(1.0); 270 break; 271 case Value.STRING: 272 argValues[k] = new StringValue(""); 273 break; 274 case Value.NODESET: 275 argValues[k] = new EmptyNodeSet(); 276 break; 277 } 278 } 279 try { 280 Object method = getBestFit(argValues); 281 candidateMethods = new Vector(); 282 candidateMethods.addElement(method); 283 } catch (XPathException err) { 284 theException = err; 285 } 286 } 287 } 288 return this; 289 } 290 291 296 297 public int getDependencies() { 298 int dep = 0; 299 dep = Context.CONTEXT_NODE | Context.POSITION | Context.LAST; 301 for (int i=0; i<getNumberOfArguments(); i++) { 303 dep |= argument[i].getDependencies(); 304 } 305 return dep; 306 } 307 308 316 317 public Expression reduce(int dependencies, Context context) throws XPathException { 318 319 if ( (dependencies & (Context.CONTEXT_NODE | 322 Context.POSITION | Context.LAST)) != 0) { 323 return evaluate(context); 324 325 } else { 326 327 FunctionProxy fp = new FunctionProxy(); 328 fp.theClass = theClass; 329 fp.candidateMethods = candidateMethods; 330 fp.theException = theException; 331 fp.name = name; 332 fp.argument = new Expression[getNumberOfArguments()]; 333 for (int a=0; a<getNumberOfArguments(); a++) { 334 fp.addArgument(argument[a].reduce(dependencies, context)); 335 } 336 return fp; 337 } 338 } 339 340 346 347 public Object getBestFit(Value[] argValues) throws XPathException { 348 349 if (candidateMethods.size() == 1) { 350 return candidateMethods.elementAt(0); 352 353 } else { 354 int candidates = candidateMethods.size(); 358 boolean eliminated[] = new boolean[candidates]; 359 for (int i=0; i<candidates; i++) { 360 eliminated[i] = false; 361 } 362 363 for (int i=0; i<candidates-1; i++) { 364 int[] pref_i = getConversionPreferences( 365 argValues, 366 candidateMethods.elementAt(i)); 367 if (!eliminated[i]) { 368 for (int j=i+1; j<candidates; j++) { 369 if (!eliminated[j]) { 370 int[] pref_j = getConversionPreferences( 371 argValues, 372 candidateMethods.elementAt(j)); 373 for (int k=0; k<pref_j.length; k++) { 374 if (pref_i[k] > pref_j[k]) { eliminated[i] = true; 376 } 377 if (pref_i[k] < pref_j[k]) { 378 eliminated[j] = true; 379 } 380 } 381 } 382 } 383 } 384 } 385 386 int remaining = 0; 387 Object theMethod = null; for (int r=0; r<candidates; r++) { 389 if (!eliminated[r]) { 390 theMethod = candidateMethods.elementAt(r); 391 remaining++; 392 } 393 } 394 395 if (remaining==0) { 396 throw new XPathException("There is no Java method that is a unique best match"); 397 } 398 399 if (remaining>1) { 400 throw new XPathException("There are several Java methods that match equally well"); 401 } 402 403 return theMethod; 404 } 405 } 406 407 413 414 public Value evaluate(Context context) throws XPathException { 415 Object result = call(context); 416 return convertJavaObjectToXPath(result, context.getController()); 417 } 418 419 public String evaluateAsString(Context context) throws XPathException { 420 if (resultClass==String.class) { 421 return (String)call(context); 422 } else if (resultClass==NodeEnumeration.class) { 423 NodeEnumeration enum = enumerate(context, true); 424 if (enum.hasMoreElements()) { 425 return enum.nextElement().getStringValue(); 426 } else { 427 return ""; 428 } 429 } else { 430 return evaluate(context).asString(); 431 } 432 } 433 434 public double evaluateAsNumber(Context context) throws XPathException { 435 if (resultClass==double.class) { 436 return ((Double)call(context)).doubleValue(); 437 } else if (resultClass==NodeEnumeration.class) { 438 NodeEnumeration enum = enumerate(context, true); 439 if (enum.hasMoreElements()) { 440 return Value.stringToNumber(enum.nextElement().getStringValue()); 441 } else { 442 return Double.NaN; 443 } 444 } else { 445 return evaluate(context).asNumber(); 446 } 447 } 448 449 public boolean evaluateAsBoolean(Context context) throws XPathException { 450 if (resultClass==boolean.class) { 451 return ((Boolean)call(context)).booleanValue(); 452 } else if (resultClass==NodeEnumeration.class) { 453 NodeEnumeration enum = enumerate(context, false); 454 return enum.hasMoreElements(); 455 } else { 456 return evaluate(context).asBoolean(); 457 } 458 } 459 460 public NodeEnumeration enumerate(Context context, boolean requireSorted) throws XPathException { 461 if (resultClass==NodeEnumeration.class) { 462 NodeEnumeration result = (NodeEnumeration)call(context); 463 if (requireSorted && !result.isSorted()) { 464 NodeSetExtent extent = new NodeSetExtent(result, context.getController()); 465 extent.sort(); 466 return extent.enumerate(); 467 } else { 468 return result; 469 } 470 } else { 471 return super.enumerate(context, requireSorted); 472 } 473 } 474 475 478 479 private Object call(Context context) throws XPathException { 480 481 483 if (theException!=null) { 484 throw theException; 485 } 486 context.setException(null); 487 488 Value[] argValues = new Value[getNumberOfArguments()]; 489 for (int a=0; a<getNumberOfArguments(); a++) { 490 argValues[a] = argument[a].evaluate(context); 491 } 492 493 495 Object theMethod = getBestFit(argValues); 496 498 500 Class[] theParameterTypes; 501 502 if (theMethod instanceof Constructor) { 503 Constructor constructor = (Constructor)theMethod; 504 theParameterTypes = constructor.getParameterTypes(); 505 Object[] params = new Object[theParameterTypes.length]; 506 507 setupParams(argValues, params, theParameterTypes, 0, 0); 508 509 try { 510 Object obj = constructor.newInstance(params); 511 if (context.getException() != null) { 512 throw context.getException(); 513 } 514 return obj; 515 } catch (InstantiationException err0) { 517 throw new XPathException ("Cannot instantiate class", err0); 518 } catch (IllegalAccessException err1) { 519 throw new XPathException ("Constructor access is illegal", err1); 520 } catch (IllegalArgumentException err2) { 521 throw new XPathException ("Argument is of wrong type", err2); 522 } catch (InvocationTargetException err3) { 523 Throwable ex = err3.getTargetException(); 524 if (ex instanceof XPathException) { 525 throw (XPathException)ex; 526 } else { 527 if (context.getController().isTracing()) { 528 err3.getTargetException().printStackTrace(); 529 } 530 throw new XPathException ("Exception in extension function " + 531 err3.getTargetException().toString()); 532 } 533 } 534 } else { 535 Method method = (Method)theMethod; 536 boolean isStatic = Modifier.isStatic(method.getModifiers()); 537 Object theInstance; 538 theParameterTypes = method.getParameterTypes(); 539 boolean usesContext = theParameterTypes.length > 0 && 540 (theParameterTypes[0] == Context.class || 541 theParameterTypes[0] == XSLTContext.class); 542 if (isStatic) { 543 theInstance = null; 544 } else { 545 int actualArgs = getNumberOfArguments(); 546 if (actualArgs==0) { 547 throw new XPathException("Must supply an argument for an instance-level extension function"); 548 } 549 Value arg0 = argument[0].evaluate(context); 550 if (arg0 instanceof ObjectValue) { 551 theInstance = ((ObjectValue)arg0).getObject(); 553 } else if (theClass==String.class) { 554 theInstance = arg0.asString(); 555 } else if (theClass==Boolean.class) { 556 theInstance = new Boolean(arg0.asBoolean()); 557 } else if (theClass==Double.class) { 558 theInstance = new Double(arg0.asNumber()); 559 } else { 560 throw new XPathException("First argument is not an object instance"); 561 } 562 563 } 564 565 int requireArgs = theParameterTypes.length - 566 (usesContext ? 1 : 0) + 567 (isStatic ? 0 : 1); 568 569 checkArgumentCount(requireArgs, requireArgs); 570 Object[] params = new Object[theParameterTypes.length]; 571 572 if (usesContext) { 573 params[0] = context; 574 } 575 576 setupParams(argValues, params, theParameterTypes, 577 (usesContext ? 1 : 0), 578 (isStatic ? 0 : 1) 579 ); 580 581 try { 582 Object result = method.invoke(theInstance, params); 583 if (context.getException() != null) { 584 throw context.getException(); 585 } 586 if (method.getReturnType().toString().equals("void")) { 587 return new EmptyNodeSet(); 590 } 591 return result; 592 593 } catch (IllegalAccessException err1) { 594 throw new XPathException ("Method access is illegal", err1); 595 } catch (IllegalArgumentException err2) { 596 throw new XPathException ("Argument is of wrong type", err2); 597 } catch (InvocationTargetException err3) { 598 Throwable ex = err3.getTargetException(); 599 if (ex instanceof XPathException) { 600 throw (XPathException)ex; 601 } else { 602 if (context.getController().isTracing()) { 603 err3.getTargetException().printStackTrace(); 604 } 605 throw new XPathException ("Exception in extension function " + 606 err3.getTargetException().toString()); 607 } 608 } 609 } 610 } 611 612 617 618 public static Value convertJavaObjectToXPath(Object result, Controller controller) 619 throws XPathException { 620 if (result==null) { 621 return new ObjectValue(null); 622 623 } else if (result instanceof String) { 624 return new StringValue((String)result); 625 626 } else if (result instanceof Boolean) { 627 return new BooleanValue(((Boolean)result).booleanValue()); 628 629 } else if (result instanceof Double) { 630 return new NumericValue(((Double)result).doubleValue()); 631 } else if (result instanceof Float) { 632 return new NumericValue((double)((Float)result).floatValue()); 633 } else if (result instanceof Short) { 634 return new NumericValue((double)((Short)result).shortValue()); 635 } else if (result instanceof Integer) { 636 return new NumericValue((double)((Integer)result).intValue()); 637 } else if (result instanceof Long) { 638 return new NumericValue((double)((Long)result).longValue()); 639 } else if (result instanceof Character) { 640 return new NumericValue((double)((Character)result).charValue()); 641 } else if (result instanceof Byte) { 642 return new NumericValue((double)((Byte)result).byteValue()); 643 644 } else if (result instanceof Value) { 645 return (Value)result; 646 647 } else if (result instanceof NodeInfo) { 648 return new SingletonNodeSet((NodeInfo)result); 649 } else if (result instanceof NodeEnumeration) { 650 return new NodeSetExtent((NodeEnumeration)result, 653 controller); 654 } else if (result instanceof org.w3c.dom.NodeList) { 655 NodeList list = ((NodeList)result); 656 NodeInfo[] nodes = new NodeInfo[list.getLength()]; 657 for (int i=0; i<list.getLength(); i++) { 658 if (list.item(i) instanceof NodeInfo) { 659 nodes[i] = (NodeInfo)list.item(i); 660 } else { 661 throw new XPathException("Supplied NodeList contains non-Saxon DOM Nodes"); 662 } 663 664 } 665 return new NodeSetExtent(nodes, controller); 666 } else if (result instanceof org.w3c.dom.Node) { 667 throw new XPathException("Result is a non-Saxon DOM Node"); 668 } else { 669 return new ObjectValue(result); 670 } 671 } 672 673 681 682 private int[] getConversionPreferences(Value[] argValues, Object method) { 683 684 Class[] params; 685 int firstArg; 686 687 if (method instanceof Constructor) { 688 firstArg = 0; 689 params = ((Constructor)method).getParameterTypes(); 690 } else { 691 boolean isStatic = Modifier.isStatic(((Method)method).getModifiers()); 692 firstArg = (isStatic ? 0 : 1); 693 params = ((Method)method).getParameterTypes(); 694 } 695 696 int noOfArgs = getNumberOfArguments() - firstArg; 697 int preferences[] = new int[noOfArgs]; 698 int firstParam = 0; 699 700 if (params[0] == Context.class || params[0] == XSLTContext.class) { 701 firstParam = 1; 702 } 703 704 for (int i = 0; i<noOfArgs; i++) { 705 preferences[i] = argValues[i+firstArg].conversionPreference(params[i+firstParam]); 706 } 707 708 return preferences; 709 } 710 711 712 private void setupParams(Value[] argValues, 713 Object[] params, 714 Class[] paramTypes, 715 int firstParam, 716 int firstArg) throws XPathException { 717 int j=firstParam; 718 for (int i=firstArg; i<getNumberOfArguments(); i++) { 719 params[j] = argValues[i].convertToJava(paramTypes[j]); 720 j++; 721 } 722 } 723 724 } 725 726 | Popular Tags |