001 package railo.runtime.type; 002 003 import java.io.Externalizable; 004 import java.io.IOException; 005 import java.io.ObjectInput; 006 import java.io.ObjectOutput; 007 import java.util.Iterator; 008 import java.util.Map; 009 import java.util.Map.Entry; 010 011 import javax.servlet.jsp.tagext.BodyContent; 012 013 import railo.print; 014 import railo.commons.io.cache.Cache; 015 import railo.commons.lang.CFTypes; 016 import railo.commons.lang.SizeOf; 017 import railo.commons.lang.StringUtil; 018 import railo.runtime.Component; 019 import railo.runtime.ComponentImpl; 020 import railo.runtime.Page; 021 import railo.runtime.PageContext; 022 import railo.runtime.PageContextImpl; 023 import railo.runtime.PagePlus; 024 import railo.runtime.PageSource; 025 import railo.runtime.cache.ram.RamCache; 026 import railo.runtime.component.MemberSupport; 027 import railo.runtime.config.ConfigImpl; 028 import railo.runtime.config.NullSupportHelper; 029 import railo.runtime.dump.DumpData; 030 import railo.runtime.dump.DumpProperties; 031 import railo.runtime.dump.DumpRow; 032 import railo.runtime.dump.DumpTable; 033 import railo.runtime.dump.SimpleDumpData; 034 import railo.runtime.exp.ExpressionException; 035 import railo.runtime.exp.PageException; 036 import railo.runtime.exp.UDFCasterException; 037 import railo.runtime.functions.cache.Util; 038 import railo.runtime.listener.ApplicationContextSupport; 039 import railo.runtime.op.Caster; 040 import railo.runtime.op.Decision; 041 import railo.runtime.op.Duplicator; 042 import railo.runtime.tag.util.DeprecatedUtil; 043 import railo.runtime.type.Collection.Key; 044 import railo.runtime.type.scope.Argument; 045 import railo.runtime.type.scope.ArgumentIntKey; 046 import railo.runtime.type.scope.Local; 047 import railo.runtime.type.scope.LocalImpl; 048 import railo.runtime.type.scope.Undefined; 049 import railo.runtime.type.udf.UDFCacheEntry; 050 import railo.runtime.type.util.ComponentUtil; 051 import railo.runtime.type.util.KeyConstants; 052 import railo.runtime.type.util.UDFUtil; 053 import railo.runtime.writer.BodyContentUtil; 054 055 /** 056 * defines a abstract class for a User defined Functions 057 */ 058 public class UDFImpl extends MemberSupport implements UDFPlus,Sizeable,Externalizable { 059 060 private static final FunctionArgument[] EMPTY = new FunctionArgument[0]; 061 private static final RamCache DEFAULT_CACHE=new RamCache(); 062 063 064 065 protected ComponentImpl ownerComponent; 066 protected UDFPropertiesImpl properties; 067 068 /** 069 * DO NOT USE THIS CONSTRUCTOR! 070 * this constructor is only for deserialize process 071 */ 072 public UDFImpl(){ 073 super(0); 074 } 075 076 public UDFImpl(UDFProperties properties) { 077 super(properties.getAccess()); 078 this.properties= (UDFPropertiesImpl) properties; 079 } 080 081 @Override 082 public long sizeOf() { 083 return SizeOf.size(properties); 084 } 085 086 087 public UDF duplicate(ComponentImpl c) { 088 UDFImpl udf = new UDFImpl(properties); 089 udf.ownerComponent=c; 090 udf.setAccess(getAccess()); 091 return udf; 092 } 093 094 @Override 095 public UDF duplicate(boolean deepCopy) { 096 return duplicate(ownerComponent); 097 } 098 099 @Override 100 public UDF duplicate() { 101 return duplicate(ownerComponent); 102 } 103 104 @Override 105 public Object implementation(PageContext pageContext) throws Throwable { 106 return ComponentUtil.getPage(pageContext, properties.pageSource).udfCall(pageContext,this,properties.index); 107 } 108 109 private final Object castToAndClone(PageContext pc,FunctionArgument arg,Object value, int index) throws PageException { 110 //if(value instanceof Array)print.out(count++); 111 if(Decision.isCastableTo(arg.getType(),arg.getTypeAsString(),value)) 112 return arg.isPassByReference()?value:Duplicator.duplicate(value,false); 113 throw new UDFCasterException(this,arg,value,index); 114 //REALCAST return Caster.castTo(pc,arg.getType(),arg.getTypeAsString(),value); 115 } 116 private final Object castTo(FunctionArgument arg,Object value, int index) throws PageException { 117 if(Decision.isCastableTo(arg.getType(),arg.getTypeAsString(),value)) return value; 118 throw new UDFCasterException(this,arg,value,index); 119 } 120 121 private void defineArguments(PageContext pc,FunctionArgument[] funcArgs, Object[] args,Argument newArgs) throws PageException { 122 // define argument scope 123 for(int i=0;i<funcArgs.length;i++) { 124 // argument defined 125 if(args.length>i) { 126 newArgs.setEL(funcArgs[i].getName(),castToAndClone(pc,funcArgs[i], args[i],i+1)); 127 } 128 // argument not defined 129 else { 130 Object d=getDefaultValue(pc,i,NullSupportHelper.NULL()); 131 if(d==NullSupportHelper.NULL()) { 132 if(funcArgs[i].isRequired()) { 133 throw new ExpressionException("The parameter "+funcArgs[i].getName()+" to function "+getFunctionName()+" is required but was not passed in."); 134 } 135 if(!NullSupportHelper.full()) newArgs.setEL(funcArgs[i].getName(),Argument.NULL); 136 } 137 else { 138 newArgs.setEL(funcArgs[i].getName(),castTo(funcArgs[i],d,i+1)); 139 } 140 } 141 } 142 for(int i=funcArgs.length;i<args.length;i++) { 143 newArgs.setEL(ArgumentIntKey.init(i+1),args[i]); 144 } 145 } 146 147 148 private void defineArguments(PageContext pageContext, FunctionArgument[] funcArgs, Struct values, Argument newArgs) throws PageException { 149 // argumentCollection 150 argumentCollection(values,funcArgs); 151 //print.out(values.size()); 152 Object value; 153 Collection.Key name; 154 155 for(int i=0;i<funcArgs.length;i++) { 156 // argument defined 157 name=funcArgs[i].getName(); 158 value=values.removeEL(name); 159 if(value!=null) { 160 newArgs.set(name,castToAndClone(pageContext,funcArgs[i], value,i+1)); 161 continue; 162 } 163 value=values.removeEL(ArgumentIntKey.init(i+1)); 164 if(value!=null) { 165 newArgs.set(name,castToAndClone(pageContext,funcArgs[i], value,i+1)); 166 continue; 167 } 168 169 170 // default argument or exception 171 Object defaultValue=getDefaultValue(pageContext,i,NullSupportHelper.NULL());//funcArgs[i].getDefaultValue(); 172 if(defaultValue==NullSupportHelper.NULL()) { 173 if(funcArgs[i].isRequired()) { 174 throw new ExpressionException("The parameter "+funcArgs[i].getName()+" to function "+getFunctionName()+" is required but was not passed in."); 175 } 176 newArgs.set(name,Argument.NULL); 177 } 178 else newArgs.set(name,castTo(funcArgs[i],defaultValue,i+1)); 179 } 180 181 182 Iterator<Entry<Key, Object>> it = values.entryIterator(); 183 Entry<Key, Object> e; 184 while(it.hasNext()) { 185 e = it.next(); 186 newArgs.set(e.getKey(),e.getValue()); 187 } 188 } 189 190 191 public static void argumentCollection(Struct values) { 192 argumentCollection(values,EMPTY); 193 } 194 195 public static void argumentCollection(Struct values, FunctionArgument[] funcArgs) { 196 Object value=values.removeEL(KeyConstants._argumentCollection); 197 if(value !=null) { 198 value=Caster.unwrap(value,value); 199 200 if(value instanceof Argument) { 201 Argument argColl=(Argument) value; 202 Iterator<Key> it = argColl.keyIterator(); 203 Key k; 204 int i=-1; 205 while(it.hasNext()) { 206 i++; 207 k = it.next(); 208 if(funcArgs.length>i && k instanceof ArgumentIntKey) { 209 if(!values.containsKey(funcArgs[i].getName())) 210 values.setEL(funcArgs[i].getName(),argColl.get(k,Argument.NULL)); 211 else 212 values.setEL(k,argColl.get(k,Argument.NULL)); 213 } 214 else if(!values.containsKey(k)){ 215 values.setEL(k,argColl.get(k,Argument.NULL)); 216 } 217 } 218 } 219 else if(value instanceof Collection) { 220 Collection argColl=(Collection) value; 221 //Collection.Key[] keys = argColl.keys(); 222 Iterator<Key> it = argColl.keyIterator(); 223 Key k; 224 while(it.hasNext()) { 225 k = it.next(); 226 if(!values.containsKey(k)){ 227 values.setEL(k,argColl.get(k,Argument.NULL)); 228 } 229 } 230 } 231 else if(value instanceof Map) { 232 Map map=(Map) value; 233 Iterator it = map.entrySet().iterator(); 234 Map.Entry entry; 235 Key key; 236 while(it.hasNext()) { 237 entry=(Entry) it.next(); 238 key = toKey(entry.getKey()); 239 if(!values.containsKey(key)){ 240 values.setEL(key,entry.getValue()); 241 } 242 } 243 } 244 else if(value instanceof java.util.List) { 245 java.util.List list=(java.util.List) value; 246 Iterator it = list.iterator(); 247 Object v; 248 int index=0; 249 Key k; 250 while(it.hasNext()) { 251 v= it.next(); 252 k=ArgumentIntKey.init(++index); 253 if(!values.containsKey(k)){ 254 values.setEL(k,v); 255 } 256 } 257 } 258 else { 259 values.setEL(KeyConstants._argumentCollection,value); 260 } 261 } 262 } 263 264 public static Collection.Key toKey(Object obj) { 265 if(obj==null) return null; 266 if(obj instanceof Collection.Key) return (Collection.Key) obj; 267 String str = Caster.toString(obj,null); 268 if(str==null) return KeyImpl.init(obj.toString()); 269 return KeyImpl.init(str); 270 } 271 272 @Override 273 public Object callWithNamedValues(PageContext pc, Struct values,boolean doIncludePath) throws PageException { 274 return this.properties.cachedWithin>0? 275 _callCachedWithin(pc, null, values, doIncludePath): 276 _call(pc, null, values, doIncludePath); 277 } 278 279 @Override 280 public Object call(PageContext pc, Object[] args, boolean doIncludePath) throws PageException { 281 return this.properties.cachedWithin>0? 282 _callCachedWithin(pc, args,null, doIncludePath): 283 _call(pc, args,null, doIncludePath); 284 } 285 // private static int count=0; 286 287 288 289 private Object _callCachedWithin(PageContext pc, Object[] args, Struct values,boolean doIncludePath) throws PageException { 290 PageContextImpl pci=(PageContextImpl) pc; 291 String id = UDFUtil.callerHash(this,args,values); 292 293 Cache cache = Util.getDefault(pc,ConfigImpl.CACHE_DEFAULT_FUNCTION,DEFAULT_CACHE); 294 Object o = cache.getValue(id,null); 295 296 // get from cache 297 if(o instanceof UDFCacheEntry ) { 298 UDFCacheEntry entry = (UDFCacheEntry)o; 299 //if(entry.creationdate+properties.cachedWithin>=System.currentTimeMillis()) { 300 try { 301 pc.write(entry.output); 302 } catch (IOException e) { 303 throw Caster.toPageException(e); 304 } 305 return entry.returnValue; 306 //} 307 308 //cache.remove(id); 309 } 310 311 // execute the function 312 BodyContent bc = pci.pushBody(); 313 314 try { 315 Object rtn = _call(pci, args, values, doIncludePath); 316 String out = bc.getString(); 317 cache.put(id, new UDFCacheEntry(out, rtn),properties.cachedWithin,properties.cachedWithin); 318 return rtn; 319 } 320 finally { 321 BodyContentUtil.flushAndPop(pc,bc); 322 } 323 } 324 325 private Object _call(PageContext pc, Object[] args, Struct values,boolean doIncludePath) throws PageException { 326 327 //print.out(count++); 328 PageContextImpl pci=(PageContextImpl) pc; 329 Argument newArgs= pci.getScopeFactory().getArgumentInstance(); 330 newArgs.setFunctionArgumentNames(properties.argumentsSet); 331 LocalImpl newLocal=pci.getScopeFactory().getLocalInstance(); 332 333 Undefined undefined=pc.undefinedScope(); 334 Argument oldArgs=pc.argumentsScope(); 335 Local oldLocal=pc.localScope(); 336 337 pc.setFunctionScopes(newLocal,newArgs); 338 339 int oldCheckArgs=undefined.setMode(properties.localMode==null?pc.getApplicationContext().getLocalMode():properties.localMode.intValue()); 340 PageSource psInc=null; 341 try { 342 PageSource ps = getPageSource(); 343 if(doIncludePath)psInc = ps; 344 //if(!ps.getDisplayPath().endsWith("Dump.cfc"))print.e(getPageSource().getDisplayPath()); 345 if(doIncludePath && getOwnerComponent()!=null) { 346 //if(!ps.getDisplayPath().endsWith("Dump.cfc"))print.ds(ps.getDisplayPath()); 347 psInc=ComponentUtil.getPageSource(getOwnerComponent()); 348 if(psInc==pci.getCurrentTemplatePageSource()) { 349 psInc=null; 350 } 351 352 } 353 pci.addPageSource(ps,psInc); 354 pci.addUDF(this); 355 356 ////////////////////////////////////////// 357 BodyContent bc=null; 358 Boolean wasSilent=null; 359 boolean bufferOutput=getBufferOutput(pci); 360 if(!getOutput()) { 361 if(bufferOutput) bc = pci.pushBody(); 362 else wasSilent=pc.setSilent()?Boolean.TRUE:Boolean.FALSE; 363 } 364 365 UDF parent=null; 366 if(ownerComponent!=null) { 367 parent=pci.getActiveUDF(); 368 pci.setActiveUDF(this); 369 } 370 Object returnValue = null; 371 372 try { 373 374 if(args!=null) defineArguments(pc,getFunctionArguments(),args,newArgs); 375 else defineArguments(pc,getFunctionArguments(),values,newArgs); 376 377 returnValue=implementation(pci); 378 if(ownerComponent!=null)pci.setActiveUDF(parent); 379 } 380 catch(Throwable t) { 381 if(ownerComponent!=null)pci.setActiveUDF(parent); 382 if(!getOutput()) { 383 if(bufferOutput)BodyContentUtil.flushAndPop(pc,bc); 384 else if(!wasSilent)pc.unsetSilent(); 385 } 386 //BodyContentUtil.flushAndPop(pc,bc); 387 throw Caster.toPageException(t); 388 } 389 if(!getOutput()) { 390 if(bufferOutput)BodyContentUtil.clearAndPop(pc,bc); 391 else if(!wasSilent)pc.unsetSilent(); 392 } 393 //BodyContentUtil.clearAndPop(pc,bc); 394 395 396 397 398 if(properties.returnType==CFTypes.TYPE_ANY) return returnValue; 399 else if(Decision.isCastableTo(properties.strReturnType,returnValue,false,false,-1)) return returnValue; 400 else throw new UDFCasterException(this,properties.strReturnType,returnValue); 401 //REALCAST return Caster.castTo(pageContext,returnType,returnValue,false); 402 ////////////////////////////////////////// 403 404 } 405 finally { 406 pc.removeLastPageSource(psInc!=null); 407 pci.removeUDF(); 408 pci.setFunctionScopes(oldLocal,oldArgs); 409 undefined.setMode(oldCheckArgs); 410 pci.getScopeFactory().recycle(newArgs); 411 pci.getScopeFactory().recycle(newLocal); 412 } 413 } 414 415 @Override 416 public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) { 417 return toDumpData(pageContext, maxlevel, dp,this,false); 418 } 419 public static DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp,UDF udf, boolean closure) { 420 421 if(!dp.getShowUDFs()) 422 return new SimpleDumpData(closure?"<Closure>":"<UDF>"); 423 424 // arguments 425 FunctionArgument[] args = udf.getFunctionArguments(); 426 427 DumpTable atts = closure?new DumpTable("udf","#ff00ff","#ffccff","#000000"):new DumpTable("udf","#cc66ff","#ffccff","#000000"); 428 429 atts.appendRow(new DumpRow(63,new DumpData[]{new SimpleDumpData("label"),new SimpleDumpData("name"),new SimpleDumpData("required"),new SimpleDumpData("type"),new SimpleDumpData("default"),new SimpleDumpData("hint")})); 430 for(int i=0;i<args.length;i++) { 431 FunctionArgument arg=args[i]; 432 DumpData def; 433 try { 434 Object oa=null; 435 try { 436 oa = UDFUtil.getDefaultValue(pageContext, (UDFPlus)udf, i, null);//udf.getDefaultValue(pageContext,i,null); 437 } catch (PageException e1) { 438 } 439 if(oa==null)oa="null"; 440 def=new SimpleDumpData(Caster.toString(oa)); 441 } catch (PageException e) { 442 def=new SimpleDumpData(""); 443 } 444 atts.appendRow(new DumpRow(0,new DumpData[]{ 445 new SimpleDumpData(arg.getDisplayName()), 446 new SimpleDumpData(arg.getName().getString()), 447 new SimpleDumpData(arg.isRequired()), 448 new SimpleDumpData(arg.getTypeAsString()), 449 def, 450 new SimpleDumpData(arg.getHint())})); 451 //atts.setRow(0,arg.getHint()); 452 453 } 454 455 DumpTable func = closure?new DumpTable("#ff00ff","#ffccff","#000000"):new DumpTable("#cc66ff","#ffccff","#000000"); 456 if(closure) func.setTitle("Closure"); 457 else { 458 String f="Function "; 459 try { 460 f=StringUtil.ucFirst(ComponentUtil.toStringAccess(udf.getAccess()).toLowerCase())+" "+f; 461 } 462 catch (ExpressionException e) {} 463 func.setTitle(f+udf.getFunctionName()); 464 } 465 466 if(udf instanceof UDFImpl)func.setComment("source:"+((UDFImpl)udf).getPageSource().getDisplayPath()); 467 468 if(!StringUtil.isEmpty(udf.getDescription()))func.setComment(udf.getDescription()); 469 470 func.appendRow(1,new SimpleDumpData("arguments"),atts); 471 func.appendRow(1,new SimpleDumpData("return type"),new SimpleDumpData(udf.getReturnTypeAsString())); 472 473 boolean hasLabel=!StringUtil.isEmpty(udf.getDisplayName());//displayName!=null && !displayName.equals(""); 474 boolean hasHint=!StringUtil.isEmpty(udf.getHint());//hint!=null && !hint.equals(""); 475 476 if(hasLabel || hasHint) { 477 DumpTable box = new DumpTable("#ffffff","#cccccc","#000000"); 478 box.setTitle(hasLabel?udf.getDisplayName():udf.getFunctionName()); 479 if(hasHint)box.appendRow(0,new SimpleDumpData(udf.getHint())); 480 box.appendRow(0,func); 481 return box; 482 } 483 return func; 484 } 485 486 @Override 487 public String getDisplayName() { 488 return properties.displayName; 489 } 490 491 @Override 492 public String getHint() { 493 return properties.hint; 494 } 495 496 @Override 497 public PageSource getPageSource() { 498 return properties.pageSource; 499 } 500 501 public Struct getMeta() { 502 return properties.meta; 503 } 504 505 @Override 506 public Struct getMetaData(PageContext pc) throws PageException { 507 return ComponentUtil.getMetaData(pc, properties); 508 //return getMetaData(pc, this); 509 } 510 511 @Override 512 public Object getValue() { 513 return this; 514 } 515 516 517 /** 518 * @param componentImpl the componentImpl to set 519 * @param injected 520 */ 521 public void setOwnerComponent(ComponentImpl component) { 522 this.ownerComponent = component; 523 } 524 525 @Override 526 public Component getOwnerComponent() { 527 return ownerComponent;//+++ 528 } 529 530 @Override 531 public String toString() { 532 StringBuffer sb=new StringBuffer(properties.functionName); 533 sb.append("("); 534 int optCount=0; 535 for(int i=0;i<properties.arguments.length;i++) { 536 if(i>0)sb.append(", "); 537 if(!properties.arguments[i].isRequired()){ 538 sb.append("["); 539 optCount++; 540 } 541 sb.append(properties.arguments[i].getTypeAsString()); 542 sb.append(" "); 543 sb.append(properties.arguments[i].getName()); 544 } 545 for(int i=0;i<optCount;i++){ 546 sb.append("]"); 547 } 548 sb.append(")"); 549 return sb.toString(); 550 } 551 552 @Override 553 public Boolean getSecureJson() { 554 return properties.secureJson; 555 } 556 557 @Override 558 public Boolean getVerifyClient() { 559 return properties.verifyClient; 560 } 561 562 @Override 563 public Object clone() { 564 return duplicate(); 565 } 566 567 @Override 568 public FunctionArgument[] getFunctionArguments() { 569 return properties.arguments; 570 } 571 572 @Override 573 public Object getDefaultValue(PageContext pc,int index) throws PageException { 574 return UDFUtil.getDefaultValue(pc,properties.pageSource,properties.index,index,null); 575 } 576 577 @Override 578 public Object getDefaultValue(PageContext pc,int index, Object defaultValue) throws PageException { 579 return UDFUtil.getDefaultValue(pc,properties.pageSource,properties.index,index,defaultValue); 580 } 581 // public abstract Object getDefaultValue(PageContext pc,int index) throws PageException; 582 583 @Override 584 public String getFunctionName() { 585 return properties.functionName; 586 } 587 588 @Override 589 public boolean getOutput() { 590 return properties.output; 591 } 592 593 public Boolean getBufferOutput() { 594 return properties.bufferOutput; 595 } 596 597 private boolean getBufferOutput(PageContextImpl pc) {// FUTURE move to interface 598 if(properties.bufferOutput!=null) 599 return properties.bufferOutput.booleanValue(); 600 return ((ApplicationContextSupport)pc.getApplicationContext()).getBufferOutput(); 601 } 602 603 @Override 604 public int getReturnType() { 605 return properties.returnType; 606 } 607 608 @Override 609 public String getReturnTypeAsString() { 610 return properties.strReturnType; 611 } 612 613 @Override 614 public String getDescription() { 615 return properties.description; 616 } 617 618 @Override 619 public int getReturnFormat() { 620 return properties.returnFormat; 621 } 622 623 public final String getReturnFormatAsString() { 624 return properties.strReturnFormat; 625 } 626 627 628 public static int toReturnFormat(String returnFormat) throws ExpressionException { 629 if(StringUtil.isEmpty(returnFormat,true)) 630 return UDF.RETURN_FORMAT_WDDX; 631 632 633 returnFormat=returnFormat.trim().toLowerCase(); 634 if("wddx".equals(returnFormat)) return UDF.RETURN_FORMAT_WDDX; 635 else if("json".equals(returnFormat)) return UDF.RETURN_FORMAT_JSON; 636 else if("plain".equals(returnFormat)) return UDF.RETURN_FORMAT_PLAIN; 637 else if("text".equals(returnFormat)) return UDF.RETURN_FORMAT_PLAIN; 638 else if("serialize".equals(returnFormat)) return UDF.RETURN_FORMAT_SERIALIZE; 639 else if("cfml".equals(returnFormat)) return UDF.RETURN_FORMAT_SERIALIZE; 640 else if("cfm".equals(returnFormat)) return UDF.RETURN_FORMAT_SERIALIZE; 641 else throw new ExpressionException("invalid returnFormat definition ["+returnFormat+"], valid values are [wddx,plain,json,cfml]"); 642 } 643 644 public static String toReturnFormat(int returnFormat) throws ExpressionException { 645 if(RETURN_FORMAT_WDDX==returnFormat) return "wddx"; 646 else if(RETURN_FORMAT_JSON==returnFormat) return "json"; 647 else if(RETURN_FORMAT_PLAIN==returnFormat) return "plain"; 648 else if(RETURN_FORMAT_SERIALIZE==returnFormat) return "cfml"; 649 else throw new ExpressionException("invalid returnFormat definition, valid values are [wddx,plain,json,cfml]"); 650 } 651 652 public static String toReturnFormat(int returnFormat,String defaultValue) { 653 if(RETURN_FORMAT_WDDX==returnFormat) return "wddx"; 654 else if(RETURN_FORMAT_JSON==returnFormat) return "json"; 655 else if(RETURN_FORMAT_PLAIN==returnFormat) return "plain"; 656 else if(RETURN_FORMAT_SERIALIZE==returnFormat) return "cfml"; 657 else return defaultValue; 658 } 659 660 @Override 661 public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 662 // access 663 setAccess(in.readInt()); 664 665 // properties 666 properties=(UDFPropertiesImpl) in.readObject(); 667 } 668 669 @Override 670 public void writeExternal(ObjectOutput out) throws IOException { 671 // access 672 out.writeInt(getAccess()); 673 674 // properties 675 out.writeObject(properties); 676 } 677 678 @Override 679 public boolean equals(Object obj){ 680 if(!(obj instanceof UDF)) return false; 681 return equals(this,(UDF)obj); 682 } 683 public static boolean equals(UDF left, UDF right){ 684 if( 685 !left.getPageSource().equals(right.getPageSource()) 686 || !_eq(left.getFunctionName(),right.getFunctionName()) 687 || left.getAccess()!=right.getAccess() 688 || !_eq(left.getFunctionName(),right.getFunctionName()) 689 || left.getOutput()!=right.getOutput() 690 || left.getReturnFormat()!=right.getReturnFormat() 691 || left.getReturnType()!=right.getReturnType() 692 || !_eq(left.getReturnTypeAsString(),right.getReturnTypeAsString()) 693 || !_eq(left.getSecureJson(),right.getSecureJson()) 694 || !_eq(left.getVerifyClient(),right.getVerifyClient()) 695 ) return false; 696 697 // Arguments 698 FunctionArgument[] largs = left.getFunctionArguments(); 699 FunctionArgument[] rargs = right.getFunctionArguments(); 700 if(largs.length!=rargs.length) return false; 701 for(int i=0;i<largs.length;i++){ 702 if(!largs[i].equals(rargs[i]))return false; 703 } 704 705 706 707 708 return true; 709 } 710 711 private static boolean _eq(Object left, Object right) { 712 if(left==null) return right==null; 713 return left.equals(right); 714 } 715 716 public int getIndex(){ 717 return properties.index; 718 } 719 720 } 721