001 /* 002 Copyright (c) 2009 Pawel Gdula <pawel.gdula@burningice.pl> 003 004 Permission is hereby granted, free of charge, to any person obtaining a copy 005 of this software and associated documentation files (the "Software"), to deal 006 in the Software without restriction, including without limitation the rights 007 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 008 copies of the Software, and to permit persons to whom the Software is 009 furnished to do so, subject to the following conditions: 010 011 The above copyright notice and this permission notice shall be included in 012 all copies or substantial portions of the Software. 013 014 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 015 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 016 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 017 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 018 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 019 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 020 THE SOFTWARE. 021 */ 022 package pl.burningice.plugins.image.ast; 023 024 import groovy.lang.Closure; 025 import org.codehaus.groovy.ast.*; 026 import org.codehaus.groovy.ast.expr.*; 027 import org.codehaus.groovy.ast.stmt.BlockStatement; 028 import org.codehaus.groovy.ast.stmt.ExpressionStatement; 029 import org.codehaus.groovy.ast.stmt.Statement; 030 import org.codehaus.groovy.control.CompilePhase; 031 import org.codehaus.groovy.transform.GroovyASTTransformation; 032 import pl.burningice.plugins.image.ast.intarface.DBImageContainer; 033 import pl.burningice.plugins.image.container.DeleteDbImageCommand; 034 035 import java.lang.reflect.Modifier; 036 import java.util.Map; 037 038 /** 039 * Class execute transformation of objects marked by DBImageContainer annotation 040 * 041 * @author pawel.gdula@burningice.pl 042 */ 043 @GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION) 044 public class DBImageContainerTransformation extends AbstractImageContainerTransformation { 045 046 @Override 047 protected void transformSpecified(ClassNode node, String fieldName) { 048 // implements interface 049 node.addInterface(new ClassNode(DBImageContainer.class)); 050 // add relation with images table 051 FieldNode imageBindField = new FieldNode("biImage", Modifier.PRIVATE, new ClassNode(Map.class), new ClassNode(node.getClass()), null); 052 node.addField(imageBindField); 053 addGetter(imageBindField, node); 054 addSetter(imageBindField, node); 055 addNullableConstraint(node, "biImage"); 056 // add hasMany relation 057 FieldNode hasManyField = getHasManyField(node); 058 MapExpression mapValues = (MapExpression)hasManyField.getInitialExpression(); 059 mapValues.addMapEntryExpression(new ConstantExpression("biImage"), new ClassExpression(new ClassNode(Image.class))); 060 // add beforeDelete handler 061 MethodNode beforeDeleteMethod = getBeforeDeleteMethod(node); 062 ((BlockStatement)beforeDeleteMethod.getCode()).addStatement(createDeleteImageCommandCall()); 063 // add caching 064 FieldNode mappingField = getMappingField(node); 065 ((BlockStatement)((ClosureExpression)mappingField.getInitialExpression()).getCode()).addStatement(createBiImageFieldMapping()); 066 } 067 068 private FieldNode getMappingField(ClassNode node){ 069 final String fieldName = "mapping"; 070 FieldNode mappingField = node.getDeclaredField(fieldName); 071 072 if (mappingField == null){ 073 ClosureExpression mappingExpression = new ClosureExpression(new Parameter[0], new BlockStatement()); 074 // this is very important in ClosureExpression - in other case NPE! 075 mappingExpression.setVariableScope(new VariableScope()); 076 077 mappingField = new FieldNode( 078 fieldName, 079 Modifier.STATIC | Modifier.PRIVATE, 080 new ClassNode(Closure.class), 081 new ClassNode(DBImageContainer.class), 082 mappingExpression); 083 084 addGetter(mappingField, node, Modifier.STATIC | Modifier.PUBLIC); 085 addSetter(mappingField, node, Modifier.STATIC | Modifier.PUBLIC); 086 node.addField(mappingField); 087 } 088 089 return mappingField; 090 } 091 092 private MethodNode getBeforeDeleteMethod(ClassNode node){ 093 final String methodName = "beforeDelete"; 094 MethodNode beforeDeleteMethod = node.getDeclaredMethod(methodName, new Parameter[0]); 095 096 if (beforeDeleteMethod == null){ 097 beforeDeleteMethod = new MethodNode(methodName, Modifier.PUBLIC, new ClassNode(Object.class), new Parameter[0], new ClassNode[0], new BlockStatement()); 098 node.addMethod(beforeDeleteMethod); 099 } 100 101 return beforeDeleteMethod; 102 } 103 104 private Statement createDeleteImageCommandCall() { 105 return new ExpressionStatement( 106 new StaticMethodCallExpression( 107 new ClassNode(DeleteDbImageCommand.class), 108 "execute", 109 new ArgumentListExpression(new VariableExpression("this")) 110 ) 111 ); 112 } 113 114 private Statement createBiImageFieldMapping() { 115 NamedArgumentListExpression namedarg = new NamedArgumentListExpression(); 116 namedarg.addMapEntryExpression(new ConstantExpression("cache"), new BooleanExpression(new ConstantExpression(true))); 117 namedarg.addMapEntryExpression(new ConstantExpression("lazy"), new BooleanExpression(new ConstantExpression(false))); 118 119 MethodCallExpression constExpr = new MethodCallExpression(VariableExpression.THIS_EXPRESSION, 120 new ConstantExpression("biImage"), 121 namedarg); 122 return new ExpressionStatement(constExpr); 123 } 124 }