1 /*
2  * Copyright 2004-2005 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.codehaus.groovy.grails.plugins;
17 
18 import grails.util.GrailsNameUtils;
19 import groovy.lang.GroovyClassLoader;
20 
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.LinkedHashMap;
24 import java.util.List;
25 import java.util.Map;
26 
27 import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
28 import org.codehaus.groovy.ast.ClassNode;
29 import org.codehaus.groovy.ast.PropertyNode;
30 import org.codehaus.groovy.ast.expr.Expression;
31 import org.codehaus.groovy.ast.expr.ListExpression;
32 import org.codehaus.groovy.ast.expr.MapEntryExpression;
33 import org.codehaus.groovy.ast.expr.MapExpression;
34 import org.codehaus.groovy.classgen.GeneratorContext;
35 import org.codehaus.groovy.control.CompilationFailedException;
36 import org.codehaus.groovy.control.CompilationUnit;
37 import org.codehaus.groovy.control.Phases;
38 import org.codehaus.groovy.control.SourceUnit;
39 import org.codehaus.groovy.grails.plugins.exceptions.PluginException;
40 import org.springframework.beans.BeanWrapper;
41 import org.springframework.beans.BeanWrapperImpl;
42 import org.springframework.core.io.Resource;
43 
44 /**
45  * Used to read plugin information from the AST.
46  *
47  * @author Graeme Rocher
48  * @since 1.3
49  */
50 public class AstPluginDescriptorReader implements PluginDescriptorReader {
51 
52     private GroovyClassLoader classLoader;
53 
AstPluginDescriptorReader()54     public AstPluginDescriptorReader() {
55         this(new GroovyClassLoader(Thread.currentThread().getContextClassLoader()));
56     }
57 
AstPluginDescriptorReader(GroovyClassLoader cl)58     public AstPluginDescriptorReader(GroovyClassLoader cl) {
59         classLoader = cl;
60     }
61 
readPluginInfo(Resource pluginLocation)62     public GrailsPluginInfo readPluginInfo(Resource pluginLocation) {
63         CompilationUnit compilationUnit = new CompilationUnit(classLoader);
64         BasicGrailsPluginInfo pluginInfo = new BasicGrailsPluginInfo(pluginLocation);
65 
66         try {
67             compilationUnit.addSource("dummy",pluginLocation.getInputStream());
68             compilationUnit.addPhaseOperation(new PluginReadingPhaseOperation(pluginInfo), Phases.CONVERSION);
69             compilationUnit.compile(Phases.CONVERSION);
70             return pluginInfo;
71         }
72         catch (IOException e) {
73             throw new PluginException("Cannot read plugin info: " + e.getMessage());
74         }
75     }
76 
77     class PluginReadingPhaseOperation  extends CompilationUnit.PrimaryClassNodeOperation {
78         private BasicGrailsPluginInfo pluginInfo;
79         private BeanWrapper wrapper;
PluginReadingPhaseOperation(BasicGrailsPluginInfo pluginInfo)80         public PluginReadingPhaseOperation(BasicGrailsPluginInfo pluginInfo) {
81             this.pluginInfo = pluginInfo;
82             wrapper = new BeanWrapperImpl(pluginInfo);
83         }
84 
85         @Override
call(final SourceUnit source, GeneratorContext context, ClassNode classNode)86         public void call(final SourceUnit source, GeneratorContext context,
87                 ClassNode classNode) throws CompilationFailedException {
88 
89             ClassCodeVisitorSupport visitor = new ClassCodeVisitorSupport() {
90 
91                 @Override
92                 public void visitProperty(PropertyNode node) {
93                     String name = node.getName();
94                     final Expression expr = node.getField().getInitialExpression();
95                     Object value;
96                     if (expr instanceof ListExpression) {
97                         final List<String> list = new ArrayList<String>();
98                         value = list;
99                         for (Expression i : ((ListExpression)expr).getExpressions()) {
100                             list.add(i.getText());
101                         }
102                     }
103                     else if (expr instanceof MapExpression) {
104                         final Map<String, String> map = new LinkedHashMap<String, String>();
105                         value = map;
106                         for (MapEntryExpression mee : ((MapExpression)expr).getMapEntryExpressions()) {
107                             map.put(mee.getKeyExpression().getText(), mee.getValueExpression().getText());
108                         }
109                     }
110                     else {
111                         value = expr.getText();
112                     }
113 
114                     if (wrapper.isWritableProperty(name)) {
115                             wrapper.setPropertyValue(name, value);
116                     }
117                     else {
118                         pluginInfo.setProperty(name, value);
119                     }
120                     super.visitProperty(node);
121                 }
122 
123                 @Override
124                 protected SourceUnit getSourceUnit() {
125                     return source;
126                 }
127             };
128 
129             classNode.visitContents(visitor);
130             String className = classNode.getNameWithoutPackage();
131 
132             wrapper.setPropertyValue("name", GrailsNameUtils.getPluginName(className + ".groovy"));
133         }
134     }
135 }
136