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    }