001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.type.util; 020 021import java.io.ByteArrayInputStream; 022import java.io.IOException; 023import java.lang.reflect.Field; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Map.Entry; 029 030import lucee.commons.digest.MD5; 031import lucee.commons.io.IOUtil; 032import lucee.commons.io.res.Resource; 033import lucee.commons.io.res.util.ResourceUtil; 034import lucee.commons.lang.ClassUtil; 035import lucee.commons.lang.ExceptionUtil; 036import lucee.commons.lang.PhysicalClassLoader; 037import lucee.commons.lang.StringUtil; 038import lucee.commons.lang.types.RefBoolean; 039import lucee.runtime.Component; 040import lucee.runtime.ComponentSpecificAccess; 041import lucee.runtime.Mapping; 042import lucee.runtime.Page; 043import lucee.runtime.PageContext; 044import lucee.runtime.PageContextImpl; 045import lucee.runtime.PageSource; 046import lucee.runtime.PageSourceImpl; 047import lucee.runtime.component.Property; 048import lucee.runtime.config.Config; 049import lucee.runtime.engine.ThreadLocalPageContext; 050import lucee.runtime.exp.ExpressionException; 051import lucee.runtime.exp.PageException; 052import lucee.runtime.listener.AppListenerUtil; 053import lucee.runtime.net.rpc.AxisCaster; 054import lucee.runtime.net.rpc.Pojo; 055import lucee.runtime.net.rpc.server.ComponentController; 056import lucee.runtime.net.rpc.server.RPCServer; 057import lucee.runtime.op.Caster; 058import lucee.runtime.type.Array; 059import lucee.runtime.type.ArrayImpl; 060import lucee.runtime.type.Collection; 061import lucee.runtime.type.Collection.Key; 062import lucee.runtime.type.FunctionArgument; 063import lucee.runtime.type.KeyImpl; 064import lucee.runtime.type.Struct; 065import lucee.runtime.type.StructImpl; 066import lucee.runtime.type.UDF; 067import lucee.runtime.type.UDFPropertiesImpl; 068import lucee.transformer.bytecode.BytecodeContext; 069import lucee.transformer.bytecode.literal.LitString; 070import lucee.transformer.bytecode.util.ASMProperty; 071import lucee.transformer.bytecode.util.ASMPropertyImpl; 072import lucee.transformer.bytecode.util.ASMUtil; 073import lucee.transformer.bytecode.util.Types; 074import lucee.transformer.bytecode.visitor.ArrayVisitor; 075 076import org.apache.axis.AxisFault; 077import org.objectweb.asm.ClassWriter; 078import org.objectweb.asm.Label; 079import org.objectweb.asm.Opcodes; 080import org.objectweb.asm.Type; 081import org.objectweb.asm.commons.GeneratorAdapter; 082import org.objectweb.asm.commons.Method; 083 084public final class ComponentUtil { 085 086 087 private final static Method CONSTRUCTOR_OBJECT = Method.getMethod("void <init> ()"); 088 private static final Type COMPONENT_CONTROLLER = Type.getType(ComponentController.class); 089 private static final Method INVOKE = new Method("invoke",Types.OBJECT,new Type[]{Types.STRING,Types.OBJECT_ARRAY}); 090 //private static final Method INVOKE_PROPERTY = new Method("invoke",Types.OBJECT,new Type[]{Types.STRING,Types.OBJECT_ARRAY}); 091 092 /** 093 * generate a ComponentJavaAccess (CJA) class from a component 094 * a CJA is a dynamic genarted java class that has all method defined inside a component as java methods. 095 * 096 * This is used to generated server side Webservices. 097 * @param component 098 * @param isNew 099 * @return 100 * @throws PageException 101 */ 102 public static Class getComponentJavaAccess(PageContext pc,Component component, RefBoolean isNew,boolean create,boolean writeLog, boolean suppressWSbeforeArg,boolean output) throws PageException { 103 isNew.setValue(false); 104 String classNameOriginal=component.getPageSource().getFullClassName(); 105 String className=getClassname(component,null).concat("_wrap"); 106 String real=className.replace('.','/'); 107 String realOriginal=classNameOriginal.replace('.','/'); 108 Mapping mapping = component.getPageSource().getMapping(); 109 PhysicalClassLoader cl=null; 110 try { 111 cl = (PhysicalClassLoader) ((PageContextImpl)pc).getRPCClassLoader(false); 112 } catch (IOException e) { 113 throw Caster.toPageException(e); 114 } 115 Resource classFile = cl.getDirectory().getRealResource(real.concat(".class")); 116 Resource classFileOriginal = mapping.getClassRootDirectory().getRealResource(realOriginal.concat(".class")); 117 118 // LOAD CLASS 119 //print.out(className); 120 // check last Mod 121 if(classFile.lastModified()>=classFileOriginal.lastModified()) { 122 try { 123 Class clazz=cl.loadClass(className); 124 if(clazz!=null && !hasChangesOfChildren(classFile.lastModified(),clazz))return registerTypeMapping(clazz); 125 } 126 catch(Throwable t){ 127 ExceptionUtil.rethrowIfNecessary(t); 128 } 129 } 130 if(!create) return null; 131 isNew.setValue(true); 132 //print.out("new"); 133 // CREATE CLASS 134 ClassWriter cw = ASMUtil.getClassWriter(); 135 cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, real, null, "java/lang/Object", null); 136 137 //GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,Page.STATIC_CONSTRUCTOR,null,null,cw); 138 BytecodeContext statConstr = null;//new BytecodeContext(null,null,null,cw,real,ga,Page.STATIC_CONSTRUCTOR); 139 140 ///ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,Page.CONSTRUCTOR,null,null,cw); 141 BytecodeContext constr = null;//new BytecodeContext(null,null,null,cw,real,ga,Page.CONSTRUCTOR); 142 143 144 // field component 145 //FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE, "c", "Llucee/runtime/ComponentImpl;", null, null); 146 //fv.visitEnd(); 147 148 java.util.List<LitString> _keys=new ArrayList<LitString>(); 149 150 // remote methods 151 Collection.Key[] keys = ComponentProUtil.keys(component,Component.ACCESS_REMOTE); 152 int max; 153 for(int i=0;i<keys.length;i++){ 154 max=-1; 155 while((max=createMethod(statConstr,constr,_keys,cw,real,component.get(keys[i]),max, writeLog,suppressWSbeforeArg,output))!=-1){ 156 break;// for overload remove this 157 } 158 } 159 160 // Constructor 161 GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR_OBJECT,null,null,cw); 162 adapter.loadThis(); 163 adapter.invokeConstructor(Types.OBJECT, CONSTRUCTOR_OBJECT); 164 lucee.transformer.bytecode.Page.registerFields(new BytecodeContext(null,statConstr,constr,getPage(statConstr,constr),_keys,cw,real,adapter,CONSTRUCTOR_OBJECT,writeLog,suppressWSbeforeArg,output), _keys); 165 adapter.returnValue(); 166 adapter.endMethod(); 167 168 169 cw.visitEnd(); 170 byte[] barr = cw.toByteArray(); 171 172 try { 173 ResourceUtil.touch(classFile); 174 IOUtil.copy(new ByteArrayInputStream(barr), classFile,true); 175 176 cl = (PhysicalClassLoader) ((PageContextImpl)pc).getRPCClassLoader(true); 177 178 return registerTypeMapping(cl.loadClass(className, barr)); 179 } 180 catch(Throwable t) { 181 throw Caster.toPageException(t); 182 } 183 } 184 185 private static lucee.transformer.bytecode.Page getPage(BytecodeContext bc1, BytecodeContext bc2) { 186 lucee.transformer.bytecode.Page page=null; 187 if(bc1!=null)page=bc1.getPage(); 188 if(page==null && bc2!=null)page=bc2.getPage(); 189 return page; 190 } 191 192 /** 193 * check if one of the children is changed 194 * @param component 195 * @param clazz 196 * @return return true if children has changed 197 */ 198 private static boolean hasChangesOfChildren(long last, Class clazz) { 199 return hasChangesOfChildren(last,ThreadLocalPageContext.get(),clazz); 200 } 201 202 /** 203 * check if one of the children is changed 204 * @param component 205 * @param pc 206 * @param clazz 207 * @return return true if children has changed 208 */ 209 private static boolean hasChangesOfChildren(long last,PageContext pc, Class clazz) { 210 211 java.lang.reflect.Method[] methods = clazz.getMethods(); 212 java.lang.reflect.Method method; 213 Class[] params; 214 for(int i=0;i<methods.length;i++){ 215 method=methods[i]; 216 if(method.getDeclaringClass()==clazz){ 217 if(_hasChangesOfChildren(pc, last,method.getReturnType())) return true; 218 params = method.getParameterTypes(); 219 for(int y=0;y<params.length;y++){ 220 if(_hasChangesOfChildren(pc, last, params[y])) return true; 221 } 222 } 223 } 224 return false; 225 } 226 227 private static boolean _hasChangesOfChildren(PageContext pc, long last, Class clazz) { 228 clazz=ClassUtil.toComponentType(clazz); 229 java.lang.reflect.Method m = getComplexTypeMethod(clazz); 230 if(m==null) return false; 231 try { 232 String path=Caster.toString(m.invoke(null, new Object[0])); 233 Resource res = ResourceUtil.toResourceExisting(pc, path); 234 if(last<res.lastModified()) { 235 return true; 236 } 237 } 238 catch (Exception e) { 239 return true; 240 } 241 // possible that a child of the Cmplex Object is also a complex object 242 return hasChangesOfChildren(last, pc, clazz); 243 } 244 245 private static boolean isComplexType(Class clazz) { 246 return getComplexTypeMethod(clazz)!=null; 247 248 } 249 private static java.lang.reflect.Method getComplexTypeMethod(Class clazz) { 250 try { 251 return clazz.getMethod("_srcName", new Class[0]); 252 } 253 catch (Exception e) { 254 return null; 255 } 256 } 257 258 /** 259 * search in methods of a class for complex types 260 * @param clazz 261 * @return 262 */ 263 private static Class registerTypeMapping(Class clazz) throws AxisFault { 264 PageContext pc = ThreadLocalPageContext.get(); 265 RPCServer server=RPCServer.getInstance(pc.getId(),pc.getServletContext()); 266 return registerTypeMapping(server, clazz); 267 } 268 /** 269 * search in methods of a class for complex types 270 * @param server 271 * @param clazz 272 * @return 273 */ 274 private static Class registerTypeMapping(RPCServer server, Class clazz) { 275 java.lang.reflect.Method[] methods = clazz.getMethods(); 276 java.lang.reflect.Method method; 277 Class[] params; 278 for(int i=0;i<methods.length;i++){ 279 method=methods[i]; 280 if(method.getDeclaringClass()==clazz){ 281 _registerTypeMapping(server, method.getReturnType()); 282 params = method.getParameterTypes(); 283 for(int y=0;y<params.length;y++){ 284 _registerTypeMapping(server, params[y]); 285 } 286 } 287 } 288 return clazz; 289 } 290 291 /** 292 * register ComplexType 293 * @param server 294 * @param clazz 295 */ 296 private static void _registerTypeMapping(RPCServer server, Class clazz) { 297 if(clazz==null) return; 298 299 if(!isComplexType(clazz)) { 300 if(clazz.isArray()) { 301 _registerTypeMapping(server, clazz.getComponentType()); 302 } 303 return; 304 } 305 server.registerTypeMapping(clazz); 306 registerTypeMapping(server,clazz); 307 } 308 309 public static String getClassname(Component component, ASMProperty[] props) { 310 311 String prefix=""; 312 /*if(props!=null) { 313 StringBuilder sb=new StringBuilder(); 314 315 for(int i=0;i<props.length;i++){ 316 sb.append(props[i].toString()).append(';'); 317 } 318 319 320 prefix = Long.toString(HashUtil.create64BitHash(sb),Character.MAX_RADIX); 321 char c=prefix.charAt(0); 322 if(c>='0' && c<='9') prefix="a"+prefix; 323 prefix=prefix+"."; 324 }*/ 325 326 PageSource ps = component.getPageSource(); 327 return prefix+ps.getComponentName(); 328 } 329 330 /* 331 * includes the application context javasettings 332 * @param pc 333 * @param className 334 * @param properties 335 * @return 336 * @throws PageException 337 */ 338 public static Class getClientComponentPropertiesClass(PageContext pc, String className, ASMProperty[] properties, Class extendsClass) throws PageException { 339 try { 340 return _getComponentPropertiesClass(pc,pc.getConfig(), className, properties,extendsClass); 341 } catch (Exception e) { 342 throw Caster.toPageException(e); 343 } 344 } 345 /* 346 * does not include the application context javasettings 347 * @param pc 348 * @param className 349 * @param properties 350 * @return 351 * @throws PageException 352 */ 353 public static Class getComponentPropertiesClass(Config config, String className, ASMProperty[] properties,Class extendsClass) throws PageException { 354 try { 355 return _getComponentPropertiesClass(null,config, className, properties,extendsClass); 356 } catch (Exception e) { 357 throw Caster.toPageException(e); 358 } 359 } 360 361 362 363 private static Class _getComponentPropertiesClass(PageContext pc, Config secondChanceConfig, String className, ASMProperty[] properties, Class extendsClass) throws PageException, IOException, ClassNotFoundException { 364 String real=className.replace('.','/'); 365 366 PhysicalClassLoader cl; 367 if(pc==null)cl = (PhysicalClassLoader)secondChanceConfig.getRPCClassLoader(false); 368 else cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(false); 369 370 Resource rootDir = cl.getDirectory(); 371 Resource classFile = rootDir.getRealResource(real.concat(".class")); 372 373 if(classFile.exists()) { 374 try { 375 Class clazz = cl.loadClass(className); 376 Field field = clazz.getField("_md5_"); 377 if(ASMUtil.createMD5(properties).equals(field.get(null))){ 378 //if(equalInterface(properties,clazz)) { 379 return clazz; 380 } 381 } 382 catch(Exception e) { 383 384 } 385 } 386 // create file 387 if(extendsClass==null)extendsClass=Object.class; 388 byte[] barr = ASMUtil.createPojo(real, properties,extendsClass,new Class[]{Pojo.class},null); 389 boolean exist=classFile.exists(); 390 ResourceUtil.touch(classFile); 391 IOUtil.copy(new ByteArrayInputStream(barr), classFile,true); 392 393 if(pc==null)cl = (PhysicalClassLoader)secondChanceConfig.getRPCClassLoader(exist); 394 else cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(exist); 395 396 return cl.loadClass(className); 397 398 } 399 400 public static Class getComponentPropertiesClass(PageContext pc,Component component) throws PageException { 401 try { 402 return _getComponentPropertiesClass(pc,component); 403 } 404 catch (Exception e) { 405 throw Caster.toPageException(e); 406 } 407 } 408 409 private static Class _getComponentPropertiesClass(PageContext pc,Component component) throws PageException, IOException, ClassNotFoundException { 410 411 ASMProperty[] props = ASMUtil.toASMProperties(ComponentProUtil.getProperties(component, false, true, false, false)); 412 413 String className=getClassname(component,props);//StringUtil.replaceLast(classNameOriginal,"$cfc",""); 414 String real=className.replace('.','/'); 415 416 Mapping mapping = component.getPageSource().getMapping(); 417 PhysicalClassLoader cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(false); 418 419 Resource classFile = cl.getDirectory().getRealResource(real.concat(".class")); 420 421 // get component class information 422 String classNameOriginal=component.getPageSource().getFullClassName(); 423 String realOriginal=classNameOriginal.replace('.','/'); 424 Resource classFileOriginal = mapping.getClassRootDirectory().getRealResource(realOriginal.concat(".class")); 425 426 // load existing class when pojo is still newer than component class file 427 if(classFile.lastModified()>=classFileOriginal.lastModified()) { 428 try { 429 Class clazz=cl.loadClass(className); 430 if(clazz!=null && !hasChangesOfChildren(classFile.lastModified(), clazz))return clazz;//ClassUtil.loadInstance(clazz); 431 } 432 catch(Throwable t){ 433 ExceptionUtil.rethrowIfNecessary(t); 434 } 435 } 436 437 // extends 438 String strExt = component.getExtends(); 439 Class<?> ext=Object.class; 440 if(!StringUtil.isEmpty(strExt,true)) { 441 ext = Caster.cfTypeToClass(strExt); 442 } 443 // 444 // create file 445 byte[] barr = ASMUtil.createPojo(real, props,ext,new Class[]{Pojo.class},component.getPageSource().getDisplayPath()); 446 ResourceUtil.touch(classFile); 447 IOUtil.copy(new ByteArrayInputStream(barr), classFile,true); 448 cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(true); 449 return cl.loadClass(className); //ClassUtil.loadInstance(cl.loadClass(className)); 450 } 451 452 public static Class getStructPropertiesClass(PageContext pc,Struct sct, PhysicalClassLoader cl) throws PageException { 453 try { 454 return _getStructPropertiesClass(pc,sct,cl); 455 } 456 catch (Exception e) { 457 throw Caster.toPageException(e); 458 } 459 } 460 461 private static Class _getStructPropertiesClass(PageContext pc,Struct sct, PhysicalClassLoader cl) throws PageException, IOException, ClassNotFoundException { 462 // create hash based on the keys of the struct 463 String hash = StructUtil.keyHash(sct); 464 char c=hash.charAt(0); 465 if(c>='0' && c<='9') hash="a"+hash; 466 467 // create class name (struct class name + hash) 468 String className=sct.getClass().getName()+"."+hash; 469 470 // create physcal location for the file 471 String real=className.replace('.','/'); 472 Resource classFile = cl.getDirectory().getRealResource(real.concat(".class")); 473 474 // load existing class 475 if(classFile.exists()) { 476 try { 477 Class clazz=cl.loadClass(className); 478 if(clazz!=null )return clazz; 479 } 480 catch(Throwable t){ 481 ExceptionUtil.rethrowIfNecessary(t); 482 } 483 } 484 485 // Properties 486 List<ASMProperty> props=new ArrayList<ASMProperty>(); 487 Iterator<Entry<Key, Object>> it = sct.entryIterator(); 488 Entry<Key, Object> e; 489 while(it.hasNext()){ 490 e = it.next(); 491 props.add(new ASMPropertyImpl( 492 ASMUtil.toType(e.getValue()==null?Object.class:Object.class/*e.getValue().getClass()*/, true) 493 ,e.getKey().getString() 494 )); 495 } 496 497 // create file 498 byte[] barr = ASMUtil.createPojo(real, props.toArray(new ASMProperty[props.size()]) 499 ,Object.class,new Class[]{Pojo.class},null); 500 501 // create class file from bytecode 502 ResourceUtil.touch(classFile); 503 IOUtil.copy(new ByteArrayInputStream(barr), classFile,true); 504 cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(true); 505 return cl.loadClass(className); 506 } 507 508 private static int createMethod(BytecodeContext statConstr,BytecodeContext constr, java.util.List<LitString> keys,ClassWriter cw,String className, Object member,int max,boolean writeLog, boolean suppressWSbeforeArg, boolean output) throws PageException { 509 510 boolean hasOptionalArgs=false; 511 512 if(member instanceof UDF) { 513 UDF udf = (UDF) member; 514 FunctionArgument[] args = udf.getFunctionArguments(); 515 Type[] types=new Type[max<0?args.length:max]; 516 for(int y=0;y<types.length;y++){ 517 types[y]=toType(args[y].getTypeAsString(),true);//Type.getType(Caster.cfTypeToClass(args[y].getTypeAsString())); 518 if(!args[y].isRequired())hasOptionalArgs=true; 519 } 520 Type rtnType=toType(udf.getReturnTypeAsString(),true); 521 Method method = new Method( 522 udf.getFunctionName(), 523 rtnType, 524 types 525 ); 526 GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , method, null, null, cw); 527 BytecodeContext bc = new BytecodeContext(null,statConstr,constr,getPage(statConstr,constr),keys,cw,className,adapter,method,writeLog,suppressWSbeforeArg,output); 528 Label start=adapter.newLabel(); 529 adapter.visitLabel(start); 530 531 //ComponentController.invoke(name, args); 532 // name 533 adapter.push(udf.getFunctionName()); 534 535 // args 536 ArrayVisitor av=new ArrayVisitor(); 537 av.visitBegin(adapter,Types.OBJECT,types.length); 538 for(int y=0;y<types.length;y++){ 539 av.visitBeginItem(adapter, y); 540 adapter.loadArg(y); 541 av.visitEndItem(bc.getAdapter()); 542 } 543 av.visitEnd(); 544 adapter.invokeStatic(COMPONENT_CONTROLLER, INVOKE); 545 adapter.checkCast(rtnType); 546 547 //ASMConstants.NULL(adapter); 548 adapter.returnValue(); 549 Label end=adapter.newLabel(); 550 adapter.visitLabel(end); 551 552 for(int y=0;y<types.length;y++){ 553 adapter.visitLocalVariable(args[y].getName().getString(), types[y].getDescriptor(), null, start, end, y+1); 554 } 555 adapter.endMethod(); 556 557 if(hasOptionalArgs) { 558 if(max==-1)max=args.length-1; 559 else max--; 560 return max; 561 } 562 } 563 return -1; 564 } 565 566 567 568 private static Type toType(String cfType, boolean axistype) throws PageException { 569 Class clazz=Caster.cfTypeToClass(cfType); 570 if(axistype)clazz=AxisCaster.toAxisTypeClass(clazz); 571 return Type.getType(clazz); 572 573 } 574 575 576 577 public static String md5(Component c) throws IOException { 578 return md5(ComponentSpecificAccess.toComponentSpecificAccess(Component.ACCESS_PRIVATE,c)); 579 } 580 public static String md5(ComponentSpecificAccess cw) throws IOException { 581 Key[] keys = cw.keys(); 582 Arrays.sort(keys); 583 584 StringBuffer _interface=new StringBuffer(); 585 586 Object member; 587 UDF udf; 588 FunctionArgument[] args; 589 FunctionArgument arg; 590 for(int y=0;y<keys.length;y++) { 591 member = cw.get(keys[y],null); 592 if(member instanceof UDF) { 593 udf=(UDF) member; 594 //print.out(udf.); 595 _interface.append(udf.getAccess()); 596 _interface.append(udf.getOutput()); 597 _interface.append(udf.getFunctionName()); 598 _interface.append(udf.getReturnTypeAsString()); 599 args = udf.getFunctionArguments(); 600 for(int i=0;i<args.length;i++){ 601 arg=args[i]; 602 _interface.append(arg.isRequired()); 603 _interface.append(arg.getName()); 604 _interface.append(arg.getTypeAsString()); 605 } 606 } 607 } 608 return MD5.getDigestAsString(_interface.toString().toLowerCase()); 609 } 610 611 612 /** 613 * cast a strong access definition to the int type 614 * @param access access type 615 * @return int access type 616 * @throws ExpressionException 617 */ 618 public static int toIntAccess(String access) throws ExpressionException { 619 access=StringUtil.toLowerCase(access.trim()); 620 if(access.equals("package"))return Component.ACCESS_PACKAGE; 621 else if(access.equals("private"))return Component.ACCESS_PRIVATE; 622 else if(access.equals("public"))return Component.ACCESS_PUBLIC; 623 else if(access.equals("remote"))return Component.ACCESS_REMOTE; 624 throw new ExpressionException("invalid access type ["+access+"], access types are remote, public, package, private"); 625 626 } 627 628 public static int toIntAccess(String access, int defaultValue) { 629 access=StringUtil.toLowerCase(access.trim()); 630 if(access.equals("package"))return Component.ACCESS_PACKAGE; 631 else if(access.equals("private"))return Component.ACCESS_PRIVATE; 632 else if(access.equals("public"))return Component.ACCESS_PUBLIC; 633 else if(access.equals("remote"))return Component.ACCESS_REMOTE; 634 return defaultValue; 635 } 636 637 /** 638 * cast int type to string type 639 * @param access 640 * @return String access type 641 * @throws ExpressionException 642 */ 643 public static String toStringAccess(int access) throws ExpressionException { 644 String res = toStringAccess(access,null); 645 if(res!=null) return res; 646 throw new ExpressionException("invalid access type ["+access+"], access types are Component.ACCESS_PACKAGE, Component.ACCESS_PRIVATE, Component.ACCESS_PUBLIC, Component.ACCESS_REMOTE"); 647 } 648 649 public static String toStringAccess(int access,String defaultValue) { 650 switch(access) { 651 case Component.ACCESS_PACKAGE: return "package"; 652 case Component.ACCESS_PRIVATE: return "private"; 653 case Component.ACCESS_PUBLIC: return "public"; 654 case Component.ACCESS_REMOTE: return "remote"; 655 } 656 return defaultValue; 657 } 658 659 public static ExpressionException notFunction(Component c,Collection.Key key, Object member,int access) { 660 if(member==null) { 661 String strAccess = toStringAccess(access,""); 662 663 Collection.Key[] other=ComponentProUtil.keys(c,access); 664 665 if(other.length==0) 666 return new ExpressionException( 667 "component ["+c.getCallName()+"] has no "+strAccess+" function with name ["+key+"]"); 668 669 return new ExpressionException( 670 "component ["+c.getCallName()+"] has no "+strAccess+" function with name ["+key+"]", 671 "accessible functions are ["+ListUtil.arrayToList(other,",")+"]"); 672 } 673 return new ExpressionException("member ["+key+"] of component ["+c.getCallName()+"] is not a function", "Member is of type ["+Caster.toTypeName(member)+"]"); 674 } 675 676 677 678 /*public static ComponentAccess toComponentAccess(Component comp) throws ExpressionException { 679 ComponentAccess ca = toComponentAccess(comp, null); 680 if(ca!=null) return ca; 681 throw new ExpressionException("can't cast class ["+Caster.toClassName(comp)+"] to a class of type ComponentAccess"); 682 }*/ 683 684 /*public static Component toComponentAccess(Component comp, Component defaultValue) { 685 if(comp instanceof ComponentAccess) return (ComponentAccess) comp; 686 if(comp instanceof ComponentSpecificAccess) return ((ComponentSpecificAccess) comp).getComponentAccess(); 687 return defaultValue; 688 }*/ 689 690 691 692 public static Component toComponent(Object obj) throws ExpressionException { 693 if(obj instanceof Component) return (Component) obj; 694 throw new ExpressionException("can't cast class ["+Caster.toClassName(obj)+"] to a class of type Component"); 695 } 696 697 698 699 public static PageSource getPageSource(Component cfc) { 700 // TODO Auto-generated method stub 701 try { 702 return toComponent(cfc).getPageSource(); 703 } catch (ExpressionException e) { 704 return null; 705 } 706 } 707 708 public static Component getActiveComponent(PageContext pc, Component current) { 709 if(pc.getActiveComponent()==null) return current; 710 if(pc.getActiveUDF()!=null && (pc.getActiveComponent()).getPageSource()==(pc.getActiveUDF().getOwnerComponent()).getPageSource()){ 711 712 return pc.getActiveUDF().getOwnerComponent(); 713 } 714 return pc.getActiveComponent(); 715 } 716 717 public static long getCompileTime(PageContext pc, PageSource ps,long defaultValue) { 718 try { 719 return getCompileTime(pc, ps); 720 } catch (Throwable t) { 721 ExceptionUtil.rethrowIfNecessary(t); 722 return defaultValue; 723 } 724 } 725 726 public static long getCompileTime(PageContext pc, PageSource ps) throws PageException { 727 return getPage(pc,ps).getCompileTime(); 728 } 729 730 public static Page getPage(PageContext pc, PageSource ps) throws PageException { 731 PageSourceImpl psi = (PageSourceImpl)ps; 732 733 Page p = psi.getPage(); 734 if(p!=null){ 735 //print.o("getPage(existing):"+ps.getDisplayPath()+":"+psi.hashCode()+":"+p.hashCode()); 736 return p; 737 } 738 pc=ThreadLocalPageContext.get(pc); 739 return psi.loadPage(pc); 740 } 741 742 public static Struct getPropertiesAsStruct(Component c, boolean onlyPersistent) { 743 Property[] props = c.getProperties(onlyPersistent); 744 Struct sct=new StructImpl(); 745 if(props!=null)for(int i=0;i<props.length;i++){ 746 sct.setEL(KeyImpl.getInstance(props[i].getName()), props[i]); 747 } 748 return sct; 749 } 750 public static Struct getMetaData(PageContext pc,UDFPropertiesImpl udf) throws PageException { 751 StructImpl func=new StructImpl(); 752 pc=ThreadLocalPageContext.get(pc); 753 // TODO func.set("roles", value); 754 // TODO func.set("userMetadata", value); neo unterstuetzt irgendwelche a 755 // meta data 756 Struct meta = udf.meta; 757 if(meta!=null) StructUtil.copy(meta, func, true); 758 759 func.setEL(KeyConstants._closure, Boolean.FALSE); 760 761 func.set(KeyConstants._access,ComponentUtil.toStringAccess(udf.getAccess())); 762 String hint=udf.hint; 763 if(!StringUtil.isEmpty(hint))func.set(KeyConstants._hint,hint); 764 String displayname=udf.displayName; 765 if(!StringUtil.isEmpty(displayname))func.set(KeyConstants._displayname,displayname); 766 func.set(KeyConstants._name,udf.functionName); 767 func.set(KeyConstants._output,Caster.toBoolean(udf.output)); 768 func.set(KeyConstants._returntype, udf.strReturnType); 769 func.set(KeyConstants._description, udf.description==null?"":udf.description); 770 if(udf.localMode!=null)func.set("localMode", AppListenerUtil.toLocalMode(udf.localMode.intValue(), "")); 771 772 func.set(KeyConstants._owner, udf.pageSource.getDisplayPath()); 773 774 775 int format = udf.returnFormat; 776 if(format<0 || format==UDF.RETURN_FORMAT_WDDX) func.set(KeyConstants._returnFormat, "wddx"); 777 else if(format==UDF.RETURN_FORMAT_PLAIN) func.set(KeyConstants._returnFormat, "plain"); 778 else if(format==UDF.RETURN_FORMAT_JSON) func.set(KeyConstants._returnFormat, "json"); 779 else if(format==UDF.RETURN_FORMAT_SERIALIZE)func.set(KeyConstants._returnFormat, "cfml"); 780 781 782 FunctionArgument[] args = udf.arguments; 783 Array params=new ArrayImpl(); 784 //Object defaultValue; 785 Struct m; 786 //Object defaultValue; 787 for(int y=0;y<args.length;y++) { 788 StructImpl param=new StructImpl(); 789 param.set(KeyConstants._name,args[y].getName().getString()); 790 param.set(KeyConstants._required,Caster.toBoolean(args[y].isRequired())); 791 param.set(KeyConstants._type,args[y].getTypeAsString()); 792 displayname=args[y].getDisplayName(); 793 if(!StringUtil.isEmpty(displayname)) param.set(KeyConstants._displayname,displayname); 794 795 int defType = args[y].getDefaultType(); 796 if(defType==FunctionArgument.DEFAULT_TYPE_RUNTIME_EXPRESSION){ 797 param.set(KeyConstants._default, "[runtime expression]"); 798 } 799 else if(defType==FunctionArgument.DEFAULT_TYPE_LITERAL){ 800 param.set(KeyConstants._default, 801 UDFUtil.getDefaultValue(pc, udf.pageSource, udf.index, y, null)); 802 } 803 804 hint=args[y].getHint(); 805 if(!StringUtil.isEmpty(hint))param.set(KeyConstants._hint,hint); 806 // TODO func.set("userMetadata", value); neo unterstuetzt irgendwelche attr, die dann hier ausgebenen werden bloedsinn 807 808 // meta data 809 m=args[y].getMetaData(); 810 if(m!=null) StructUtil.copy(m, param, true); 811 812 params.append(param); 813 } 814 func.set(KeyConstants._parameters,params); 815 return func; 816 } 817 818 819}