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.Lists;
     35 import com.intellij.openapi.util.TextRange;
     36 import com.intellij.psi.PsiClass;
     37 import com.intellij.psi.PsiElement;
     38 import com.intellij.psi.PsiReference;
     39 import com.intellij.psi.PsiType;
     40 import com.intellij.psi.impl.light.LightMethodBuilder;
     41 import com.intellij.util.ArrayUtil;
     42 import com.intellij.util.IncorrectOperationException;
     43 import org.jetbrains.annotations.NotNull;
     44 import org.jetbrains.annotations.Nullable;
     45 import org.jf.smalidea.SmaliLanguage;
     46 import org.jf.smalidea.psi.SmaliCompositeElementFactory;
     47 import org.jf.smalidea.psi.SmaliElementTypes;
     48 
     49 import javax.annotation.Nonnull;
     50 import java.util.ArrayList;
     51 import java.util.List;
     52 
     53 public class SmaliMethodReference extends SmaliCompositeElement implements PsiReference {
     54     public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
     55         @Override public SmaliCompositeElement createElement() {
     56             return new SmaliMethodReference();
     57         }
     58     };
     59 
     60     @Override public String getName() {
     61         PsiElement memberName = getMemberName();
     62         if (memberName == null) {
     63             return null;
     64         }
     65         return memberName.getText();
     66     }
     67 
     68     public SmaliMethodReference() {
     69         super(SmaliElementTypes.METHOD_REFERENCE);
     70     }
     71 
     72     @Override public PsiReference getReference() {
     73         return this;
     74     }
     75 
     76     @Override public PsiElement getElement() {
     77         return this;
     78     }
     79 
     80     @Override public TextRange getRangeInElement() {
     81         return new TextRange(0, getTextLength());
     82     }
     83 
     84     @Nullable
     85     public PsiClass getContainingClass() {
     86         SmaliClassTypeElement containingClassReference = getContainingType();
     87         if (containingClassReference == null) {
     88             return null;
     89         }
     90         PsiClass containingClass = containingClassReference.resolve();
     91         if (containingClass == null) {
     92             return null;
     93         }
     94 
     95         return containingClass;
     96     }
     97 
     98     @Nullable
     99     public SmaliClassTypeElement getContainingType() {
    100         return findChildByClass(SmaliClassTypeElement.class);
    101     }
    102 
    103     @Nullable
    104     public SmaliMemberName getMemberName() {
    105         return findChildByClass(SmaliMemberName.class);
    106     }
    107 
    108     @Nonnull
    109     public List<PsiType> getParameterTypes() {
    110         SmaliMethodReferenceParamList paramList = findChildByClass(SmaliMethodReferenceParamList.class);
    111         if (paramList == null) {
    112             return Lists.newArrayList();
    113         }
    114 
    115         SmaliTypeElement[] parameterElements = paramList.getParameterTypes();
    116 
    117         List<PsiType> types = new ArrayList<PsiType>(parameterElements.length);
    118         for (SmaliTypeElement parameterElement: parameterElements) {
    119             types.add(parameterElement.getType());
    120         }
    121         return types;
    122     }
    123 
    124     @Nullable
    125     public SmaliTypeElement getReturnType() {
    126         SmaliTypeElement[] types = findChildrenByClass(SmaliTypeElement.class);
    127         if (types.length < 2) {
    128             return null;
    129         }
    130         return types[1];
    131     }
    132 
    133     @Nullable @Override public PsiElement resolve() {
    134         PsiClass containingClass = getContainingClass();
    135         if (containingClass == null) {
    136             return null;
    137         }
    138 
    139         SmaliMemberName memberName = getMemberName();
    140         if (memberName == null) {
    141             return null;
    142         }
    143 
    144         LightMethodBuilder pattern = new LightMethodBuilder(getManager(), SmaliLanguage.INSTANCE, memberName.getText());
    145 
    146         for (PsiType type: getParameterTypes()) {
    147             pattern.addParameter("", type);
    148         }
    149 
    150         SmaliTypeElement returnTypeElement = getReturnType();
    151         if (returnTypeElement == null) {
    152             return null;
    153         }
    154 
    155         pattern.setMethodReturnType(returnTypeElement.getType());
    156 
    157         // TODO: what about static constructor?
    158         pattern.setConstructor(memberName.getText().equals("<init>"));
    159 
    160         return containingClass.findMethodBySignature(pattern, true);
    161     }
    162 
    163     @NotNull @Override public String getCanonicalText() {
    164         return getText();
    165     }
    166 
    167     @Override public boolean isReferenceTo(PsiElement element) {
    168         return resolve() == element;
    169     }
    170 
    171     @NotNull @Override public Object[] getVariants() {
    172         return ArrayUtil.EMPTY_OBJECT_ARRAY;
    173     }
    174 
    175     @Override public boolean isSoft() {
    176         return false;
    177     }
    178 
    179     @Override public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
    180         SmaliMemberName memberName = getMemberName();
    181         if (memberName == null) {
    182             throw new IncorrectOperationException();
    183         }
    184         memberName.setName(newElementName);
    185         return this;
    186     }
    187 
    188     @Override public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
    189         //TODO: implement this
    190         throw new IncorrectOperationException();
    191     }
    192 }
    193