blob: 77d95a7933e9ae716768b724c1effb4e60c21c2b [file] [log] [blame]
/*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.smalidea.psi.impl;
import com.google.common.collect.Lists;
import com.intellij.debugger.SourcePosition;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.PsiModifier.ModifierConstant;
import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.ReferenceType;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jf.smalidea.SmaliIcons;
import org.jf.smalidea.psi.SmaliElementTypes;
import org.jf.smalidea.psi.iface.SmaliModifierListOwner;
import org.jf.smalidea.psi.stub.SmaliClassStub;
import javax.annotation.Nonnull;
import javax.swing.*;
import java.util.Collection;
import java.util.List;
public class SmaliClass extends SmaliStubBasedPsiElement<SmaliClassStub> implements PsiClass, SmaliModifierListOwner {
public SmaliClass(@NotNull SmaliClassStub stub) {
super(stub, SmaliElementTypes.CLASS);
}
public SmaliClass(@NotNull ASTNode node) {
super(node);
}
@Nonnull
@Override
public String getName() {
String name = getQualifiedName();
if (name == null) {
return "";
}
int lastDot = name.lastIndexOf('.');
if (lastDot < 0) {
return name;
}
return name.substring(lastDot+1);
}
@Nullable @Override public String getQualifiedName() {
SmaliClassStatement classStatement = getStubOrPsiChild(SmaliElementTypes.CLASS_STATEMENT);
if (classStatement == null) {
return null;
}
return classStatement.getQualifiedName();
}
@NotNull public String getPackageName() {
String name = getQualifiedName();
if (name == null) {
return "";
}
int lastDot = name.lastIndexOf('.');
if (lastDot < 0) {
return "";
}
return name.substring(0, lastDot);
}
@Override public boolean hasTypeParameters() {
// TODO: implement generics
return false;
}
@Override public boolean isInterface() {
return hasModifierProperty("interface");
}
@Override public boolean isAnnotationType() {
return hasModifierProperty("annotation");
}
@Override public boolean isEnum() {
return hasModifierProperty("enum");
}
@Nullable public SmaliSuperStatement getSuperStatement() {
return findChildByClass(SmaliSuperStatement.class);
}
@NotNull @Override public SmaliExtendsList getExtendsList() {
return getRequiredStubOrPsiChild(SmaliElementTypes.EXTENDS_LIST);
}
@NotNull public SmaliImplementsStatement[] getImplementsStatements() {
return findChildrenByClass(SmaliImplementsStatement.class);
}
@NotNull @Override public SmaliImplementsList getImplementsList() {
return getRequiredStubOrPsiChild(SmaliElementTypes.IMPLEMENTS_LIST);
}
@NotNull @Override public SmaliClassType[] getExtendsListTypes() {
return getExtendsList().getReferencedTypes();
}
@NotNull @Override public SmaliClassType[] getImplementsListTypes() {
return getImplementsList().getReferencedTypes();
}
@Nullable @Override public PsiClass getSuperClass() {
return PsiClassImplUtil.getSuperClass(this);
}
@Override public PsiClass[] getInterfaces() {
return PsiClassImplUtil.getInterfaces(this);
}
@NotNull @Override public PsiClass[] getSupers() {
return PsiClassImplUtil.getSupers(this);
}
@NotNull @Override public PsiClassType[] getSuperTypes() {
return PsiClassImplUtil.getSuperTypes(this);
}
@NotNull @Override public SmaliField[] getFields() {
SmaliField[] fields = getStubOrPsiChildren(SmaliElementTypes.FIELD, new SmaliField[0]);
List<SmaliField> filteredFields = null;
for (int i=fields.length-1; i>=0; i--) {
SmaliField field = fields[i];
if (field.getName() == null) {
if (filteredFields == null) {
filteredFields = Lists.newArrayList(fields);
}
filteredFields.remove(i);
}
}
if (filteredFields != null) {
return filteredFields.toArray(new SmaliField[filteredFields.size()]);
}
return fields;
}
@NotNull @Override public SmaliMethod[] getMethods() {
return getStubOrPsiChildren(SmaliElementTypes.METHOD, new SmaliMethod[0]);
}
@NotNull @Override public PsiMethod[] getConstructors() {
return PsiImplUtil.getConstructors(this);
}
@NotNull @Override public PsiClass[] getInnerClasses() {
return new PsiClass[0];
}
@NotNull @Override public PsiClassInitializer[] getInitializers() {
// TODO: do we need to return the <clinit> method here?
return new PsiClassInitializer[0];
}
@NotNull @Override public PsiField[] getAllFields() {
return PsiClassImplUtil.getAllFields(this);
}
@NotNull @Override public PsiMethod[] getAllMethods() {
return PsiClassImplUtil.getAllMethods(this);
}
@NotNull @Override public PsiClass[] getAllInnerClasses() {
return new PsiClass[0];
}
@Nullable @Override public PsiField findFieldByName(@NonNls String name, boolean checkBases) {
return PsiClassImplUtil.findFieldByName(this, name, checkBases);
}
@Nullable @Override public PsiMethod findMethodBySignature(PsiMethod patternMethod, boolean checkBases) {
return PsiClassImplUtil.findMethodBySignature(this, patternMethod, checkBases);
}
@NotNull @Override public PsiMethod[] findMethodsBySignature(PsiMethod patternMethod, boolean checkBases) {
return PsiClassImplUtil.findMethodsBySignature(this, patternMethod, checkBases);
}
@NotNull @Override public PsiMethod[] findMethodsByName(@NonNls String name, boolean checkBases) {
return PsiClassImplUtil.findMethodsByName(this, name, checkBases);
}
@NotNull @Override
public List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(@NonNls String name, boolean checkBases) {
return PsiClassImplUtil.findMethodsAndTheirSubstitutorsByName(this, name, checkBases);
}
@NotNull @Override public List<Pair<PsiMethod, PsiSubstitutor>> getAllMethodsAndTheirSubstitutors() {
return PsiClassImplUtil.getAllWithSubstitutorsByMap(this, PsiClassImplUtil.MemberType.METHOD);
}
@Nullable @Override public PsiClass findInnerClassByName(@NonNls String name, boolean checkBases) {
return null;
}
@Nullable @Override public PsiElement getLBrace() {
return null;
}
@Nullable @Override public PsiElement getRBrace() {
return null;
}
@Nullable @Override public PsiIdentifier getNameIdentifier() {
return null;
}
@Override public PsiElement getScope() {
return null;
}
@Override public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) {
return false;
}
@Override public boolean isInheritorDeep(PsiClass baseClass, @Nullable PsiClass classToByPass) {
return false;
}
@Nullable @Override public PsiClass getContainingClass() {
return null;
}
@NotNull @Override public Collection<HierarchicalMethodSignature> getVisibleSignatures() {
return null;
}
@Override public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
return null;
}
@Nullable @Override public PsiDocComment getDocComment() {
return null;
}
@Override public boolean isDeprecated() {
return false;
}
@Nullable @Override public PsiTypeParameterList getTypeParameterList() {
return null;
}
@NotNull @Override public PsiTypeParameter[] getTypeParameters() {
return new PsiTypeParameter[0];
}
@Nullable @Override public SmaliModifierList getModifierList() {
SmaliClassStatement classStatement = getStubOrPsiChild(SmaliElementTypes.CLASS_STATEMENT);
if (classStatement == null) {
return null;
}
return classStatement.getModifierList();
}
@Override public boolean hasModifierProperty(@ModifierConstant @NonNls @NotNull String name) {
SmaliModifierList smaliModifierList = getModifierList();
return smaliModifierList != null && smaliModifierList.hasModifierProperty(name);
}
@NotNull @Override public SmaliAnnotation[] getAnnotations() {
return getStubOrPsiChildren(SmaliElementTypes.ANNOTATION, new SmaliAnnotation[0]);
}
@NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() {
return getAnnotations();
}
@Nullable @Override public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
for (SmaliAnnotation annotation: getAnnotations()) {
if (qualifiedName.equals(annotation.getQualifiedName())) {
return annotation;
}
}
return null;
}
@NotNull @Override public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) {
// TODO: implement this
return null;
}
@Nullable public Location getLocationForSourcePosition(@Nonnull ReferenceType type,
@Nonnull SourcePosition position) {
SmaliMethod[] smaliMethods = findChildrenByType(SmaliElementTypes.METHOD, SmaliMethod.class);
for (SmaliMethod smaliMethod: smaliMethods) {
//TODO: check the start line+end line of the method
int offset = smaliMethod.getOffsetForLine(position.getLine());
if (offset != -1) {
List<Method> methods = type.methodsByName(smaliMethod.getName(),
smaliMethod.getMethodPrototype().getText());
if (methods.size() > 0) {
return methods.get(0).locationOfCodeIndex(offset/2);
}
}
}
return null;
}
@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state,
PsiElement lastParent, @NotNull PsiElement place) {
return PsiClassImplUtil.processDeclarationsInClass(this, processor, state, null, lastParent, place,
PsiUtil.getLanguageLevel(place), false);
}
@Nullable @Override protected Icon getElementIcon(@IconFlags int flags) {
return SmaliIcons.SmaliIcon;
}
}