Get PsiMethod wrappers for properties and property accessors

This commit is contained in:
Nikolay Krasko
2013-05-16 19:07:53 +04:00
parent d97ab29588
commit 312bd6f828
8 changed files with 225 additions and 47 deletions
@@ -31,10 +31,13 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.stubs.PsiFileStub;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.binding.PsiCodegenPredictor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.java.JetClsMethod;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
@@ -42,6 +45,7 @@ import org.jetbrains.jet.utils.KotlinVfsUtil;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
public class LightClassUtil {
private static final Logger LOG = Logger.getInstance(LightClassUtil.class);
@@ -116,42 +120,70 @@ public class LightClassUtil {
return LightClassGenerationSupport.getInstance(classOrObject.getProject()).getPsiClass(classOrObject);
}
@Nullable
public static PsiMethod getLightClassAccessorMethod(@NotNull JetPropertyAccessor accessor) {
return getPsiMethodWrapper(accessor);
}
@NotNull
public static PropertyAccessorsPsiMethods getLightClassPropertyMethods(@NotNull JetProperty property) {
JetPropertyAccessor getter = property.getGetter();
JetPropertyAccessor setter = property.getSetter();
PsiMethod getterWrapper = getter != null ? getLightClassAccessorMethod(getter) : null;
PsiMethod setterWrapper = setter != null ? getLightClassAccessorMethod(setter) : null;
if (getterWrapper == null || setterWrapper == null) {
List<PsiMethod> wrappers = getPsiMethodWrappers(property, true);
assert wrappers.size() <= 2 : "Maximum two wrappers are expected to be generated for property";
for (PsiMethod wrapper : wrappers) {
if (wrapper.getName().startsWith(JvmAbi.SETTER_PREFIX)) {
assert setterWrapper == null : String.format(
"Setter accessor isn't expected to be reassigned (old: %s, new: %s)", setterWrapper, wrapper);
setterWrapper = wrapper;
}
else {
assert getterWrapper == null : String.format(
"Getter accessor isn't expected to be reassigned (old: %s, new: %s)", getterWrapper, wrapper);
getterWrapper = wrapper;
}
}
}
return new PropertyAccessorsPsiMethods(getterWrapper, setterWrapper);
}
@Nullable
public static PsiMethod getLightClassMethod(@NotNull JetNamedFunction function) {
//noinspection unchecked
if (PsiTreeUtil.getParentOfType(function, JetFunction.class, JetProperty.class) != null) {
// Don't genClassOrObject method wrappers for internal declarations. Their classes are not generated during calcStub
// with ClassBuilderMode.SIGNATURES mode, and this produces "Class not found exception" in getDelegate()
return null;
return getPsiMethodWrapper(function);
}
@Nullable
private static PsiMethod getPsiMethodWrapper(@NotNull JetDeclaration declaration) {
List<PsiMethod> wrappers = getPsiMethodWrappers(declaration, false);
return !wrappers.isEmpty() ? wrappers.get(0) : null;
}
@NotNull
private static List<PsiMethod> getPsiMethodWrappers(@NotNull JetDeclaration declaration, boolean collectAll) {
PsiClass psiClass = getWrappingClass(declaration);
if (psiClass == null) {
return Collections.emptyList();
}
Project project = function.getProject();
PsiElement parent = function.getParent();
PsiClass psiClass;
if (parent == function.getContainingFile()) {
// top-level function
JvmClassName jvmClassName = PsiCodegenPredictor.getPredefinedJvmClassName((JetFile) parent, true);
if (jvmClassName == null) return null;
String fqName = jvmClassName.getFqName().getFqName();
psiClass = JavaElementFinder.getInstance(project).findClass(fqName, GlobalSearchScope.allScope(project));
}
else {
if (!(parent instanceof JetClassBody)) return null;
assert parent.getParent() instanceof JetClassOrObject;
// function in a class
JetClassOrObject classOrObject = (JetClassOrObject) parent.getParent();
psiClass = getPsiClass(classOrObject);
}
if (psiClass == null) return null;
List<PsiMethod> methods = new SmartList<PsiMethod>();
for (PsiMethod method : psiClass.getMethods()) {
try {
if (method instanceof PsiCompiledElement && ((PsiCompiledElement) method).getMirror() == function) {
return method;
if (method instanceof JetClsMethod) {
if (method instanceof PsiCompiledElement && ((PsiCompiledElement) method).getMirror() == declaration) {
methods.add(method);
if (!collectAll) {
return methods;
}
}
}
}
catch (ProcessCanceledException e) {
@@ -159,24 +191,94 @@ public class LightClassUtil {
}
catch (Throwable e) {
throw new IllegalStateException(
"Error while wrapping function " + function.getName() +
"Error while wrapping declaration " + declaration.getName() +
"Context\n:" +
String.format("=== In file ===\n" +
"%s\n" +
"===On element===\n" +
"%s\n" +
"===WrappedElement===\n" +
"%s\n",
function.getContainingFile().getText(),
function.getText(),
method.toString()),
"%s\n" +
"=== On element ===\n" +
"%s\n" +
"=== WrappedElement ===\n" +
"%s\n",
declaration.getContainingFile().getText(),
declaration.getText(),
method.toString()),
e
);
}
}
return methods;
}
@Nullable
private static PsiClass getWrappingClass(@NotNull JetDeclaration declaration) {
if (declaration instanceof JetPropertyAccessor) {
PsiElement propertyParent = declaration.getParent();
assert propertyParent instanceof JetProperty : "JetProperty is expected to be parent of accessor";
declaration = (JetProperty) propertyParent;
}
//noinspection unchecked
if (PsiTreeUtil.getParentOfType(declaration, JetFunction.class, JetProperty.class) != null) {
// Can't get wrappers for internal declarations. Their classes are not generated during calcStub
// with ClassBuilderMode.SIGNATURES mode, and this produces "Class not found exception" in getDelegate()
return null;
}
PsiElement parent = declaration.getParent();
if (parent instanceof JetFile) {
// top-level declaration
JvmClassName jvmName = PsiCodegenPredictor.getPredefinedJvmClassName((JetFile) parent, true);
if (jvmName != null) {
Project project = declaration.getProject();
String fqName = jvmName.getFqName().getFqName();
return JavaElementFinder.getInstance(project).findClass(fqName, GlobalSearchScope.allScope(project));
}
}
else if (parent instanceof JetClassBody) {
assert parent.getParent() instanceof JetClassOrObject;
return getPsiClass((JetClassOrObject) parent.getParent());
}
return null;
}
public static class PropertyAccessorsPsiMethods implements Iterable<PsiMethod> {
private final PsiMethod getter;
private final PsiMethod setter;
private final Collection<PsiMethod> accessors = new ArrayList<PsiMethod>(2);
PropertyAccessorsPsiMethods(@Nullable PsiMethod getter, @Nullable PsiMethod setter) {
this.getter = getter;
if (getter != null) {
accessors.add(getter);
}
this.setter = setter;
if (setter != null) {
accessors.add(setter);
}
}
@Nullable
public PsiMethod getGetter() {
return getter;
}
@Nullable
public PsiMethod getSetter() {
return setter;
}
@NotNull
@Override
public Iterator<PsiMethod> iterator() {
return accessors.iterator();
}
}
private LightClassUtil() {}
}