1 package net.sf.saxon.functions; 2 import net.sf.saxon.Configuration; 3 import net.sf.saxon.expr.*; 4 import net.sf.saxon.om.*; 5 import net.sf.saxon.pattern.AnyNodeTest; 6 import net.sf.saxon.trans.DynamicError; 7 import net.sf.saxon.trans.XPathException; 8 import net.sf.saxon.type.AnyItemType; 9 import net.sf.saxon.type.ExternalObjectType; 10 import net.sf.saxon.type.ItemType; 11 import net.sf.saxon.type.Type; 12 import net.sf.saxon.value.*; 13 14 import javax.xml.transform.Source ; 15 import java.io.IOException ; 16 import java.io.ObjectInputStream ; 17 import java.io.ObjectOutputStream ; 18 import java.io.Serializable ; 19 import java.lang.reflect.*; 20 import java.util.List ; 21 import java.math.BigDecimal ; 22 23 24 25 32 33 public class ExtensionFunctionCall extends FunctionCall { 34 35 private transient AccessibleObject theMethod; 36 private MethodRepresentation persistentMethod; 38 private Class theClass; 40 private Configuration config; 41 42 45 46 public ExtensionFunctionCall() {} 47 48 54 55 public void init(int nameCode, Class theClass, AccessibleObject object, Configuration config) { 56 setFunctionNameCode(nameCode); 57 this.theClass = theClass; 58 this.theMethod = object; 59 this.config = config; 60 } 61 62 66 67 public Expression preEvaluate(StaticContext env) { 68 return this; 69 } 70 71 72 75 76 public void checkArguments(StaticContext env) throws XPathException { 77 } 78 79 80 85 86 public int getIntrinsicDependencies() { 87 if (theMethod instanceof Method) { 88 Class [] theParameterTypes = ((Method)theMethod).getParameterTypes(); 89 if (theParameterTypes.length > 0 && theParameterTypes[0] == XPathContext.class) { 90 return StaticProperty.DEPENDS_ON_CONTEXT_ITEM | 91 StaticProperty.DEPENDS_ON_POSITION | 92 StaticProperty.DEPENDS_ON_LAST; 93 } 94 } 95 return 0; 96 } 97 98 99 105 106 public SequenceIterator iterate(XPathContext context) throws XPathException { 107 ValueRepresentation[] argValues = new ValueRepresentation[argument.length]; 108 for (int i=0; i<argValues.length; i++) { 109 argValues[i] = ExpressionTool.lazyEvaluate(argument[i], context, 1); 110 } 111 try { 112 return call(argValues, context); 113 } catch (XPathException err) { 114 String msg = err.getMessage(); 115 msg = "Error in call to extension function {" + theMethod.toString() + "}: " + msg; 116 DynamicError err2 = new DynamicError(msg, err.getException()); 117 err2.setXPathContext(context); 118 err2.setLocator(this); 119 err2.setErrorCode(err.getErrorCodeLocalPart()); 120 throw err2; 121 } 122 } 123 124 127 128 public Class getTargetClass() { 129 return theClass; 130 } 131 132 135 136 public AccessibleObject getTargetMethod() { 137 return theMethod; 138 } 139 140 141 147 148 private SequenceIterator call(ValueRepresentation[] argValues, XPathContext context) throws XPathException { 149 150 Class [] theParameterTypes; 151 152 if (theMethod instanceof Constructor) { 153 Constructor constructor = (Constructor) theMethod; 154 theParameterTypes = constructor.getParameterTypes(); 155 Object [] params = new Object [theParameterTypes.length]; 156 157 setupParams(argValues, params, theParameterTypes, 0, 0, context); 158 159 try { 160 Object result = invokeConstructor(constructor, params); 161 return asIterator(result, context); 162 } catch (InstantiationException err0) { 163 DynamicError e = new DynamicError("Cannot instantiate class", err0); 164 throw e; 165 } catch (IllegalAccessException err1) { 166 DynamicError e = new DynamicError("Constructor access is illegal", err1); 167 throw e; 168 } catch (IllegalArgumentException err2) { 169 DynamicError e = new DynamicError("Argument is of wrong type", err2); 170 throw e; 171 } catch (NullPointerException err2) { 172 DynamicError e = new DynamicError("Object is null"); 173 throw e; 174 } catch (InvocationTargetException err3) { 175 Throwable ex = err3.getTargetException(); 176 if (ex instanceof XPathException) { 177 throw (XPathException) ex; 178 } else { 179 if (context.getController().isTracing() || 180 context.getController().getConfiguration().isTraceExternalFunctions()) { 181 err3.getTargetException().printStackTrace(); 182 } 183 DynamicError e = new DynamicError("Exception in extension function: " + 184 err3.getTargetException().toString(), ex); 185 throw e; 186 } 187 } 188 } else if (theMethod instanceof Method) { 189 Method method = (Method) theMethod; 190 boolean isStatic = Modifier.isStatic(method.getModifiers()); 191 Object theInstance; 192 theParameterTypes = method.getParameterTypes(); 193 boolean usesContext = theParameterTypes.length > 0 && 194 (theParameterTypes[0] == XPathContext.class); 195 if (isStatic) { 196 theInstance = null; 197 } else { 198 if (argValues.length == 0) { 199 DynamicError e = new DynamicError("Must supply an argument for an instance-level extension function"); 200 throw e; 201 } 202 Value arg0 = Value.asValue(argValues[0]); 203 theInstance = arg0.convertToJava(theClass, context); 204 } 206 207 Object [] params = new Object [theParameterTypes.length]; 208 209 if (usesContext) { 210 params[0] = context; 211 } 212 213 setupParams(argValues, params, theParameterTypes, 214 (usesContext ? 1 : 0), 215 (isStatic ? 0 : 1), 216 context 217 ); 218 219 try { 220 Object result = invokeMethod(method, theInstance, params); 221 if (method.getReturnType().toString().equals("void")) { 223 return EmptyIterator.getInstance(); 226 } 227 return asIterator(result, context); 228 229 } catch (IllegalAccessException err1) { 230 throw new DynamicError("Method access is illegal", err1); 231 } catch (IllegalArgumentException err2) { 232 throw new DynamicError("Argument is of wrong type", err2); 233 } catch (NullPointerException err2) { 234 throw new DynamicError("Object is null", err2); 235 } catch (InvocationTargetException err3) { 236 Throwable ex = err3.getTargetException(); 237 if (ex instanceof XPathException) { 238 throw (XPathException) ex; 239 } else { 240 if (context.getController().isTracing() || 241 context.getController().getConfiguration().isTraceExternalFunctions()) { 242 err3.getTargetException().printStackTrace(); 243 } 244 throw new DynamicError("Exception in extension function " + 245 err3.getTargetException().toString(), ex); 246 } 247 } 248 } else if (theMethod instanceof Field) { 249 250 252 Field field = (Field) theMethod; 253 boolean isStatic = Modifier.isStatic(field.getModifiers()); 254 Object theInstance; 255 if (isStatic) { 256 theInstance = null; 257 } else { 258 if (argValues.length == 0) { 259 DynamicError e = new DynamicError("Must supply an argument for an instance-level extension function"); 260 throw e; 261 } 262 Value arg0 = Value.asValue(argValues[0]); 263 theInstance = arg0.convertToJava(theClass, context); 264 } 266 267 try { 268 Object result = getField(field, theInstance); 269 return asIterator(result, context); 270 271 } catch (IllegalAccessException err1) { 272 DynamicError e = new DynamicError("Field access is illegal", err1); 273 throw e; 274 } catch (IllegalArgumentException err2) { 275 DynamicError e = new DynamicError("Argument is of wrong type", err2); 276 throw e; 277 } 278 } else { 279 throw new AssertionError ("property " + theMethod + " is neither constructor, method, nor field"); 280 } 281 282 } 283 284 292 293 private SequenceIterator asIterator(Object result, XPathContext context) throws XPathException { 294 if (result == null) { 295 return EmptyIterator.getInstance(); 296 } 297 if (result instanceof SequenceIterator) { 298 return (SequenceIterator) result; 299 } 300 if (result instanceof Value) { 301 return ((Value) result).iterate(null); 302 } 303 if (result instanceof NodeInfo) { 304 return SingletonIterator.makeIterator(((NodeInfo) result)); 305 } 306 Value actual = Value.convertJavaObjectToXPath( 307 result, SequenceType.ANY_SEQUENCE, context.getController().getConfiguration()); 308 return actual.iterate(context); 309 } 310 311 322 323 private void setupParams(ValueRepresentation[] argValues, 324 Object [] params, 325 Class [] paramTypes, 326 int firstParam, 327 int firstArg, 328 XPathContext context) throws XPathException { 329 int j = firstParam; 330 for (int i = firstArg; i < argValues.length; i++) { 331 argValues[i] = Value.asValue(argValues[i]); 332 params[j] = ((Value)argValues[i]).convertToJava(paramTypes[j], context); 333 j++; 334 } 335 } 336 337 348 349 public ItemType getItemType() { 350 return convertClassToType(getReturnClass()); 351 } 352 353 private ItemType convertClassToType(Class resultClass) { 354 if (resultClass==null || resultClass==Value.class) { 355 return AnyItemType.getInstance(); 356 } else if (resultClass.toString().equals("void")) { 357 return AnyItemType.getInstance(); 358 } else if (resultClass==String .class || resultClass==StringValue.class) { 359 return Type.STRING_TYPE; 360 } else if (resultClass==Boolean .class || resultClass==boolean.class || resultClass == BooleanValue.class) { 361 return Type.BOOLEAN_TYPE; 362 } else if (resultClass==Double .class || resultClass==double.class || resultClass==DoubleValue.class) { 363 return Type.DOUBLE_TYPE; 364 } else if (resultClass==Float .class || resultClass==float.class || resultClass==FloatValue.class) { 365 return Type.FLOAT_TYPE; 366 } else if (resultClass==Long .class || resultClass==long.class || 367 resultClass==IntegerValue.class || resultClass==BigIntegerValue.class || 368 resultClass==Integer .class || resultClass==int.class || 369 resultClass==Short .class || resultClass==short.class || 370 resultClass==Byte .class || resultClass==byte.class ) { 371 return Type.INTEGER_TYPE; 372 } else if (resultClass == BigDecimal .class) { 373 return Type.DECIMAL_TYPE; 374 } else if (Value.class.isAssignableFrom(resultClass) || 375 SequenceIterator.class.isAssignableFrom(resultClass)) { 376 return AnyItemType.getInstance(); 377 378 } else { 379 List externalObjectModels = config.getExternalObjectModels(); 381 for (int m=0; m<externalObjectModels.size(); m++) { 382 ExternalObjectModel model = (ExternalObjectModel)externalObjectModels.get(m); 383 if (model.isRecognizedNodeClass(resultClass)) { 384 return AnyNodeTest.getInstance(); 385 } 386 } 387 } 388 389 if ( NodeInfo.class.isAssignableFrom(resultClass) || 390 Source .class.isAssignableFrom(resultClass)) { 391 return AnyNodeTest.getInstance(); 392 } else if (List .class.isAssignableFrom(resultClass)) { 394 return AnyItemType.getInstance(); 395 } else if (resultClass.isArray()) { 396 Class component = resultClass.getComponentType(); 397 return convertClassToType(component); 398 } else { 399 return new ExternalObjectType(resultClass); 400 } 401 } 402 403 public int computeCardinality() { 404 Class resultClass = getReturnClass(); 405 if (resultClass==null) { 406 return StaticProperty.ALLOWS_ZERO_OR_MORE; 408 } 409 if (Value.class.isAssignableFrom(resultClass) || 410 SequenceIterator.class.isAssignableFrom(resultClass) || 411 List .class.isAssignableFrom(resultClass) || 412 Closure.class.isAssignableFrom(resultClass)|| 413 Source .class.isAssignableFrom(resultClass) || 414 resultClass.isArray()) { 415 return StaticProperty.ALLOWS_ZERO_OR_MORE; 416 } 417 List models = config.getExternalObjectModels(); 418 for (int m=0; m<models.size(); m++) { 419 ExternalObjectModel model = (ExternalObjectModel)models.get(m); 420 if (model.isRecognizedNodeClass(resultClass)) { 421 return StaticProperty.ALLOWS_ZERO_OR_ONE; 422 } else if (model.isRecognizedNodeListClass(resultClass)) { 423 return StaticProperty.ALLOWS_ZERO_OR_MORE; 424 } 425 } 426 if (resultClass.isPrimitive()) { 427 if (resultClass.equals(Void.TYPE)) { 428 return StaticProperty.ALLOWS_ZERO_OR_ONE; 431 } else { 432 return StaticProperty.EXACTLY_ONE; 434 } 435 } else { 436 return StaticProperty.ALLOWS_ZERO_OR_ONE; 437 } 438 } 439 440 444 445 private Class getReturnClass() { 446 if (theMethod instanceof Method) { 447 return ((Method)theMethod).getReturnType(); 448 } else if (theMethod instanceof Field) { 449 return ((Field)theMethod).getType(); 450 } else if (theMethod instanceof Constructor) { 451 return theClass; 452 } else { 453 return null; 455 } 456 } 457 458 461 462 public boolean usesFocus() { if (theMethod instanceof Method) { 464 Class [] theParameterTypes = ((Method)theMethod).getParameterTypes(); 465 return theParameterTypes.length > 0 && (theParameterTypes[0] == XPathContext.class); 466 } else { 467 return false; 468 } 469 } 470 471 482 483 protected Object invokeConstructor(Constructor constructor, Object [] params) 484 throws java.lang.InstantiationException , 485 java.lang.IllegalAccessException , 486 java.lang.reflect.InvocationTargetException { 487 return constructor.newInstance(params); 488 } 489 490 502 503 protected Object invokeMethod(Method method, Object instance, Object [] params) 504 throws java.lang.IllegalAccessException , 505 java.lang.reflect.InvocationTargetException { 506 return method.invoke(instance, params); 507 } 508 509 518 519 protected Object getField(Field field, Object instance) 520 throws java.lang.IllegalAccessException { 521 return field.get(instance); 522 } 523 524 527 528 private void writeObject(ObjectOutputStream s) throws IOException { 529 persistentMethod = new MethodRepresentation(theClass, theMethod); 530 s.defaultWriteObject(); 531 } 532 533 536 537 private void readObject(ObjectInputStream s) throws IOException { 538 try { 539 s.defaultReadObject(); 540 theMethod = persistentMethod.recoverAccessibleObject(); 541 } catch (Exception e) { 542 throw new IOException ("Failed to read compiled representation of extension function call to " + theClass.getClass()); 543 } 544 } 545 546 547 552 553 private static class MethodRepresentation implements Serializable { 554 private Class theClass; 555 private byte category; private String name; private Class [] params; 559 public MethodRepresentation(Class theClass, AccessibleObject obj) { 560 this.theClass = theClass; 561 if (obj instanceof Method) { 562 category = 0; 563 name = ((Method)obj).getName(); 564 params = ((Method)obj).getParameterTypes(); 565 } else if (obj instanceof Constructor) { 566 category = 1; 567 params = ((Constructor)obj).getParameterTypes(); 568 } else { 569 category = 2; 570 name = ((Field)obj).getName(); 571 } 572 } 573 574 public AccessibleObject recoverAccessibleObject() throws NoSuchMethodException , NoSuchFieldException { 575 switch (category) { 576 case 0: 577 return theClass.getMethod(name, params); 578 case 1: 579 return theClass.getConstructor(params); 580 case 2: 581 return theClass.getField(name); 582 default: 583 return null; 584 } 585 } 586 } 587 588 } 589 590 | Popular Tags |