001 package org.apache.commons.ognl.enhance; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import javassist.CannotCompileException; 023 import javassist.ClassPool; 024 import javassist.CtClass; 025 import javassist.CtField; 026 import javassist.CtMethod; 027 import javassist.CtNewConstructor; 028 import javassist.CtNewMethod; 029 import javassist.LoaderClassPath; 030 import javassist.NotFoundException; 031 import org.apache.commons.ognl.ASTAnd; 032 import org.apache.commons.ognl.ASTChain; 033 import org.apache.commons.ognl.ASTConst; 034 import org.apache.commons.ognl.ASTCtor; 035 import org.apache.commons.ognl.ASTList; 036 import org.apache.commons.ognl.ASTMethod; 037 import org.apache.commons.ognl.ASTOr; 038 import org.apache.commons.ognl.ASTProperty; 039 import org.apache.commons.ognl.ASTRootVarRef; 040 import org.apache.commons.ognl.ASTStaticField; 041 import org.apache.commons.ognl.ASTStaticMethod; 042 import org.apache.commons.ognl.ASTThisVarRef; 043 import org.apache.commons.ognl.ASTVarRef; 044 import org.apache.commons.ognl.ClassResolver; 045 import org.apache.commons.ognl.ExpressionNode; 046 import org.apache.commons.ognl.Node; 047 import org.apache.commons.ognl.OgnlContext; 048 import org.apache.commons.ognl.OgnlRuntime; 049 050 import java.lang.reflect.Method; 051 import java.lang.reflect.Modifier; 052 import java.util.Collection; 053 import java.util.HashMap; 054 import java.util.Iterator; 055 import java.util.List; 056 import java.util.Map; 057 import java.util.Set; 058 059 import static java.lang.String.format; 060 061 /** 062 * Responsible for managing/providing functionality related to compiling generated java source expressions via bytecode 063 * enhancements for a given ognl expression. 064 */ 065 public class ExpressionCompiler 066 implements OgnlExpressionCompiler 067 { 068 069 /** 070 * Key used to store any java source string casting statements in the {@link OgnlContext} during class compilation. 071 */ 072 public static final String PRE_CAST = "_preCast"; 073 074 /** 075 * {@link ClassLoader} instances. 076 */ 077 protected Map<ClassResolver, EnhancedClassLoader> loaders = new HashMap<ClassResolver, EnhancedClassLoader>(); 078 079 /** 080 * Javassist class definition poool. 081 */ 082 protected ClassPool pool; 083 084 protected int classCounter = 0; 085 086 /** 087 * Used by {@link #castExpression(org.apache.commons.ognl.OgnlContext, org.apache.commons.ognl.Node, String)} to 088 * store the cast java source string in to the current {@link org.apache.commons.ognl.OgnlContext}. This will either 089 * add to the existing string present if it already exists or create a new instance and store it using the static 090 * key of {@link #PRE_CAST}. 091 * 092 * @param context The current execution context. 093 * @param cast The java source string to store in to the context. 094 */ 095 public static void addCastString( OgnlContext context, String cast ) 096 { 097 String value = (String) context.get( PRE_CAST ); 098 099 if ( value != null ) 100 { 101 value = cast + value; 102 } 103 else 104 { 105 value = cast; 106 } 107 108 context.put( PRE_CAST, value ); 109 } 110 111 /** 112 * Returns the appropriate casting expression (minus parens) for the specified class type. 113 * <p/> 114 * For instance, if given an {@link Integer} object the string <code>"java.lang.Integer"</code> would be returned. 115 * For an array of primitive ints <code>"int[]"</code> and so on.. 116 * </p> 117 * 118 * @param type The class to cast a string expression for. 119 * @return The converted raw string version of the class name. 120 */ 121 public static String getCastString( Class<?> type ) 122 { 123 if ( type == null ) 124 { 125 return null; 126 } 127 128 return type.isArray() ? type.getComponentType().getName() + "[]" : type.getName(); 129 } 130 131 /** 132 * Convenience method called by many different property/method resolving AST types to get a root expression 133 * resolving string for the given node. The callers are mostly ignorant and rely on this method to properly 134 * determine if the expression should be cast at all and take the appropriate actions if it should. 135 * 136 * @param expression The node to check and generate a root expression to if necessary. 137 * @param root The root object for this execution. 138 * @param context The current execution context. 139 * @return Either an empty string or a root path java source string compatible with javassist compilations from the 140 * root object up to the specified {@link Node}. 141 */ 142 public static String getRootExpression( Node expression, Object root, OgnlContext context ) 143 { 144 String rootExpr = ""; 145 146 if ( !shouldCast( expression ) ) 147 { 148 return rootExpr; 149 } 150 151 if ( ( !ASTList.class.isInstance( expression ) && !ASTVarRef.class.isInstance( expression ) 152 && !ASTStaticMethod.class.isInstance( expression ) && !ASTStaticField.class.isInstance( expression ) 153 && !ASTConst.class.isInstance( expression ) && !ExpressionNode.class.isInstance( expression ) 154 && !ASTCtor.class.isInstance( expression ) && !ASTStaticMethod.class.isInstance( expression ) 155 && root != null ) || ( root != null && ASTRootVarRef.class.isInstance( expression ) ) ) 156 { 157 158 Class<?> castClass = OgnlRuntime.getCompiler( context ).getRootExpressionClass( expression, context ); 159 160 if ( castClass.isArray() || ASTRootVarRef.class.isInstance( expression ) || ASTThisVarRef.class.isInstance( 161 expression ) ) 162 { 163 rootExpr = "((" + getCastString( castClass ) + ")$2)"; 164 165 if ( ASTProperty.class.isInstance( expression ) && !( (ASTProperty) expression ).isIndexedAccess() ) 166 { 167 rootExpr += "."; 168 } 169 } 170 else if ( ( ASTProperty.class.isInstance( expression ) && ( (ASTProperty) expression ).isIndexedAccess() ) 171 || ASTChain.class.isInstance( expression ) ) 172 { 173 rootExpr = "((" + getCastString( castClass ) + ")$2)"; 174 } 175 else 176 { 177 rootExpr = "((" + getCastString( castClass ) + ")$2)."; 178 } 179 } 180 181 return rootExpr; 182 } 183 184 /** 185 * Used by {@link #getRootExpression(org.apache.commons.ognl.Node, Object, org.apache.commons.ognl.OgnlContext)} to 186 * determine if the expression needs to be cast at all. 187 * 188 * @param expression The node to check against. 189 * @return Yes if the node type should be cast - false otherwise. 190 */ 191 public static boolean shouldCast( Node expression ) 192 { 193 if ( ASTChain.class.isInstance( expression ) ) 194 { 195 Node child = expression.jjtGetChild( 0 ); 196 if ( ASTConst.class.isInstance( child ) || ASTStaticMethod.class.isInstance( child ) 197 || ASTStaticField.class.isInstance( child ) || ( ASTVarRef.class.isInstance( child ) 198 && !ASTRootVarRef.class.isInstance( child ) ) ) 199 { 200 return false; 201 } 202 } 203 204 return !ASTConst.class.isInstance( expression ); 205 } 206 207 /** 208 * {@inheritDoc} 209 */ 210 public String castExpression( OgnlContext context, Node expression, String body ) 211 { 212 //TODO: ok - so this looks really f-ed up ...and it is ..eh if you can do it better I'm all for it :) 213 214 if ( context.getCurrentAccessor() == null || context.getPreviousType() == null 215 || context.getCurrentAccessor().isAssignableFrom( context.getPreviousType() ) || ( 216 context.getCurrentType() != null && context.getCurrentObject() != null 217 && context.getCurrentType().isAssignableFrom( context.getCurrentObject().getClass() ) 218 && context.getCurrentAccessor().isAssignableFrom( context.getPreviousType() ) ) || body == null 219 || body.trim().length() < 1 || ( context.getCurrentType() != null && context.getCurrentType().isArray() && ( 220 context.getPreviousType() == null || context.getPreviousType() != Object.class ) ) 221 || ASTOr.class.isInstance( expression ) || ASTAnd.class.isInstance( expression ) 222 || ASTRootVarRef.class.isInstance( expression ) || context.getCurrentAccessor() == Class.class || ( 223 context.get( ExpressionCompiler.PRE_CAST ) != null && ( (String) context.get( 224 ExpressionCompiler.PRE_CAST ) ).startsWith( "new" ) ) || ASTStaticField.class.isInstance( expression ) 225 || ASTStaticMethod.class.isInstance( expression ) || ( OrderedReturn.class.isInstance( expression ) 226 && ( (OrderedReturn) expression ).getLastExpression() != null ) ) 227 { 228 return body; 229 } 230 231 /* 232 * System.out.println("castExpression() with expression " + expression + " expr class: " + expression.getClass() 233 * + " currentType is: " + context.getCurrentType() + " previousType: " + context.getPreviousType() + 234 * "\n current Accessor: " + context.getCurrentAccessor() + " previous Accessor: " + 235 * context.getPreviousAccessor() + " current object " + context.getCurrentObject()); 236 */ 237 238 ExpressionCompiler.addCastString( context, 239 "((" + ExpressionCompiler.getCastString( context.getCurrentAccessor() ) 240 + ")" ); 241 242 return ")" + body; 243 } 244 245 /** 246 * {@inheritDoc} 247 */ 248 public String getClassName( Class<?> clazz ) 249 { 250 if ( "java.util.AbstractList$Itr".equals( clazz.getName() ) ) 251 { 252 return Iterator.class.getName(); 253 } 254 255 if ( Modifier.isPublic( clazz.getModifiers() ) && clazz.isInterface() ) 256 { 257 return clazz.getName(); 258 } 259 260 Class<?>[] interfaces = clazz.getInterfaces(); 261 262 for ( Class<?> intface : interfaces ) 263 { 264 if ( intface.getName().indexOf( "util.List" ) > 0 ) 265 { 266 return intface.getName(); 267 } 268 else if ( intface.getName().indexOf( "Iterator" ) > 0 ) 269 { 270 return intface.getName(); 271 } 272 } 273 274 if ( clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0 ) 275 { 276 return getClassName( clazz.getSuperclass() ); 277 } 278 279 return clazz.getName(); 280 } 281 282 /** 283 * {@inheritDoc} 284 */ 285 public Class<?> getSuperOrInterfaceClass( Method m, Class<?> clazz ) 286 { 287 if ( clazz.getInterfaces() != null && clazz.getInterfaces().length > 0 ) 288 { 289 Class<?>[] intfs = clazz.getInterfaces(); 290 Class<?> intClass; 291 292 for ( Class<?> intf : intfs ) 293 { 294 intClass = getSuperOrInterfaceClass( m, intf ); 295 296 if ( intClass != null ) 297 { 298 return intClass; 299 } 300 301 if ( Modifier.isPublic( intf.getModifiers() ) && containsMethod( m, intf ) ) 302 { 303 return intf; 304 } 305 } 306 } 307 308 if ( clazz.getSuperclass() != null ) 309 { 310 Class<?> superClass = getSuperOrInterfaceClass( m, clazz.getSuperclass() ); 311 312 if ( superClass != null ) 313 { 314 return superClass; 315 } 316 } 317 318 if ( Modifier.isPublic( clazz.getModifiers() ) && containsMethod( m, clazz ) ) 319 { 320 return clazz; 321 } 322 323 return null; 324 } 325 326 /** 327 * Helper utility method used by compiler to help resolve class->method mappings during method calls to 328 * {@link OgnlExpressionCompiler#getSuperOrInterfaceClass(java.lang.reflect.Method, Class)}. 329 * 330 * @param m The method to check for existance of. 331 * @param clazz The class to check for the existance of a matching method definition to the method passed in. 332 * @return True if the class contains the specified method, false otherwise. 333 */ 334 public boolean containsMethod( Method m, Class<?> clazz ) 335 { 336 Method[] methods = clazz.getMethods(); 337 338 if ( methods == null ) 339 { 340 return false; 341 } 342 343 for ( Method method : methods ) 344 { 345 if ( method.getName().equals( m.getName() ) && method.getReturnType() == m.getReturnType() ) 346 { 347 Class<?>[] parms = m.getParameterTypes(); 348 if ( parms == null ) 349 { 350 continue; 351 } 352 353 Class<?>[] mparms = method.getParameterTypes(); 354 if ( mparms == null || mparms.length != parms.length ) 355 { 356 continue; 357 } 358 359 boolean parmsMatch = true; 360 for ( int p = 0; p < parms.length; p++ ) 361 { 362 if ( parms[p] != mparms[p] ) 363 { 364 parmsMatch = false; 365 break; 366 } 367 } 368 369 if ( !parmsMatch ) 370 { 371 continue; 372 } 373 374 Class<?>[] exceptions = m.getExceptionTypes(); 375 if ( exceptions == null ) 376 { 377 continue; 378 } 379 380 Class<?>[] mexceptions = method.getExceptionTypes(); 381 if ( mexceptions == null || mexceptions.length != exceptions.length ) 382 { 383 continue; 384 } 385 386 boolean exceptionsMatch = true; 387 for ( int e = 0; e < exceptions.length; e++ ) 388 { 389 if ( exceptions[e] != mexceptions[e] ) 390 { 391 exceptionsMatch = false; 392 break; 393 } 394 } 395 396 if ( !exceptionsMatch ) 397 { 398 continue; 399 } 400 401 return true; 402 } 403 } 404 405 return false; 406 } 407 408 /** 409 * {@inheritDoc} 410 */ 411 public Class<?> getInterfaceClass( Class<?> clazz ) 412 { 413 if ( "java.util.AbstractList$Itr".equals( clazz.getName() ) ) 414 { 415 return Iterator.class; 416 } 417 418 if ( Modifier.isPublic( clazz.getModifiers() ) && clazz.isInterface() || clazz.isPrimitive() ) 419 { 420 return clazz; 421 } 422 423 Class<?>[] intf = clazz.getInterfaces(); 424 425 for ( Class<?> anIntf : intf ) 426 { 427 if ( List.class.isAssignableFrom( anIntf ) ) 428 { 429 return List.class; 430 } 431 else if ( Iterator.class.isAssignableFrom( anIntf ) ) 432 { 433 return Iterator.class; 434 } 435 else if ( Map.class.isAssignableFrom( anIntf ) ) 436 { 437 return Map.class; 438 } 439 else if ( Set.class.isAssignableFrom( anIntf ) ) 440 { 441 return Set.class; 442 } 443 else if ( Collection.class.isAssignableFrom( anIntf ) ) 444 { 445 return Collection.class; 446 } 447 } 448 449 if ( clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0 ) 450 { 451 return getInterfaceClass( clazz.getSuperclass() ); 452 } 453 454 return clazz; 455 } 456 457 /** 458 * {@inheritDoc} 459 */ 460 public Class<?> getRootExpressionClass( Node rootNode, OgnlContext context ) 461 { 462 if ( context.getRoot() == null ) 463 { 464 return null; 465 } 466 467 Class<?> ret = context.getRoot().getClass(); 468 469 if ( context.getFirstAccessor() != null && context.getFirstAccessor().isInstance( context.getRoot() ) ) 470 { 471 ret = context.getFirstAccessor(); 472 } 473 474 return ret; 475 } 476 477 /** 478 * {@inheritDoc} 479 */ 480 public void compileExpression( OgnlContext context, Node expression, Object root ) 481 throws Exception 482 { 483 // System.out.println("Compiling expr class " + expression.getClass().getName() + " and root " + root); 484 485 if ( expression.getAccessor() != null ) 486 { 487 return; 488 } 489 490 String getBody, setBody; 491 492 EnhancedClassLoader loader = getClassLoader( context ); 493 ClassPool classPool = getClassPool( context, loader ); 494 495 CtClass newClass = classPool.makeClass( 496 expression.getClass().getName() + expression.hashCode() + classCounter++ + "Accessor" ); 497 newClass.addInterface( getCtClass( ExpressionAccessor.class ) ); 498 499 CtClass ognlClass = getCtClass( OgnlContext.class ); 500 CtClass objClass = getCtClass( Object.class ); 501 502 CtMethod valueGetter = new CtMethod( objClass, "get", new CtClass[] { ognlClass, objClass }, newClass ); 503 CtMethod valueSetter = 504 new CtMethod( CtClass.voidType, "set", new CtClass[] { ognlClass, objClass, objClass }, newClass ); 505 506 CtField nodeMember = null; // will only be set if uncompilable exception is thrown 507 508 CtClass nodeClass = getCtClass( Node.class ); 509 CtMethod setExpression = null; 510 511 try 512 { 513 514 getBody = generateGetter( context, newClass, objClass, classPool, valueGetter, expression, root ); 515 516 } 517 catch ( UnsupportedCompilationException uc ) 518 { 519 // uc.printStackTrace(); 520 521 nodeMember = new CtField( nodeClass, "_node", newClass ); 522 newClass.addField( nodeMember ); 523 524 getBody = generateOgnlGetter( newClass, valueGetter, nodeMember ); 525 526 setExpression = CtNewMethod.setter( "setExpression", nodeMember ); 527 newClass.addMethod( setExpression ); 528 } 529 530 try 531 { 532 533 setBody = generateSetter( context, newClass, objClass, classPool, valueSetter, expression, root ); 534 535 } 536 catch ( UnsupportedCompilationException uc ) 537 { 538 539 // uc.printStackTrace(); 540 541 if ( nodeMember == null ) 542 { 543 nodeMember = new CtField( nodeClass, "_node", newClass ); 544 newClass.addField( nodeMember ); 545 } 546 547 setBody = generateOgnlSetter( newClass, valueSetter, nodeMember ); 548 549 if ( setExpression == null ) 550 { 551 setExpression = CtNewMethod.setter( "setExpression", nodeMember ); 552 newClass.addMethod( setExpression ); 553 } 554 } 555 556 try 557 { 558 newClass.addConstructor( CtNewConstructor.defaultConstructor( newClass ) ); 559 560 Class<?> clazz = classPool.toClass( newClass ); 561 newClass.detach(); 562 563 expression.setAccessor( (ExpressionAccessor) clazz.newInstance() ); 564 565 // need to set expression on node if the field was just defined. 566 567 if ( nodeMember != null ) 568 { 569 expression.getAccessor().setExpression( expression ); 570 } 571 572 } 573 catch ( Throwable t ) 574 { 575 // t.printStackTrace(); 576 577 throw new RuntimeException( "Error compiling expression on object " + root + " with expression node " 578 + expression + " getter body: " + getBody + " setter body: " + setBody, t ); 579 } 580 581 } 582 583 protected String generateGetter( OgnlContext context, CtClass newClass, CtClass objClass, ClassPool classPool, 584 CtMethod valueGetter, Node expression, Object root ) 585 throws Exception 586 { 587 String pre = ""; 588 String post = ""; 589 String body; 590 591 context.setRoot( root ); 592 593 // the ExpressionAccessor API has to reference the generic Object class for get/set operations, so this sets up 594 // that known 595 // type beforehand 596 597 context.remove( PRE_CAST ); 598 599 // Recursively generate the java source code representation of the top level expression 600 601 String getterCode = expression.toGetSourceString( context, root ); 602 603 if ( getterCode == null || getterCode.trim().length() <= 0 604 && !ASTVarRef.class.isAssignableFrom( expression.getClass() ) ) 605 { 606 getterCode = "null"; 607 } 608 609 String castExpression = (String) context.get( PRE_CAST ); 610 611 if ( context.getCurrentType() == null || context.getCurrentType().isPrimitive() 612 || Character.class.isAssignableFrom( context.getCurrentType() ) 613 || Object.class == context.getCurrentType() ) 614 { 615 pre = pre + " ($w) ("; 616 post = post + ")"; 617 } 618 619 String rootExpr = !"null".equals( getterCode ) ? getRootExpression( expression, root, context ) : ""; 620 621 String noRoot = (String) context.remove( "_noRoot" ); 622 if ( noRoot != null ) 623 { 624 rootExpr = ""; 625 } 626 627 createLocalReferences( context, classPool, newClass, objClass, valueGetter.getParameterTypes() ); 628 629 if ( OrderedReturn.class.isInstance( expression ) 630 && ( (OrderedReturn) expression ).getLastExpression() != null ) 631 { 632 body = "{ " + ( ASTMethod.class.isInstance( expression ) || ASTChain.class.isInstance( expression ) 633 ? rootExpr 634 : "" ) + ( castExpression != null ? castExpression : "" ) 635 + ( (OrderedReturn) expression ).getCoreExpression() + " return " + pre 636 + ( (OrderedReturn) expression ).getLastExpression() + post + ";}"; 637 638 } 639 else 640 { 641 642 body = 643 "{ return " + pre + ( castExpression != null ? castExpression : "" ) + rootExpr + getterCode + post 644 + ";}"; 645 } 646 647 body = body.replaceAll( "\\.\\.", "." ); 648 649 // System.out.println("Getter Body: ===================================\n" + body); 650 valueGetter.setBody( body ); 651 newClass.addMethod( valueGetter ); 652 653 return body; 654 } 655 656 /** 657 * {@inheritDoc} 658 */ 659 public String createLocalReference( OgnlContext context, String expression, Class<?> type ) 660 { 661 String referenceName = "ref" + context.incrementLocalReferenceCounter(); 662 context.addLocalReference( referenceName, new LocalReferenceImpl( referenceName, expression, type ) ); 663 664 String castString = ""; 665 if ( !type.isPrimitive() ) 666 { 667 castString = "(" + ExpressionCompiler.getCastString( type ) + ") "; 668 } 669 670 return castString + referenceName + "($$)"; 671 } 672 673 void createLocalReferences( OgnlContext context, ClassPool classPool, CtClass clazz, CtClass unused, 674 CtClass[] params ) 675 throws NotFoundException, CannotCompileException 676 { 677 Map<String, LocalReference> referenceMap = context.getLocalReferences(); 678 if ( referenceMap == null || referenceMap.isEmpty() ) 679 { 680 return; 681 } 682 683 Iterator<LocalReference> it = referenceMap.values().iterator(); 684 while( it.hasNext() ) 685 { 686 LocalReference ref = it.next(); 687 String widener = ref.getType().isPrimitive() ? " " : " ($w) "; 688 689 String body = format( "{ return %s %s; }", widener, ref.getExpression() ).replaceAll( "\\.\\.", "." ); 690 691 // System.out.println("adding method " + ref.getName() + " with body:\n" + body + " and return type: " + 692 // ref.getType()); 693 694 CtMethod method = 695 new CtMethod( classPool.get( getCastString( ref.getType() ) ), ref.getName(), params, clazz ); 696 method.setBody( body ); 697 698 clazz.addMethod( method ); 699 it.remove(); 700 } 701 } 702 703 protected String generateSetter( OgnlContext context, CtClass newClass, CtClass objClass, ClassPool classPool, 704 CtMethod valueSetter, Node expression, Object root ) 705 throws Exception 706 { 707 if ( ExpressionNode.class.isInstance( expression ) || ASTConst.class.isInstance( expression ) ) 708 { 709 throw new UnsupportedCompilationException( "Can't compile expression/constant setters." ); 710 } 711 712 context.setRoot( root ); 713 context.remove( PRE_CAST ); 714 715 String body; 716 717 String setterCode = expression.toSetSourceString( context, root ); 718 String castExpression = (String) context.get( PRE_CAST ); 719 720 if ( setterCode == null || setterCode.trim().length() < 1 ) 721 { 722 throw new UnsupportedCompilationException( "Can't compile null setter body." ); 723 } 724 725 if ( root == null ) 726 { 727 throw new UnsupportedCompilationException( "Can't compile setters with a null root object." ); 728 } 729 730 String pre = getRootExpression( expression, root, context ); 731 732 String noRoot = (String) context.remove( "_noRoot" ); 733 if ( noRoot != null ) 734 { 735 pre = ""; 736 } 737 738 createLocalReferences( context, classPool, newClass, objClass, valueSetter.getParameterTypes() ); 739 740 body = "{" + ( castExpression != null ? castExpression : "" ) + pre + setterCode + ";}"; 741 742 body = body.replaceAll( "\\.\\.", "." ); 743 744 // System.out.println("Setter Body: ===================================\n" + body); 745 746 valueSetter.setBody( body ); 747 newClass.addMethod( valueSetter ); 748 749 return body; 750 } 751 752 /** 753 * Fail safe getter creation when normal compilation fails. 754 * 755 * @param clazz The javassist class the new method should be attached to. 756 * @param valueGetter The method definition the generated code will be contained within. 757 * @param node The root expression node. 758 * @return The generated source string for this method, the method will still be added via the javassist API either 759 * way so this is really a convenience for exception reporting / debugging. 760 * @throws Exception If a javassist error occurs. 761 */ 762 protected String generateOgnlGetter( CtClass clazz, CtMethod valueGetter, CtField node ) 763 throws Exception 764 { 765 String body = "return " + node.getName() + ".getValue($1, $2);"; 766 767 valueGetter.setBody( body ); 768 clazz.addMethod( valueGetter ); 769 770 return body; 771 } 772 773 /** 774 * Fail safe setter creation when normal compilation fails. 775 * 776 * @param clazz The javassist class the new method should be attached to. 777 * @param valueSetter The method definition the generated code will be contained within. 778 * @param node The root expression node. 779 * @return The generated source string for this method, the method will still be added via the javassist API either 780 * way so this is really a convenience for exception reporting / debugging. 781 * @throws Exception If a javassist error occurs. 782 */ 783 protected String generateOgnlSetter( CtClass clazz, CtMethod valueSetter, CtField node ) 784 throws Exception 785 { 786 String body = node.getName() + ".setValue($1, $2, $3);"; 787 788 valueSetter.setBody( body ); 789 clazz.addMethod( valueSetter ); 790 791 return body; 792 } 793 794 /** 795 * Creates a {@link ClassLoader} instance compatible with the javassist classloader and normal OGNL class resolving 796 * semantics. 797 * 798 * @param context The current execution context. 799 * @return The created {@link ClassLoader} instance. 800 */ 801 protected EnhancedClassLoader getClassLoader( OgnlContext context ) 802 { 803 EnhancedClassLoader ret = loaders.get( context.getClassResolver() ); 804 805 if ( ret != null ) 806 { 807 return ret; 808 } 809 810 ClassLoader classLoader = new ContextClassLoader( OgnlContext.class.getClassLoader(), context ); 811 812 ret = new EnhancedClassLoader( classLoader ); 813 loaders.put( context.getClassResolver(), ret ); 814 815 return ret; 816 } 817 818 /** 819 * Loads a new class definition via javassist for the specified class. 820 * 821 * @param searchClass The class to load. 822 * @return The javassist class equivalent. 823 * @throws javassist.NotFoundException When the class definition can't be found. 824 */ 825 protected CtClass getCtClass( Class<?> searchClass ) 826 throws NotFoundException 827 { 828 return pool.get( searchClass.getName() ); 829 } 830 831 /** 832 * Gets either a new or existing {@link ClassPool} for use in compiling javassist classes. A new class path object 833 * is inserted in to the returned {@link ClassPool} using the passed in <code>loader</code> instance if a new pool 834 * needs to be created. 835 * 836 * @param context The current execution context. 837 * @param loader The {@link ClassLoader} instance to use - as returned by 838 * {@link #getClassLoader(org.apache.commons.ognl.OgnlContext)}. 839 * @return The existing or new {@link ClassPool} instance. 840 */ 841 protected ClassPool getClassPool( OgnlContext context, EnhancedClassLoader loader ) 842 { 843 if ( pool != null ) 844 { 845 return pool; 846 } 847 848 pool = ClassPool.getDefault(); 849 pool.insertClassPath( new LoaderClassPath( loader.getParent() ) ); 850 851 return pool; 852 } 853 }