Home | History | Annotate | Download | only in impl
      1 /*
      2  * Copyright 2014, Google Inc.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 package org.jf.smalidea.psi.impl;
     33 
     34 import com.google.common.collect.ImmutableList;
     35 import com.google.common.collect.Lists;
     36 import com.intellij.debugger.SourcePosition;
     37 import com.intellij.lang.ASTNode;
     38 import com.intellij.openapi.util.Pair;
     39 import com.intellij.openapi.vfs.VirtualFile;
     40 import com.intellij.psi.*;
     41 import com.intellij.psi.PsiModifier.ModifierConstant;
     42 import com.intellij.psi.impl.InheritanceImplUtil;
     43 import com.intellij.psi.impl.PsiClassImplUtil;
     44 import com.intellij.psi.impl.PsiImplUtil;
     45 import com.intellij.psi.javadoc.PsiDocComment;
     46 import com.intellij.psi.scope.PsiScopeProcessor;
     47 import com.intellij.psi.util.PsiUtil;
     48 import com.intellij.util.IncorrectOperationException;
     49 import com.sun.jdi.Location;
     50 import com.sun.jdi.Method;
     51 import com.sun.jdi.ReferenceType;
     52 import org.jetbrains.annotations.NonNls;
     53 import org.jetbrains.annotations.NotNull;
     54 import org.jetbrains.annotations.Nullable;
     55 import org.jf.smalidea.SmaliIcons;
     56 import org.jf.smalidea.psi.SmaliElementTypes;
     57 import org.jf.smalidea.psi.iface.SmaliModifierListOwner;
     58 import org.jf.smalidea.psi.leaf.SmaliClassDescriptor;
     59 import org.jf.smalidea.psi.stub.SmaliClassStub;
     60 
     61 import javax.annotation.Nonnull;
     62 import javax.swing.*;
     63 import java.util.Collection;
     64 import java.util.List;
     65 
     66 public class SmaliClass extends SmaliStubBasedPsiElement<SmaliClassStub> implements PsiClass, SmaliModifierListOwner {
     67     public SmaliClass(@NotNull SmaliClassStub stub) {
     68         super(stub, SmaliElementTypes.CLASS);
     69     }
     70 
     71     public SmaliClass(@NotNull ASTNode node) {
     72         super(node);
     73     }
     74 
     75     @Nonnull
     76     @Override
     77     public String getName() {
     78         String name = getQualifiedName();
     79         if (name == null) {
     80             return "";
     81         }
     82         int lastDot = name.lastIndexOf('.');
     83         if (lastDot < 0) {
     84             return name;
     85         }
     86         return name.substring(lastDot+1);
     87     }
     88 
     89     @Nullable @Override public String getQualifiedName() {
     90         SmaliClassStatement classStatement = getStubOrPsiChild(SmaliElementTypes.CLASS_STATEMENT);
     91         if (classStatement == null) {
     92             return null;
     93         }
     94         return classStatement.getQualifiedName();
     95     }
     96 
     97     @NotNull public String getPackageName() {
     98         String name = getQualifiedName();
     99         if (name == null) {
    100             return "";
    101         }
    102         int lastDot = name.lastIndexOf('.');
    103         if (lastDot < 0) {
    104             return "";
    105         }
    106         return name.substring(0, lastDot);
    107     }
    108 
    109     @Override public boolean hasTypeParameters() {
    110         // TODO: implement generics
    111         return false;
    112     }
    113 
    114     @Override public boolean isInterface() {
    115         return hasModifierProperty("interface");
    116     }
    117 
    118     @Override public boolean isAnnotationType() {
    119         return hasModifierProperty("annotation");
    120     }
    121 
    122     @Override public boolean isEnum() {
    123         return hasModifierProperty("enum");
    124     }
    125 
    126     @Nullable public SmaliSuperStatement getSuperStatement() {
    127         return findChildByClass(SmaliSuperStatement.class);
    128     }
    129 
    130     @NotNull @Override public SmaliExtendsList getExtendsList() {
    131         return getRequiredStubOrPsiChild(SmaliElementTypes.EXTENDS_LIST);
    132     }
    133 
    134     @NotNull public SmaliImplementsStatement[] getImplementsStatements() {
    135         return findChildrenByClass(SmaliImplementsStatement.class);
    136     }
    137 
    138     @NotNull @Override public SmaliImplementsList getImplementsList() {
    139         return getRequiredStubOrPsiChild(SmaliElementTypes.IMPLEMENTS_LIST);
    140     }
    141 
    142     @NotNull @Override public SmaliClassType[] getExtendsListTypes() {
    143         return getExtendsList().getReferencedTypes();
    144     }
    145 
    146     @NotNull @Override public SmaliClassType[] getImplementsListTypes() {
    147         return getImplementsList().getReferencedTypes();
    148     }
    149 
    150     @Nullable @Override public PsiClass getSuperClass() {
    151         return PsiClassImplUtil.getSuperClass(this);
    152     }
    153 
    154     @Override public PsiClass[] getInterfaces() {
    155         return PsiClassImplUtil.getInterfaces(this);
    156     }
    157 
    158     @NotNull @Override public PsiClass[] getSupers() {
    159         return PsiClassImplUtil.getSupers(this);
    160     }
    161 
    162     @NotNull @Override public PsiClassType[] getSuperTypes() {
    163         return PsiClassImplUtil.getSuperTypes(this);
    164     }
    165 
    166     @NotNull @Override public SmaliField[] getFields() {
    167         SmaliField[] fields = getStubOrPsiChildren(SmaliElementTypes.FIELD, new SmaliField[0]);
    168         List<SmaliField> filteredFields = null;
    169         for (int i=fields.length-1; i>=0; i--) {
    170             SmaliField field = fields[i];
    171             if (field.getName() == null) {
    172                 if (filteredFields == null) {
    173                     filteredFields = Lists.newArrayList(fields);
    174                 }
    175                 filteredFields.remove(i);
    176             }
    177         }
    178         if (filteredFields != null) {
    179             return filteredFields.toArray(new SmaliField[filteredFields.size()]);
    180         }
    181         return fields;
    182     }
    183 
    184     @NotNull @Override public SmaliMethod[] getMethods() {
    185         return getStubOrPsiChildren(SmaliElementTypes.METHOD, new SmaliMethod[0]);
    186     }
    187 
    188     @NotNull @Override public PsiMethod[] getConstructors() {
    189         return PsiImplUtil.getConstructors(this);
    190     }
    191 
    192     @NotNull @Override public PsiClass[] getInnerClasses() {
    193         return new PsiClass[0];
    194     }
    195 
    196     @NotNull @Override public PsiClassInitializer[] getInitializers() {
    197         // TODO: do we need to return the <clinit> method here?
    198         return new PsiClassInitializer[0];
    199     }
    200 
    201     @NotNull @Override public PsiField[] getAllFields() {
    202         return PsiClassImplUtil.getAllFields(this);
    203     }
    204 
    205     @NotNull @Override public PsiMethod[] getAllMethods() {
    206         return PsiClassImplUtil.getAllMethods(this);
    207     }
    208 
    209     @NotNull @Override public PsiClass[] getAllInnerClasses() {
    210         return new PsiClass[0];
    211     }
    212 
    213     @Nullable @Override public PsiField findFieldByName(@NonNls String name, boolean checkBases) {
    214         return PsiClassImplUtil.findFieldByName(this, name, checkBases);
    215     }
    216 
    217     @Nullable @Override public PsiMethod findMethodBySignature(PsiMethod patternMethod, boolean checkBases) {
    218         return PsiClassImplUtil.findMethodBySignature(this, patternMethod, checkBases);
    219     }
    220 
    221     @NotNull @Override public PsiMethod[] findMethodsBySignature(PsiMethod patternMethod, boolean checkBases) {
    222         return PsiClassImplUtil.findMethodsBySignature(this, patternMethod, checkBases);
    223     }
    224 
    225     @NotNull @Override public PsiMethod[] findMethodsByName(@NonNls String name, boolean checkBases) {
    226         return PsiClassImplUtil.findMethodsByName(this, name, checkBases);
    227     }
    228 
    229     @NotNull @Override
    230     public List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(@NonNls String name, boolean checkBases) {
    231         return PsiClassImplUtil.findMethodsAndTheirSubstitutorsByName(this, name, checkBases);
    232     }
    233 
    234     @NotNull @Override public List<Pair<PsiMethod, PsiSubstitutor>> getAllMethodsAndTheirSubstitutors() {
    235         return PsiClassImplUtil.getAllWithSubstitutorsByMap(this, PsiClassImplUtil.MemberType.METHOD);
    236     }
    237 
    238     @Nullable @Override public PsiClass findInnerClassByName(@NonNls String name, boolean checkBases) {
    239         return null;
    240     }
    241 
    242     @Nullable @Override public PsiElement getLBrace() {
    243         return null;
    244     }
    245 
    246     @Nullable @Override public PsiElement getRBrace() {
    247         return null;
    248     }
    249 
    250     @Nullable public SmaliClassStatement getClassStatement() {
    251         return getStubOrPsiChild(SmaliElementTypes.CLASS_STATEMENT);
    252     }
    253 
    254     @Nullable @Override public SmaliClassDescriptor getNameIdentifier() {
    255         SmaliClassStatement classStatement = getClassStatement();
    256         if (classStatement == null) {
    257             return null;
    258         }
    259         return classStatement.getNameIdentifier();
    260     }
    261 
    262     @Override public PsiElement getScope() {
    263         return null;
    264     }
    265 
    266     @Override public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) {
    267         return InheritanceImplUtil.isInheritor(this, baseClass, checkDeep);
    268     }
    269 
    270     @Override public boolean isInheritorDeep(PsiClass baseClass, @Nullable PsiClass classToByPass) {
    271         return InheritanceImplUtil.isInheritorDeep(this, baseClass, classToByPass);
    272     }
    273 
    274     @Nullable @Override public PsiClass getContainingClass() {
    275         return null;
    276     }
    277 
    278     @NotNull @Override public Collection<HierarchicalMethodSignature> getVisibleSignatures() {
    279         return ImmutableList.of();
    280     }
    281 
    282     @Override public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
    283         SmaliClassStatement classStatement = getClassStatement();
    284         if (classStatement == null) {
    285             throw new IncorrectOperationException();
    286         }
    287 
    288         SmaliClassTypeElement classTypeElement = classStatement.getNameElement();
    289         if (classTypeElement == null) {
    290             throw new IncorrectOperationException();
    291         }
    292 
    293         String expectedPath = "/" + getName() + ".smali";
    294 
    295         VirtualFile virtualFile = this.getContainingFile().getVirtualFile();
    296         if (virtualFile != null) {
    297             String actualPath = virtualFile.getPath();
    298             if (actualPath.endsWith(expectedPath)) {
    299                 getContainingFile().setName(name + ".smali");
    300             }
    301         }
    302 
    303         String packageName = this.getPackageName();
    304         String newName;
    305         if (packageName.length() > 0) {
    306             newName = packageName + "." + name;
    307         } else {
    308             newName = name;
    309         }
    310         classTypeElement.handleElementRename(newName);
    311         return this;
    312     }
    313 
    314     public void setPackageName(@NonNls @NotNull String packageName) {
    315         SmaliClassStatement classStatement = getClassStatement();
    316         if (classStatement == null) {
    317             throw new IncorrectOperationException();
    318         }
    319 
    320         SmaliClassTypeElement classTypeElement = classStatement.getNameElement();
    321         if (classTypeElement == null) {
    322             throw new IncorrectOperationException();
    323         }
    324 
    325         String newName;
    326         if (packageName.length() > 0) {
    327             newName = packageName + "." + getName();
    328         } else {
    329             newName = getName();
    330         }
    331 
    332         classTypeElement.handleElementRename(newName);
    333     }
    334 
    335     @Nullable @Override public PsiDocComment getDocComment() {
    336         return null;
    337     }
    338 
    339     @Override public boolean isDeprecated() {
    340         return false;
    341     }
    342 
    343     @Nullable @Override public PsiTypeParameterList getTypeParameterList() {
    344         return null;
    345     }
    346 
    347     @NotNull @Override public PsiTypeParameter[] getTypeParameters() {
    348         return new PsiTypeParameter[0];
    349     }
    350 
    351     @Nullable @Override public SmaliModifierList getModifierList() {
    352         SmaliClassStatement classStatement = getStubOrPsiChild(SmaliElementTypes.CLASS_STATEMENT);
    353         if (classStatement == null) {
    354             return null;
    355         }
    356         return classStatement.getModifierList();
    357     }
    358 
    359     @Override public boolean hasModifierProperty(@ModifierConstant @NonNls @NotNull String name) {
    360         SmaliModifierList smaliModifierList = getModifierList();
    361         return smaliModifierList != null && smaliModifierList.hasModifierProperty(name);
    362     }
    363 
    364     @NotNull @Override public SmaliAnnotation[] getAnnotations() {
    365         return getStubOrPsiChildren(SmaliElementTypes.ANNOTATION, new SmaliAnnotation[0]);
    366     }
    367 
    368     @NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() {
    369         return getAnnotations();
    370     }
    371 
    372     @Nullable @Override public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
    373         for (SmaliAnnotation annotation: getAnnotations()) {
    374             if (qualifiedName.equals(annotation.getQualifiedName())) {
    375                 return annotation;
    376             }
    377         }
    378         return null;
    379     }
    380 
    381     @NotNull @Override public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) {
    382         // TODO: implement this
    383         return null;
    384     }
    385 
    386     @Nullable public Location getLocationForSourcePosition(@Nonnull ReferenceType type,
    387                                                            @Nonnull SourcePosition position) {
    388 
    389         SmaliMethod[] smaliMethods = findChildrenByType(SmaliElementTypes.METHOD, SmaliMethod.class);
    390 
    391         for (SmaliMethod smaliMethod: smaliMethods) {
    392             //TODO: check the start line+end line of the method
    393             int offset = smaliMethod.getOffsetForLine(position.getLine());
    394             if (offset != -1) {
    395                 List<Method> methods = type.methodsByName(smaliMethod.getName(),
    396                         smaliMethod.getMethodPrototype().getText());
    397                 if (methods.size() > 0) {
    398                     return methods.get(0).locationOfCodeIndex(offset/2);
    399                 }
    400             }
    401         }
    402         return null;
    403     }
    404 
    405     @Override
    406     public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state,
    407                                        PsiElement lastParent, @NotNull PsiElement place) {
    408         return PsiClassImplUtil.processDeclarationsInClass(this, processor, state, null, lastParent, place,
    409                 PsiUtil.getLanguageLevel(place), false);
    410     }
    411 
    412     @Nullable @Override protected Icon getElementIcon(@IconFlags int flags) {
    413         return SmaliIcons.SmaliIcon;
    414     }
    415 }