mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java] better inner class detection (IDEA-132606)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2016 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.
|
||||
@@ -17,19 +17,29 @@ package com.intellij.psi;
|
||||
|
||||
import com.intellij.ide.highlighter.JavaClassFileType;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.FileIndexFacade;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.impl.PsiManagerImpl;
|
||||
import com.intellij.psi.impl.compiled.ClsFileImpl;
|
||||
import com.intellij.psi.impl.file.PsiBinaryFileImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader;
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
public class ClassFileViewProvider extends SingleRootFileViewProvider {
|
||||
private static final Key<Boolean> IS_INNER_CLASS = Key.create("java.is.inner.class.key");
|
||||
|
||||
public ClassFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile file) {
|
||||
super(manager, file);
|
||||
}
|
||||
@@ -45,7 +55,7 @@ public class ClassFileViewProvider extends SingleRootFileViewProvider {
|
||||
return new PsiBinaryFileImpl((PsiManagerImpl)getManager(), this);
|
||||
}
|
||||
|
||||
// skip inners & anonymous
|
||||
// skip inner & anonymous
|
||||
if (isInnerClass(file)) return null;
|
||||
|
||||
return new ClsFileImpl(this);
|
||||
@@ -53,16 +63,45 @@ public class ClassFileViewProvider extends SingleRootFileViewProvider {
|
||||
|
||||
public static boolean isInnerClass(@NotNull VirtualFile file) {
|
||||
String name = file.getNameWithoutExtension();
|
||||
int index = name.lastIndexOf('$', name.length());
|
||||
int index = name.lastIndexOf('$');
|
||||
if (index > 0 && index < name.length() - 1) {
|
||||
String supposedParentName = name.substring(0, index) + ".class";
|
||||
if (file.getParent().findChild(supposedParentName) != null) {
|
||||
return true;
|
||||
String parentName = name.substring(0, index), childName = name.substring(index + 1);
|
||||
if (file.getParent().findChild(parentName + ".class") != null) {
|
||||
return isInnerClass(file, parentName, childName);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isInnerClass(VirtualFile file, final String parentName, final String childName) {
|
||||
Boolean isInner = IS_INNER_CLASS.get(file);
|
||||
if (isInner != null) return isInner;
|
||||
|
||||
final Ref<Boolean> ref = Ref.create(Boolean.FALSE);
|
||||
try {
|
||||
new ClassReader(file.contentsToByteArray(false)).accept(new ClassVisitor(Opcodes.ASM5) {
|
||||
@Override
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
ref.set(Boolean.TRUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outer, String inner, int access) {
|
||||
if (inner == null || childName.equals(inner) && outer != null && parentName.equals(outer.substring(outer.lastIndexOf('/') + 1))) {
|
||||
ref.set(Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Logger.getInstance(ClassFileViewProvider.class).info(e);
|
||||
}
|
||||
|
||||
isInner = ref.get();
|
||||
IS_INNER_CLASS.set(file, isInner);
|
||||
return isInner;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SingleRootFileViewProvider createCopy(@NotNull VirtualFile copy) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2016 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.
|
||||
@@ -51,30 +51,32 @@ public class ClassFileStubBuilder implements BinaryFileStubBuilder {
|
||||
byte[] content = fileContent.getContent();
|
||||
|
||||
try {
|
||||
file.setPreloadedContentHint(content);
|
||||
ClassFileDecompilers.Decompiler decompiler = ClassFileDecompilers.find(file);
|
||||
if (decompiler instanceof Full) {
|
||||
return ((Full)decompiler).getStubBuilder().buildFileStub(fileContent);
|
||||
try {
|
||||
file.setPreloadedContentHint(content);
|
||||
ClassFileDecompilers.Decompiler decompiler = ClassFileDecompilers.find(file);
|
||||
if (decompiler instanceof Full) {
|
||||
return ((Full)decompiler).getStubBuilder().buildFileStub(fileContent);
|
||||
}
|
||||
}
|
||||
catch (ClsFormatException e) {
|
||||
LOG.debug(e);
|
||||
}
|
||||
|
||||
try {
|
||||
PsiFileStub<?> stub = ClsFileImpl.buildFileStub(file, content);
|
||||
if (stub == null && fileContent.getFileName().indexOf('$') >= 0) {
|
||||
LOG.info("No stub built for file " + fileContent);
|
||||
}
|
||||
return stub;
|
||||
}
|
||||
catch (ClsFormatException e) {
|
||||
LOG.debug(e);
|
||||
}
|
||||
}
|
||||
catch (ClsFormatException e) {
|
||||
LOG.debug(e);
|
||||
}
|
||||
finally {
|
||||
file.setPreloadedContentHint(null);
|
||||
}
|
||||
|
||||
try {
|
||||
PsiFileStub<?> stub = ClsFileImpl.buildFileStub(file, content);
|
||||
if (stub == null && !fileContent.getFileName().contains("$")) {
|
||||
LOG.info("No stub built for file " + fileContent);
|
||||
}
|
||||
return stub;
|
||||
}
|
||||
catch (ClsFormatException e) {
|
||||
LOG.debug(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -99,4 +101,4 @@ public class ClassFileStubBuilder implements BinaryFileStubBuilder {
|
||||
|
||||
return version;
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
package pkg;
|
||||
|
||||
class KindaInner$Class { }
|
||||
@@ -0,0 +1,3 @@
|
||||
package pkg;
|
||||
|
||||
class KindaInner { }
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2016 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.
|
||||
@@ -161,6 +161,14 @@ public class ClsMirrorBuildingTest extends LightIdeaTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testInnerClassDetection() {
|
||||
assertTrue(isInner("Nested$Inner1"));
|
||||
assertTrue(isInner("Nested$Inner1$Inner2"));
|
||||
assertTrue(isInner("NormalClass$1"));
|
||||
assertTrue(isInner("LocalClass$1MyRunnable"));
|
||||
assertFalse(isInner("KindaInner$Class"));
|
||||
}
|
||||
|
||||
private static String getTestDataDir() {
|
||||
return JavaTestUtil.getJavaTestDataPath() + "/psi/cls/mirror/";
|
||||
}
|
||||
@@ -188,4 +196,10 @@ public class ClsMirrorBuildingTest extends LightIdeaTestCase {
|
||||
|
||||
assertEquals(expected, ClsFileImpl.decompile(file).toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isInner(String name) {
|
||||
VirtualFile file = StandardFileSystems.local().findFileByPath(getTestDataDir() + "pkg/" + name + ".class");
|
||||
assertNotNull(file);
|
||||
return ClassFileViewProvider.isInnerClass(file);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user