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
;
52 import java
.util
.Random
;
57 public class GroovyCodeFragmentFactory
implements CodeFragmentFactory
{
58 private static final String EVAL_NAME
= "_JETGROOVY_EVAL_";
60 private static String
createProperty(String text
, String imports
) {
61 String classText
= "\"" +
64 "public groovy.lang.Closure " +
65 EVAL_NAME
+ " = {" + text
+ "}}\"";
67 return "final java.lang.ClassLoader parentLoader = clazz.getClassLoader();\n" +
68 " final groovy.lang.GroovyClassLoader loader = new groovy.lang.GroovyClassLoader(parentLoader);\n" +
69 " final java.lang.Class c = loader.parseClass(" +
71 ", \"DUMMY.groovy\");\n" +
73 " java.lang.reflect.Field[] fields = c.getFields();\n" +
74 " for (int j = 0; j < fields.length; j++) if (fields[j].getName().equals(\"_JETGROOVY_EVAL_\")) {i = j; break;}\n" +
75 " final java.lang.reflect.Field field = fields[i];\n" +
76 " final java.lang.Object closure = field.get(c.newInstance());\n";
79 private static String
unwrapVals(List
<String
> vals
) {
80 return "java.lang.Object[] vals = new java.lang.Object[]{" + StringUtil
.join(vals
, ",") + "};\n" +
81 "java.lang.Object[] resVals = new java.lang.Object[" + vals
.size() + "];\n" +
82 "for (int iii =0; iii<vals.length; iii++){java.lang.Object o = vals[iii];\n" +
83 "if (o instanceof groovy.lang.Reference) {o = ((groovy.lang.Reference)o).get();}\n" +
88 public JavaCodeFragment
createCodeFragment(TextWithImports textWithImports
, PsiElement context
, Project project
) {
89 String text
= textWithImports
.getText();
90 String imports
= textWithImports
.getImports();
93 final Pair
<Map
<String
, String
>, GroovyFile
> pair
= externalParameters(text
, context
);
94 GroovyFile toEval
= pair
.second
;
95 final Map
<String
, String
> parameters
= pair
.first
;
97 List
<String
> names
= new ArrayList
<String
>(parameters
.keySet());
98 List
<String
> values
= ContainerUtil
.map(names
, new Function
<String
, String
>() {
99 public String
fun(String name
) {
100 return parameters
.get(name
);
105 text
= toEval
.getText();
107 PsiClass contextClass
= PsiUtil
.getContextClass(context
);
108 boolean isStatic
= isStaticContext(context
);
109 StringBuffer javaText
= new StringBuffer();
111 javaText
.append("groovy.lang.MetaClass mc;\n");
112 javaText
.append("java.lang.Class clazz;\n");
114 javaText
.append("clazz = ((java.lang.Object)this).getClass();\n");
115 javaText
.append("mc = ((groovy.lang.GroovyObject)this).getMetaClass();\n");
117 javaText
.append("clazz = java.lang.Class.forName(\"").append(contextClass
.getQualifiedName()).append("\");\n");
118 javaText
.append("mc = groovy.lang.GroovySystem.getMetaClassRegistry().getMetaClass(clazz);\n");
121 javaText
.append(createProperty(StringUtil
.join(names
, ", ") + "->" + stripImports(text
, toEval
), imports
));
123 javaText
.append("groovy.lang.ExpandoMetaClass emc = new groovy.lang.ExpandoMetaClass(clazz);\n");
125 javaText
.append("emc.setProperty(\"").append(EVAL_NAME
).append("\", closure);\n");
126 javaText
.append("((groovy.lang.GroovyObject)this).setMetaClass(emc);\n");
128 javaText
.append("((groovy.lang.GroovyObject)emc.getProperty(\"static\")).setProperty(\"").append(EVAL_NAME
).append("\", closure);\n");
129 javaText
.append("groovy.lang.GroovySystem.getMetaClassRegistry().setMetaClass(clazz, emc);\n");
131 javaText
.append("emc.initialize();\n");
132 javaText
.append(unwrapVals(values
));
134 javaText
.append("java.lang.Object res = ((groovy.lang.MetaClassImpl)emc).invokeMethod(this, \"").append(EVAL_NAME
).append("\", ").
135 append("resVals").append(");\n");
136 javaText
.append("((groovy.lang.GroovyObject)this).setMetaClass(mc);"); //try/finally is not supported
138 javaText
.append("java.lang.Object res = ((groovy.lang.MetaClassImpl)emc).invokeStaticMethod(clazz, \"").append(EVAL_NAME
)
140 append("resVals").append(");\n");
141 javaText
.append("groovy.lang.GroovySystem.getMetaClassRegistry().setMetaClass(clazz, mc);\n");
143 javaText
.append("res");
145 final PsiElementFactory factory
= JavaPsiFacade
.getInstance(toEval
.getProject()).getElementFactory();
146 JavaCodeFragment result
= factory
.createCodeBlockCodeFragment(javaText
.toString(), null, true);
147 hideInternalJavaVariables(factory
, result
);
148 if (contextClass
!= null) {
149 result
.setThisType(factory
.createType(contextClass
));
154 private static void hideInternalJavaVariables(final PsiElementFactory factory
, JavaCodeFragment result
) {
155 final String varPrefix
= "_$$_$$$_$$$$$$$$$_" + new Random().nextInt(42);
156 result
.accept(new PsiRecursiveElementWalkingVisitor() {
158 public void visitElement(PsiElement element
) {
159 if (element
instanceof PsiReferenceExpression
&& ((PsiReferenceExpression
)element
).resolve() instanceof PsiLocalVariable
) {
160 element
.replace(factory
.createExpressionFromText(varPrefix
+ element
.getText(), element
));
162 super.visitElement(element
);
165 result
.accept(new PsiRecursiveElementWalkingVisitor() {
167 public void visitElement(PsiElement element
) {
168 if (element
instanceof PsiLocalVariable
) {
169 ((PsiLocalVariable
)element
).setName(varPrefix
+ ((PsiLocalVariable
)element
).getName());
171 super.visitElement(element
);
176 public static Pair
<Map
<String
, String
>, GroovyFile
> externalParameters(String text
, @NotNull final PsiElement context
) {
177 final GroovyPsiElementFactory factory
= GroovyPsiElementFactory
.getInstance(context
.getProject());
178 final GroovyFile toEval
= factory
.createGroovyFile(text
, false, context
);
180 final GrClosableBlock closure
= PsiTreeUtil
.getParentOfType(context
, GrClosableBlock
.class);
181 final Map
<String
, String
> parameters
= new THashMap
<String
, String
>();
182 toEval
.accept(new GroovyRecursiveElementVisitor() {
183 public void visitReferenceExpression(GrReferenceExpression referenceExpression
) {
184 super.visitReferenceExpression(referenceExpression
);
185 PsiElement resolved
= referenceExpression
.resolve();
187 if (resolved
instanceof PsiMethod
&& "getDelegate".equals(((PsiMethod
) resolved
).getName()) && closure
!= null) {
188 replaceWithReference(referenceExpression
, "owner");
192 if (resolved
instanceof GrField
&& !referenceExpression
.isQualified()) {
193 replaceWithReference(referenceExpression
, (closure
== null ?
"delegate" : "owner") + "." + referenceExpression
.getReferenceName());
197 if (resolved
instanceof GrVariableBase
&& !(resolved
instanceof GrField
) && !PsiTreeUtil
.isAncestor(toEval
, resolved
, false)) {
198 final String name
= ((GrVariableBase
)resolved
).getName();
199 if (resolved
instanceof ClosureSyntheticParameter
&& PsiTreeUtil
.isAncestor(toEval
, ((ClosureSyntheticParameter
) resolved
).getClosure(), false)) {
203 if (closure
!= null &&
204 PsiTreeUtil
.findCommonParent(resolved
, closure
) != closure
&&
205 !(resolved
instanceof ClosureSyntheticParameter
)) {
206 // Evaluating inside closure for outer variable definitions
207 // All non-local variables are accessed by references
208 value
= "this." + name
;
212 parameters
.put(name
, value
);
217 public void visitThisExpression(final GrThisReferenceExpression thisExpression
) {
218 super.visitThisExpression(thisExpression
);
219 replaceWithReference(thisExpression
, closure
== null ?
"delegate" : "owner");
223 public void visitSuperExpression(final GrSuperReferenceExpression superExpression
) {
224 super.visitSuperExpression(superExpression
);
225 replaceWithReference(superExpression
, closure
== null ?
"delegate" : "owner");
228 private void replaceWithReference(GrExpression expr
, final String exprText
) {
229 final GroovyPsiElementFactory factory
= GroovyPsiElementFactory
.getInstance(expr
.getProject());
230 visitReferenceExpression((GrReferenceExpression
)expr
.replaceWithExpression(factory
.createExpressionFromText(exprText
), false));
233 public void visitCodeReferenceElement(GrCodeReferenceElement refElement
) {
234 if (refElement
.getQualifier() != null) {
235 super.visitCodeReferenceElement(refElement
);
237 PsiElement resolved
= refElement
.resolve();
238 if (resolved
instanceof PsiClass
) {
239 String qName
= ((PsiClass
)resolved
).getQualifiedName();
241 int dotIndex
= qName
.lastIndexOf(".");
242 if (dotIndex
< 0) return;
243 String packageName
= qName
.substring(0, dotIndex
);
244 refElement
.setQualifier(factory
.createReferenceElementFromText(packageName
));
250 return Pair
.create(parameters
, toEval
);
253 private static String
stripImports(String text
, GroovyFile toEval
) {
254 GrImportStatement
[] imports
= toEval
.getImportStatements();
255 for (int i
= imports
.length
- 1; i
>= 0; i
--) {
256 TextRange range
= imports
[i
].getTextRange();
257 text
= text
.substring(0, range
.getStartOffset()) + text
.substring(range
.getEndOffset(), text
.length());
259 return StringUtil
.escapeStringCharacters(text
);
262 public JavaCodeFragment
createPresentationCodeFragment(TextWithImports item
, PsiElement context
, Project project
) {
263 GroovyCodeFragment result
= new GroovyCodeFragment(project
, item
.getText());
264 result
.setContext(context
);
268 private static boolean isStaticContext(PsiElement context
) {
269 PsiElement parent
= context
;
270 while (parent
!= null) {
271 if (parent
instanceof PsiModifierListOwner
&& ((PsiModifierListOwner
)parent
).hasModifierProperty(PsiModifier
.STATIC
)) return true;
272 if (parent
instanceof GrTypeDefinition
|| parent
instanceof GroovyFile
) return false;
273 parent
= parent
.getParent();
279 public boolean isContextAccepted(PsiElement context
) {
280 return context
!= null && context
.getLanguage().equals(GroovyFileType
.GROOVY_FILE_TYPE
.getLanguage());
283 public String
getDisplayName() {
287 public LanguageFileType
getFileType() {
288 return GroovyFileType
.GROOVY_FILE_TYPE
;