2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org
.jetbrains
.plugins
.groovy
.debugger
;
18 import com
.intellij
.debugger
.engine
.evaluation
.CodeFragmentFactory
;
19 import com
.intellij
.debugger
.engine
.evaluation
.TextWithImports
;
20 import com
.intellij
.openapi
.fileTypes
.LanguageFileType
;
21 import com
.intellij
.openapi
.project
.Project
;
22 import com
.intellij
.openapi
.util
.Pair
;
23 import com
.intellij
.openapi
.util
.TextRange
;
24 import com
.intellij
.openapi
.util
.text
.StringUtil
;
25 import com
.intellij
.psi
.*;
26 import com
.intellij
.psi
.util
.PsiTreeUtil
;
27 import com
.intellij
.util
.Function
;
28 import com
.intellij
.util
.containers
.ContainerUtil
;
29 import gnu
.trove
.THashMap
;
30 import org
.jetbrains
.annotations
.NotNull
;
31 import org
.jetbrains
.plugins
.groovy
.GroovyFileType
;
32 import org
.jetbrains
.plugins
.groovy
.debugger
.fragments
.GroovyCodeFragment
;
33 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.GroovyFile
;
34 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.GroovyPsiElementFactory
;
35 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.GroovyRecursiveElementVisitor
;
36 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.GrField
;
37 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.GrVariableBase
;
38 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.blocks
.GrClosableBlock
;
39 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.expressions
.GrExpression
;
40 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.expressions
.GrReferenceExpression
;
41 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.expressions
.GrSuperReferenceExpression
;
42 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.expressions
.GrThisReferenceExpression
;
43 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.typedef
.GrTypeDefinition
;
44 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.toplevel
.imports
.GrImportStatement
;
45 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.types
.GrCodeReferenceElement
;
46 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.impl
.synthetic
.ClosureSyntheticParameter
;
47 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.util
.PsiUtil
;
49 import java
.util
.ArrayList
;
50 import java
.util
.List
;
56 public class GroovyCodeFragmentFactory
implements CodeFragmentFactory
{
57 private static final String EVAL_NAME
= "_JETGROOVY_EVAL_";
59 private static String
createProperty(String text
, String imports
) {
60 String classText
= "\"" +
63 "public groovy.lang.Closure " +
64 EVAL_NAME
+ " = {" + text
+ "}}\"";
66 return "final java.lang.ClassLoader parentLoader = clazz.getClassLoader();\n" +
67 " final groovy.lang.GroovyClassLoader loader = new groovy.lang.GroovyClassLoader(parentLoader);\n" +
68 " final java.lang.Class c = loader.parseClass(" +
70 ", \"DUMMY.groovy\");\n" +
72 " java.lang.reflect.Field[] fields = c.getFields();\n" +
73 " for (int j = 0; j < fields.length; j++) if (fields[j].getName().equals(\"_JETGROOVY_EVAL_\")) {i = j; break;}\n" +
74 " final java.lang.reflect.Field field = fields[i];\n" +
75 " final java.lang.Object closure = field.get(c.newInstance());\n";
78 private static String
unwrapVals(List
<String
> vals
) {
79 return "java.lang.Object[] vals = new java.lang.Object[]{" + StringUtil
.join(vals
, ",") + "};\n" +
80 "java.lang.Object[] resVals = new java.lang.Object[" + vals
.size() + "];\n" +
81 "for (int iii =0; iii<vals.length; iii++){java.lang.Object o = vals[iii];\n" +
82 "if (o instanceof groovy.lang.Reference) {o = ((groovy.lang.Reference)o).get();}\n" +
87 public JavaCodeFragment
createCodeFragment(TextWithImports textWithImports
, PsiElement context
, Project project
) {
88 String text
= textWithImports
.getText();
89 String imports
= textWithImports
.getImports();
92 final Pair
<Map
<String
, String
>, GroovyFile
> pair
= externalParameters(text
, context
);
93 GroovyFile toEval
= pair
.second
;
94 final Map
<String
, String
> parameters
= pair
.first
;
96 List
<String
> names
= new ArrayList
<String
>(parameters
.keySet());
97 List
<String
> values
= ContainerUtil
.map(names
, new Function
<String
, String
>() {
98 public String
fun(String name
) {
99 return parameters
.get(name
);
104 text
= toEval
.getText();
106 PsiClass contextClass
= PsiUtil
.getContextClass(context
);
107 boolean isStatic
= isStaticContext(context
);
108 StringBuffer javaText
= new StringBuffer();
110 javaText
.append("groovy.lang.MetaClass mc;\n");
111 javaText
.append("java.lang.Class clazz;\n");
113 javaText
.append("clazz = ((java.lang.Object)this).getClass();\n");
114 javaText
.append("mc = ((groovy.lang.GroovyObject)this).getMetaClass();\n");
116 javaText
.append("clazz = java.lang.Class.forName(\"").append(contextClass
.getQualifiedName()).append("\");\n");
117 javaText
.append("mc = groovy.lang.GroovySystem.getMetaClassRegistry().getMetaClass(clazz);\n");
120 javaText
.append(createProperty(StringUtil
.join(names
, ", ") + "->" + stripImports(text
, toEval
), imports
));
122 javaText
.append("groovy.lang.ExpandoMetaClass emc = new groovy.lang.ExpandoMetaClass(clazz);\n");
124 javaText
.append("emc.setProperty(\"").append(EVAL_NAME
).append("\", closure);\n");
125 javaText
.append("((groovy.lang.GroovyObject)this).setMetaClass(emc);\n");
127 javaText
.append("((groovy.lang.GroovyObject)emc.getProperty(\"static\")).setProperty(\"").append(EVAL_NAME
).append("\", closure);\n");
128 javaText
.append("groovy.lang.GroovySystem.getMetaClassRegistry().setMetaClass(clazz, emc);\n");
130 javaText
.append("emc.initialize();\n");
131 javaText
.append(unwrapVals(values
));
133 javaText
.append("java.lang.Object res = ((groovy.lang.MetaClassImpl)emc).invokeMethod(this, \"").append(EVAL_NAME
).append("\", ").
134 append("resVals").append(");\n");
135 javaText
.append("((groovy.lang.GroovyObject)this).setMetaClass(mc);"); //try/finally is not supported
137 javaText
.append("java.lang.Object res = ((groovy.lang.MetaClassImpl)emc).invokeStaticMethod(clazz, \"").append(EVAL_NAME
)
139 append("resVals").append(");\n");
140 javaText
.append("groovy.lang.GroovySystem.getMetaClassRegistry().setMetaClass(clazz, mc);\n");
142 javaText
.append("res");
144 PsiElementFactory elementFactory
= JavaPsiFacade
.getInstance(toEval
.getProject()).getElementFactory();
145 JavaCodeFragment result
= elementFactory
.createCodeBlockCodeFragment(javaText
.toString(), null, true);
146 if (contextClass
!= null) {
147 result
.setThisType(elementFactory
.createType(contextClass
));
152 public static Pair
<Map
<String
, String
>, GroovyFile
> externalParameters(String text
, @NotNull final PsiElement context
) {
153 final GroovyPsiElementFactory factory
= GroovyPsiElementFactory
.getInstance(context
.getProject());
154 final GroovyFile toEval
= factory
.createGroovyFile(text
, false, context
);
156 final GrClosableBlock closure
= PsiTreeUtil
.getParentOfType(context
, GrClosableBlock
.class);
157 final Map
<String
, String
> parameters
= new THashMap
<String
, String
>();
158 toEval
.accept(new GroovyRecursiveElementVisitor() {
159 public void visitReferenceExpression(GrReferenceExpression referenceExpression
) {
160 super.visitReferenceExpression(referenceExpression
);
161 PsiElement resolved
= referenceExpression
.resolve();
163 if (resolved
instanceof PsiMethod
&& "getDelegate".equals(((PsiMethod
) resolved
).getName()) && closure
!= null) {
164 replaceWithReference(referenceExpression
, "owner");
168 if (resolved
instanceof GrField
&& !referenceExpression
.isQualified()) {
169 replaceWithReference(referenceExpression
, (closure
== null ?
"delegate" : "owner") + "." + referenceExpression
.getReferenceName());
173 if (resolved
instanceof GrVariableBase
&& !(resolved
instanceof GrField
) && !PsiTreeUtil
.isAncestor(toEval
, resolved
, false)) {
174 final String name
= ((GrVariableBase
)resolved
).getName();
175 if (resolved
instanceof ClosureSyntheticParameter
&& PsiTreeUtil
.isAncestor(toEval
, ((ClosureSyntheticParameter
) resolved
).getClosure(), false)) {
179 if (closure
!= null &&
180 PsiTreeUtil
.findCommonParent(resolved
, closure
) != closure
&&
181 !(resolved
instanceof ClosureSyntheticParameter
)) {
182 // Evaluating inside closure for outer variable definitions
183 // All non-local variables are accessed by references
184 value
= "this." + name
;
188 parameters
.put(name
, value
);
193 public void visitThisExpression(final GrThisReferenceExpression thisExpression
) {
194 super.visitThisExpression(thisExpression
);
195 replaceWithReference(thisExpression
, closure
== null ?
"delegate" : "owner");
199 public void visitSuperExpression(final GrSuperReferenceExpression superExpression
) {
200 super.visitSuperExpression(superExpression
);
201 replaceWithReference(superExpression
, closure
== null ?
"delegate" : "owner");
204 private void replaceWithReference(GrExpression expr
, final String exprText
) {
205 final GroovyPsiElementFactory factory
= GroovyPsiElementFactory
.getInstance(expr
.getProject());
206 visitReferenceExpression((GrReferenceExpression
)expr
.replaceWithExpression(factory
.createExpressionFromText(exprText
), false));
209 public void visitCodeReferenceElement(GrCodeReferenceElement refElement
) {
210 if (refElement
.getQualifier() != null) {
211 super.visitCodeReferenceElement(refElement
);
213 PsiElement resolved
= refElement
.resolve();
214 if (resolved
instanceof PsiClass
) {
215 String qName
= ((PsiClass
)resolved
).getQualifiedName();
217 int dotIndex
= qName
.lastIndexOf(".");
218 if (dotIndex
< 0) return;
219 String packageName
= qName
.substring(0, dotIndex
);
220 refElement
.setQualifier(factory
.createReferenceElementFromText(packageName
));
226 return Pair
.create(parameters
, toEval
);
229 private static String
stripImports(String text
, GroovyFile toEval
) {
230 GrImportStatement
[] imports
= toEval
.getImportStatements();
231 for (int i
= imports
.length
- 1; i
>= 0; i
--) {
232 TextRange range
= imports
[i
].getTextRange();
233 text
= text
.substring(0, range
.getStartOffset()) + text
.substring(range
.getEndOffset(), text
.length());
235 return StringUtil
.escapeStringCharacters(text
);
238 public JavaCodeFragment
createPresentationCodeFragment(TextWithImports item
, PsiElement context
, Project project
) {
239 GroovyCodeFragment result
= new GroovyCodeFragment(project
, item
.getText());
240 result
.setContext(context
);
244 private static boolean isStaticContext(PsiElement context
) {
245 PsiElement parent
= context
;
246 while (parent
!= null) {
247 if (parent
instanceof PsiModifierListOwner
&& ((PsiModifierListOwner
)parent
).hasModifierProperty(PsiModifier
.STATIC
)) return true;
248 if (parent
instanceof GrTypeDefinition
|| parent
instanceof GroovyFile
) return false;
249 parent
= parent
.getParent();
255 public boolean isContextAccepted(PsiElement context
) {
256 return context
!= null && context
.getLanguage().equals(GroovyFileType
.GROOVY_FILE_TYPE
.getLanguage());
259 public String
getDisplayName() {
263 public LanguageFileType
getFileType() {
264 return GroovyFileType
.GROOVY_FILE_TYPE
;