From 95f859d00ddf789c7418c5e2a3d1e831cbbac5b3 Mon Sep 17 00:00:00 2001 From: Evgeny Gerashchenko Date: Sun, 11 Mar 2012 18:44:41 +0400 Subject: [PATCH] Refactored JetDecompiledData: extracted DecompiledDataFactory. --- .../libraries/DecompiledDataFactory.java | 196 ++++++++++++++++++ .../JetContentBasedFileSubstitutor.java | 3 +- .../plugin/libraries/JetDecompiledData.java | 175 +--------------- .../JetDummyClassFileViewProvider.java | 2 +- 4 files changed, 204 insertions(+), 172 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java new file mode 100644 index 00000000000..d6f4005f760 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java @@ -0,0 +1,196 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.plugin.libraries; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; +import com.intellij.psi.impl.compiled.ClsAnnotationImpl; +import com.intellij.psi.impl.compiled.ClsElementImpl; +import com.intellij.psi.impl.compiled.ClsFileImpl; +import com.intellij.psi.util.PsiTreeUtil; +import jet.runtime.typeinfo.JetClass; +import jet.runtime.typeinfo.JetMethod; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.JetSemanticServices; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.psi.JetDeclaration; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.BindingTraceContext; +import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; +import org.jetbrains.jet.lang.resolve.java.JavaSemanticServices; +import org.jetbrains.jet.lang.resolve.java.JvmAbi; +import org.jetbrains.jet.resolve.DescriptorRenderer; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Evgeny Gerashchenko + * @since 3/11/12 + */ +class DecompiledDataFactory { + private static final String JET_CLASS = JetClass.class.getName(); + private static final String JET_METHOD = JetMethod.class.getName(); + private static final String DECOMPILED_COMMENT = "/* " + PsiBundle.message("psi.decompiled.method.body") + " */"; + + private StringBuilder myBuilder = new StringBuilder(); + private ClsFileImpl myClsFile; + private BindingContext myBindingContext; + private final Map myClsMembersToRanges = new HashMap(); + + private Map myClsElementsToJetElements = new HashMap(); + private JavaDescriptorResolver myJavaDescriptorResolver; + + private DecompiledDataFactory(ClsFileImpl clsFile) { + myClsFile = clsFile; + Project project = myClsFile.getProject(); + JavaSemanticServices jss = new JavaSemanticServices( + project, + JetSemanticServices.createSemanticServices(project), + new BindingTraceContext()); + myBindingContext = jss.getTrace().getBindingContext(); + myJavaDescriptorResolver = jss.getDescriptorResolver(); + } + + @NotNull + static JetDecompiledData createDecompiledData(@NotNull ClsFileImpl clsFile) { + return new DecompiledDataFactory(clsFile).build(); + } + + private JetDecompiledData build() { + myBuilder.append(PsiBundle.message("psi.decompiled.text.header")); + myBuilder.append("\n\n"); + + String packageName = myClsFile.getPackageName(); + if (packageName.length() > 0) { + myBuilder.append("package ").append(packageName).append("\n\n"); + } + + PsiClass psiClass = myClsFile.getClasses()[0]; + + if (isKotlinNamespaceClass(psiClass)) { + NamespaceDescriptor nd = myJavaDescriptorResolver.resolveNamespace(packageName); + + if (nd != null) { + for (DeclarationDescriptor member : nd.getMemberScope().getAllDescriptors()) { + if (member instanceof ClassDescriptor || member instanceof NamespaceDescriptor) { + continue; + } + appendDescriptor(member, ""); + myBuilder.append("\n"); + } + } + } else { + ClassDescriptor cd = myJavaDescriptorResolver.resolveClass(psiClass); + if (cd != null) { + appendDescriptor(cd, ""); + } + } + + JetFile jetFile = JetDummyClassFileViewProvider.createJetFile(myClsFile.getManager(), myClsFile.getVirtualFile(), myBuilder.toString()); + for (Map.Entry clsMemberToRange : myClsMembersToRanges.entrySet()) { + PsiElement clsMember = clsMemberToRange.getKey(); + assert clsMember instanceof ClsElementImpl; + + TextRange range = clsMemberToRange.getValue(); + JetDeclaration jetDeclaration = PsiTreeUtil.findElementOfClassAtRange(jetFile, range.getStartOffset(), range.getEndOffset(), JetDeclaration.class); + assert jetDeclaration != null; + myClsElementsToJetElements.put((ClsElementImpl) clsMember, jetDeclaration); + } + + return new JetDecompiledData(jetFile, myClsElementsToJetElements); + } + + private void appendDescriptor(DeclarationDescriptor descriptor, String indent) { + int startOffset = myBuilder.length(); + myBuilder.append(DescriptorRenderer.COMPACT.render(descriptor)); + int endOffset = myBuilder.length(); + + if (descriptor instanceof FunctionDescriptor || descriptor instanceof PropertyDescriptor) { + if (((CallableMemberDescriptor) descriptor).getModality() != Modality.ABSTRACT) { + if (descriptor instanceof FunctionDescriptor) { + myBuilder.append(" { ").append(DECOMPILED_COMMENT).append(" }"); + endOffset = myBuilder.length(); + } else { // descriptor instanceof PropertyDescriptor + if (((PropertyDescriptor) descriptor).getModality() != Modality.ABSTRACT) { + myBuilder.append(" ").append(DECOMPILED_COMMENT); + } + } + } + } else if (descriptor instanceof ClassDescriptor) { + myBuilder.append(" {\n"); + ClassDescriptor classDescriptor = (ClassDescriptor) descriptor; + boolean firstPassed = false; + String subindent = indent + " "; + if (classDescriptor.getClassObjectDescriptor() != null) { + firstPassed = true; + myBuilder.append(subindent).append("class "); + appendDescriptor(classDescriptor.getClassObjectDescriptor(), subindent); + } + for (DeclarationDescriptor member : classDescriptor.getDefaultType().getMemberScope().getAllDescriptors()) { + if (member.getContainingDeclaration() == descriptor) { + if (firstPassed) { + myBuilder.append("\n"); + } else { + firstPassed = true; + } + myBuilder.append(subindent); + appendDescriptor(member, subindent); + } + } + myBuilder.append(indent).append("}"); + endOffset = myBuilder.length(); + } + + myBuilder.append("\n"); + PsiElement clsMember = myBindingContext.get(BindingContext.DESCRIPTOR_TO_DECLARATION, descriptor); + if (clsMember != null) { + myClsMembersToRanges.put(clsMember, new TextRange(startOffset, endOffset)); + } + } + + private static boolean hasAnnotation(PsiModifierListOwner modifierListOwner, String qualifiedName) { + PsiModifierList modifierList = modifierListOwner.getModifierList(); + if (modifierList != null) { + for (PsiAnnotation annotation : modifierList.getAnnotations()) { + if (annotation instanceof ClsAnnotationImpl) { + if (qualifiedName.equals(annotation.getQualifiedName())) { + return true; + } + } + } + } + return false; + } + + static boolean isKotlinClass(PsiClass psiClass) { + return hasAnnotation(psiClass, JET_CLASS); + } + + static boolean isKotlinNamespaceClass(PsiClass psiClass) { + if (JvmAbi.PACKAGE_CLASS.equals(psiClass.getName()) && !isKotlinClass(psiClass)) { + for (PsiMethod method : psiClass.getMethods()) { + if (hasAnnotation(method, JET_METHOD)) { + return true; + } + } + } + return false; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetContentBasedFileSubstitutor.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetContentBasedFileSubstitutor.java index acf14b819b8..d4c47795b9a 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/JetContentBasedFileSubstitutor.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetContentBasedFileSubstitutor.java @@ -25,7 +25,6 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.impl.compiled.ClsFileImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.plugin.JetHighlighter; -import org.jetbrains.jet.plugin.libraries.JetDecompiledData; /** * @author Evgeny Gerashchenko @@ -43,7 +42,7 @@ public class JetContentBasedFileSubstitutor implements ContentBasedClassFileProc public String obtainFileText(Project project, VirtualFile file) { ClsFileImpl clsFile = JetDecompiledData.getClsFileIfKotlin(project, file); if (clsFile != null) { - return JetDecompiledData.getDecompiledData(clsFile).getText(); + return JetDecompiledData.getDecompiledData(clsFile).getJetFile().getText(); } return ""; } diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetDecompiledData.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetDecompiledData.java index 09ee4fdb9ce..fa0f6cb8762 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/JetDecompiledData.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetDecompiledData.java @@ -20,27 +20,14 @@ import com.intellij.ide.highlighter.JavaClassFileType; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; -import com.intellij.psi.impl.compiled.ClsAnnotationImpl; import com.intellij.psi.impl.compiled.ClsElementImpl; import com.intellij.psi.impl.compiled.ClsFileImpl; -import com.intellij.psi.util.PsiTreeUtil; -import jet.runtime.typeinfo.JetClass; -import jet.runtime.typeinfo.JetMethod; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.JetSemanticServices; -import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.JetDeclaration; import org.jetbrains.jet.lang.psi.JetFile; -import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.jet.lang.resolve.BindingTraceContext; -import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; -import org.jetbrains.jet.lang.resolve.java.JavaSemanticServices; -import org.jetbrains.jet.lang.resolve.java.JvmAbi; -import org.jetbrains.jet.resolve.DescriptorRenderer; import java.util.HashMap; import java.util.Map; @@ -51,19 +38,15 @@ import java.util.Map; */ @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") public class JetDecompiledData { - private static final String JET_CLASS = JetClass.class.getName(); - private static final String JET_METHOD = JetMethod.class.getName(); - private static final String DECOMPILED_COMMENT = "/* " + PsiBundle.message("psi.decompiled.method.body") + " */"; private JetFile myJetFile; - private String myText; private Map myClsElementsToJetElements = new HashMap(); private static final Object LOCK = new String("decompiled data lock"); private static final Key USER_DATA_KEY = new Key("USER_DATA_KEY"); - private final Map myClsMembersToRanges; - private JetDecompiledData() { - myClsMembersToRanges = new HashMap(); + JetDecompiledData(JetFile jetFile, Map clsElementJetDeclarationMap) { + myJetFile = jetFile; + myClsElementsToJetElements = clsElementJetDeclarationMap; } @NotNull @@ -71,15 +54,11 @@ public class JetDecompiledData { return myJetFile; } - @NotNull - public String getText() { - return myText; - } - public JetDeclaration getJetDeclarationByClsElement(ClsElementImpl clsElement) { return myClsElementsToJetElements.get(clsElement); } + @Deprecated @Nullable public static ClsFileImpl getClsFileIfKotlin(@NotNull Project project, @NotNull VirtualFile vFile) { if (!FileTypeManager.getInstance().isFileOfType(vFile, JavaClassFileType.INSTANCE)) { @@ -94,160 +73,18 @@ public class JetDecompiledData { return null; } PsiClass psiClass = clsFile.getClasses()[0]; - return isKotlinNamespaceClass(psiClass) || isKotlinClass(psiClass) ? clsFile : null; + return DecompiledDataFactory.isKotlinNamespaceClass(psiClass) || DecompiledDataFactory.isKotlinClass(psiClass) ? clsFile : null; } @NotNull public static JetDecompiledData getDecompiledData(@NotNull ClsFileImpl clsFile) { synchronized (LOCK) { if (clsFile.getUserData(USER_DATA_KEY) == null) { - clsFile.putUserData(USER_DATA_KEY, decompileData(clsFile)); + clsFile.putUserData(USER_DATA_KEY, DecompiledDataFactory.createDecompiledData(clsFile)); } JetDecompiledData decompiledData = clsFile.getUserData(USER_DATA_KEY); assert decompiledData != null; return decompiledData; } } - - @NotNull - private static JetDecompiledData decompileData(@NotNull ClsFileImpl clsFile) { - JetDecompiledData result = new JetDecompiledData(); - - Project project = clsFile.getProject(); - result.buildTextAndTree(project, clsFile.getVirtualFile()); - - return result; - } - - - private void buildTextAndTree(Project project, VirtualFile file) { - StringBuilder builder = new StringBuilder(); - - BindingTraceContext trace = new BindingTraceContext(); - JavaSemanticServices jss = new JavaSemanticServices( - project, - JetSemanticServices.createSemanticServices(project), - trace); - PsiManager psiManager = PsiManager.getInstance(project); - ClsFileImpl clsFile = (ClsFileImpl) psiManager.findFile(file); - assert clsFile != null; - builder.append(PsiBundle.message("psi.decompiled.text.header")); - builder.append("\n\n"); - - String packageName = clsFile.getPackageName(); - if (packageName.length() > 0) { - builder.append("package ").append(packageName).append("\n\n"); - } - - PsiClass psiClass = clsFile.getClasses()[0]; - JavaDescriptorResolver jdr = jss.getDescriptorResolver(); - - if (isKotlinNamespaceClass(psiClass)) { - NamespaceDescriptor nd = jdr.resolveNamespace(packageName); - - if (nd != null) { - for (DeclarationDescriptor member : nd.getMemberScope().getAllDescriptors()) { - if (member instanceof ClassDescriptor || member instanceof NamespaceDescriptor) { - continue; - } - appendDescriptor(builder, member, trace.getBindingContext(), ""); - builder.append("\n"); - } - } - } else { - ClassDescriptor cd = jdr.resolveClass(psiClass); - if (cd != null) { - appendDescriptor(builder, cd, trace.getBindingContext(), ""); - } - } - - myText = builder.toString(); - myJetFile = JetDummyClassFileViewProvider.createJetFile(psiManager, file, myText); - for (Map.Entry clsMemberToRange : myClsMembersToRanges.entrySet()) { - PsiElement clsMember = clsMemberToRange.getKey(); - assert clsMember instanceof ClsElementImpl; - - TextRange range = clsMemberToRange.getValue(); - JetDeclaration jetDeclaration = PsiTreeUtil.findElementOfClassAtRange(myJetFile, range.getStartOffset(), range.getEndOffset(), JetDeclaration.class); - assert jetDeclaration != null; - myClsElementsToJetElements.put((ClsElementImpl) clsMember, jetDeclaration); - } - } - - private void appendDescriptor(StringBuilder builder, DeclarationDescriptor descriptor, BindingContext bindingContext, String indent) { - int startOffset = builder.length(); - builder.append(DescriptorRenderer.COMPACT.render(descriptor)); - int endOffset = builder.length(); - - if (descriptor instanceof FunctionDescriptor || descriptor instanceof PropertyDescriptor) { - if (((CallableMemberDescriptor) descriptor).getModality() != Modality.ABSTRACT) { - if (descriptor instanceof FunctionDescriptor) { - builder.append(" { ").append(DECOMPILED_COMMENT).append(" }"); - endOffset = builder.length(); - } else { // descriptor instanceof PropertyDescriptor - if (((PropertyDescriptor) descriptor).getModality() != Modality.ABSTRACT) { - builder.append(" ").append(DECOMPILED_COMMENT); - } - } - } - } else if (descriptor instanceof ClassDescriptor) { - builder.append(" {\n"); - ClassDescriptor classDescriptor = (ClassDescriptor) descriptor; - boolean firstPassed = false; - String subindent = indent + " "; - if (classDescriptor.getClassObjectDescriptor() != null) { - firstPassed = true; - builder.append(subindent).append("class "); - appendDescriptor(builder, classDescriptor.getClassObjectDescriptor(), bindingContext, subindent); - } - for (DeclarationDescriptor member : classDescriptor.getDefaultType().getMemberScope().getAllDescriptors()) { - if (member.getContainingDeclaration() == descriptor) { - if (firstPassed) { - builder.append("\n"); - } else { - firstPassed = true; - } - builder.append(subindent); - appendDescriptor(builder, member, bindingContext, subindent); - } - } - builder.append(indent).append("}"); - endOffset = builder.length(); - } - - builder.append("\n"); - PsiElement clsMember = bindingContext.get(BindingContext.DESCRIPTOR_TO_DECLARATION, descriptor); - if (clsMember != null) { - myClsMembersToRanges.put(clsMember, new TextRange(startOffset, endOffset)); - } - } - - private static boolean hasAnnotation(PsiModifierListOwner modifierListOwner, String qualifiedName) { - PsiModifierList modifierList = modifierListOwner.getModifierList(); - if (modifierList != null) { - for (PsiAnnotation annotation : modifierList.getAnnotations()) { - if (annotation instanceof ClsAnnotationImpl) { - if (qualifiedName.equals(annotation.getQualifiedName())) { - return true; - } - } - } - } - return false; - } - - private static boolean isKotlinClass(PsiClass psiClass) { - return hasAnnotation(psiClass, JET_CLASS); - } - - private static boolean isKotlinNamespaceClass(PsiClass psiClass) { - if (JvmAbi.PACKAGE_CLASS.equals(psiClass.getName()) && !isKotlinClass(psiClass)) { - for (PsiMethod method : psiClass.getMethods()) { - if (hasAnnotation(method, JET_METHOD)) { - return true; - } - } - } - return false; - } } diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetDummyClassFileViewProvider.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetDummyClassFileViewProvider.java index baa8f2d0354..6348b7be1db 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/JetDummyClassFileViewProvider.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetDummyClassFileViewProvider.java @@ -64,7 +64,7 @@ class JetDummyClassFileViewProvider extends UserDataHolderBase implements FileVi @Override @NotNull - public CharSequence getContents() { + public String getContents() { return myText; }