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( new FqName(packageName), DescriptorSearchRule.INCLUDE_KOTLIN); if (nd != null) { for (DeclarationDescriptor member : sortDeclarations(nd.getMemberScope().getAllDescriptors())) { if (member instanceof ClassDescriptor || member instanceof NamespaceDescriptor) { continue; } appendDescriptor(member, ""); myBuilder.append("\n"); } } } else { ClassDescriptor cd = myJavaDescriptorResolver.resolveClass(psiClass, DescriptorSearchRule.INCLUDE_KOTLIN); if (cd != null) { appendDescriptor(cd, ""); } } JetFile jetFile = JetDummyClassFileViewProvider.createJetFile( myClsFile.getManager(), myClsFile.getVirtualFile(), myBuilder.toString()); for (Map.Entry<PsiElement, TextRange> 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); }
/** * @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<PsiElement, TextRange> myClsMembersToRanges = new HashMap<PsiElement, TextRange>(); private Map<ClsElementImpl, JetDeclaration> myClsElementsToJetElements = new HashMap<ClsElementImpl, JetDeclaration>(); private JavaDescriptorResolver myJavaDescriptorResolver; private DecompiledDataFactory(ClsFileImpl clsFile) { myClsFile = clsFile; Project project = myClsFile.getProject(); JavaSemanticServices jss = new JavaSemanticServices(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( new FqName(packageName), DescriptorSearchRule.INCLUDE_KOTLIN); if (nd != null) { for (DeclarationDescriptor member : sortDeclarations(nd.getMemberScope().getAllDescriptors())) { if (member instanceof ClassDescriptor || member instanceof NamespaceDescriptor) { continue; } appendDescriptor(member, ""); myBuilder.append("\n"); } } } else { ClassDescriptor cd = myJavaDescriptorResolver.resolveClass(psiClass, DescriptorSearchRule.INCLUDE_KOTLIN); if (cd != null) { appendDescriptor(cd, ""); } } JetFile jetFile = JetDummyClassFileViewProvider.createJetFile( myClsFile.getManager(), myClsFile.getVirtualFile(), myBuilder.toString()); for (Map.Entry<PsiElement, TextRange> 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 List<DeclarationDescriptor> sortDeclarations(Collection<DeclarationDescriptor> input) { ArrayList<DeclarationDescriptor> r = new ArrayList<DeclarationDescriptor>(input); Collections.sort( r, new Comparator<DeclarationDescriptor>() { @Override public int compare(DeclarationDescriptor o1, DeclarationDescriptor o2) { return o1.getName().compareTo(o2.getName()); } }); return r; } 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 : sortDeclarations(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; } }