3b95af2ffa67db0b488589ab21b92edc1b544d13
[fedora-idea.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / debugger / GroovyCodeFragmentFactory.java
blob3b95af2ffa67db0b488589ab21b92edc1b544d13
1 /*
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;
51 import java.util.Map;
53 /**
54 * @author ven
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 = "\"" +
61 imports +
62 "class DUMMY { " +
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(" +
69 classText +
70 ", \"DUMMY.groovy\");\n" +
71 " int i;\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" +
83 "resVals[iii] = o;" +
84 "}\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");
112 if (!isStatic) {
113 javaText.append("clazz = ((java.lang.Object)this).getClass();\n");
114 javaText.append("mc = ((groovy.lang.GroovyObject)this).getMetaClass();\n");
115 } else {
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");
123 if (!isStatic) {
124 javaText.append("emc.setProperty(\"").append(EVAL_NAME).append("\", closure);\n");
125 javaText.append("((groovy.lang.GroovyObject)this).setMetaClass(emc);\n");
126 } else {
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));
132 if (!isStatic) {
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
136 } else {
137 javaText.append("java.lang.Object res = ((groovy.lang.MetaClassImpl)emc).invokeStaticMethod(clazz, \"").append(EVAL_NAME)
138 .append("\", ").
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));
149 return result;
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");
165 return;
168 if (resolved instanceof GrField && !referenceExpression.isQualified()) {
169 replaceWithReference(referenceExpression, (closure == null ? "delegate" : "owner") + "." + referenceExpression.getReferenceName());
170 return;
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)) {
176 return;
178 String value;
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;
185 } else {
186 value = name;
188 parameters.put(name, value);
192 @Override
193 public void visitThisExpression(final GrThisReferenceExpression thisExpression) {
194 super.visitThisExpression(thisExpression);
195 replaceWithReference(thisExpression, closure == null ? "delegate" : "owner");
198 @Override
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);
212 } else {
213 PsiElement resolved = refElement.resolve();
214 if (resolved instanceof PsiClass) {
215 String qName = ((PsiClass)resolved).getQualifiedName();
216 if (qName != null) {
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);
241 return result;
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();
252 return false;
255 public boolean isContextAccepted(PsiElement context) {
256 return context != null && context.getLanguage().equals(GroovyFileType.GROOVY_FILE_TYPE.getLanguage());
259 public String getDisplayName() {
260 return "Groovy";
263 public LanguageFileType getFileType() {
264 return GroovyFileType.GROOVY_FILE_TYPE;