1 package net.sf.saxon.instruct; 2 3 import net.sf.saxon.Controller; 4 import net.sf.saxon.expr.Expression; 5 import net.sf.saxon.expr.ExpressionTool; 6 import net.sf.saxon.expr.UserFunctionCall; 7 import net.sf.saxon.expr.XPathContextMajor; 8 import net.sf.saxon.om.*; 9 import net.sf.saxon.style.StandardNames; 10 import net.sf.saxon.trace.InstructionInfo; 11 import net.sf.saxon.trace.InstructionInfoProvider; 12 import net.sf.saxon.trans.XPathException; 13 import net.sf.saxon.type.Type; 14 import net.sf.saxon.value.SequenceType; 15 import net.sf.saxon.value.Value; 16 17 import java.util.HashMap ; 18 19 29 30 public final class UserFunction extends Procedure implements InstructionInfoProvider { 31 32 private int functionNameCode; 33 private boolean memoFunction = false; 34 private boolean tailRecursive = false; 35 private UserFunctionParameter[] parameterDefinitions; 38 private SequenceType resultType; 39 private transient InstructionDetails details = null; 40 41 public UserFunction() {} 42 43 public UserFunction(Expression body) { 44 setBody(body); 45 }; 46 47 public void setParameterDefinitions(UserFunctionParameter[] params) { 48 this.parameterDefinitions = params; 49 } 50 51 public UserFunctionParameter[] getParameterDefinitions() { 52 return parameterDefinitions; 53 } 54 55 public void setResultType(SequenceType resultType) { 56 this.resultType = resultType; 57 } 58 59 public void setTailRecursive(boolean tailCalls) { 60 tailRecursive = tailCalls; 61 } 62 63 68 public SequenceType getResultType() { 69 return resultType; 70 } 71 72 77 78 public SequenceType getArgumentType(int n) { 79 return parameterDefinitions[n].getRequiredType(); 80 } 81 82 86 87 public int getNumberOfArguments() { 88 return parameterDefinitions.length; 89 } 90 91 95 96 public void setMemoFunction(boolean isMemo) { 97 memoFunction = isMemo; 98 } 99 100 104 105 public void setFunctionNameCode(int nameCode) { 106 functionNameCode = nameCode; 107 } 108 109 113 114 public int getFunctionNameCode() { 115 return functionNameCode; 116 } 117 118 136 137 public ValueRepresentation call(ValueRepresentation[] actualArgs, XPathContextMajor context, boolean evaluateTailCalls) 138 throws XPathException { 139 140 Controller controller = context.getController(); 142 if (memoFunction) { 143 ValueRepresentation value = getCachedValue(controller, actualArgs); 144 if (value != null) return value; 145 } 146 147 149 context.setStackFrame(getStackFrameMap(), actualArgs); 150 ValueRepresentation result; 151 try { 152 if (tailRecursive || memoFunction) { 153 result = ExpressionTool.eagerEvaluate(getBody(), context); 158 } else { 159 result = ExpressionTool.lazyEvaluate(getBody(), context, 1); 160 } 161 } catch (XPathException err) { 162 if (err.getLocator() == null) { 163 err.setLocator(this); 164 } 165 throw err; 166 } 167 168 if (evaluateTailCalls) { 169 while (result instanceof UserFunctionCall.FunctionCallPackage) { 170 result = ((UserFunctionCall.FunctionCallPackage)result).call(); 171 } 172 } 173 174 if (memoFunction) { 176 putCachedValue(controller, actualArgs, result); 177 } 178 179 return result; 180 } 181 182 195 196 public ValueRepresentation call(ValueRepresentation[] actualArgs, Controller controller) throws XPathException { 197 return call(actualArgs, controller.newXPathContext(), true); 198 } 199 200 204 205 private ValueRepresentation getCachedValue(Controller controller, ValueRepresentation[] params) throws XPathException { 206 HashMap map = (HashMap ) controller.getUserData(this, "memo-function-cache"); 207 if (map == null) { 208 return null; 209 } 210 String key = getCombinedKey(params); 211 return (ValueRepresentation) map.get(key); 213 } 214 215 218 219 private void putCachedValue(Controller controller, ValueRepresentation[] params, ValueRepresentation value) throws XPathException { 220 HashMap map = (HashMap ) controller.getUserData(this, "memo-function-cache"); 221 if (map == null) { 222 map = new HashMap (32); 223 controller.setUserData(this, "memo-function-cache", map); 224 } 225 String key = getCombinedKey(params); 226 map.put(key, value); 227 } 228 229 232 233 private static String getCombinedKey(ValueRepresentation[] params) throws XPathException { 234 FastStringBuffer sb = new FastStringBuffer(120); 235 236 for (int i = 0; i < params.length; i++) { 237 ValueRepresentation val = params[i]; 238 SequenceIterator iter = Value.getIterator(val); 240 while (true) { 241 Item item = iter.next(); 242 if (item == null) { 243 break; 244 } 245 if (item instanceof NodeInfo) { 246 NodeInfo node = (NodeInfo) item; 247 sb.append(node.generateId()); 250 } else { 251 sb.append("" + Type.displayTypeName(item)); 252 sb.append('/'); 253 sb.append(item.getStringValueCS()); 254 } 255 sb.append('\u0001'); 256 } 257 sb.append('\u0002'); 258 } 259 return sb.toString(); 260 } 261 262 266 267 public InstructionInfo getInstructionInfo() { 268 if (details == null) { 269 details = new InstructionDetails(); 270 details.setSystemId(getSystemId()); 271 details.setLineNumber(getLineNumber()); 272 details.setConstructType(StandardNames.XSL_FUNCTION); 273 details.setObjectNameCode(functionNameCode); 274 details.setProperty("function", this); 275 } 276 return details; 277 } 278 } 279 280 281 | Popular Tags |