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.navigation.ItemPresentation; 39 import com.intellij.navigation.ItemPresentationProviders; 40 import com.intellij.openapi.util.Pair; 41 import com.intellij.openapi.vfs.VirtualFile; 42 import com.intellij.psi.*; 43 import com.intellij.psi.PsiModifier.ModifierConstant; 44 import com.intellij.psi.impl.InheritanceImplUtil; 45 import com.intellij.psi.impl.PsiClassImplUtil; 46 import com.intellij.psi.impl.PsiImplUtil; 47 import com.intellij.psi.javadoc.PsiDocComment; 48 import com.intellij.psi.scope.PsiScopeProcessor; 49 import com.intellij.psi.util.PsiUtil; 50 import com.intellij.util.IncorrectOperationException; 51 import com.sun.jdi.Location; 52 import com.sun.jdi.Method; 53 import com.sun.jdi.ReferenceType; 54 import org.jetbrains.annotations.NonNls; 55 import org.jetbrains.annotations.NotNull; 56 import org.jetbrains.annotations.Nullable; 57 import org.jf.smalidea.SmaliIcons; 58 import org.jf.smalidea.psi.SmaliElementTypes; 59 import org.jf.smalidea.psi.iface.SmaliModifierListOwner; 60 import org.jf.smalidea.psi.leaf.SmaliClassDescriptor; 61 import org.jf.smalidea.psi.stub.SmaliClassStub; 62 63 import javax.annotation.Nonnull; 64 import javax.swing.*; 65 import java.util.Collection; 66 import java.util.List; 67 68 public class SmaliClass extends SmaliStubBasedPsiElement<SmaliClassStub> implements PsiClass, SmaliModifierListOwner { SmaliClass(@otNull SmaliClassStub stub)69 public SmaliClass(@NotNull SmaliClassStub stub) { 70 super(stub, SmaliElementTypes.CLASS); 71 } 72 SmaliClass(@otNull ASTNode node)73 public SmaliClass(@NotNull ASTNode node) { 74 super(node); 75 } 76 77 @Nonnull 78 @Override getName()79 public String getName() { 80 String name = getQualifiedName(); 81 if (name == null) { 82 return ""; 83 } 84 int lastDot = name.lastIndexOf('.'); 85 if (lastDot < 0) { 86 return name; 87 } 88 return name.substring(lastDot+1); 89 } 90 getPresentation()91 @Override public ItemPresentation getPresentation() { 92 return ItemPresentationProviders.getItemPresentation(this); 93 } 94 getQualifiedName()95 @Nullable @Override public String getQualifiedName() { 96 SmaliClassStatement classStatement = getStubOrPsiChild(SmaliElementTypes.CLASS_STATEMENT); 97 if (classStatement == null) { 98 return null; 99 } 100 return classStatement.getQualifiedName(); 101 } 102 getPackageName()103 @NotNull public String getPackageName() { 104 String name = getQualifiedName(); 105 if (name == null) { 106 return ""; 107 } 108 int lastDot = name.lastIndexOf('.'); 109 if (lastDot < 0) { 110 return ""; 111 } 112 return name.substring(0, lastDot); 113 } 114 hasTypeParameters()115 @Override public boolean hasTypeParameters() { 116 // TODO: implement generics 117 return false; 118 } 119 isInterface()120 @Override public boolean isInterface() { 121 return hasModifierProperty("interface"); 122 } 123 isAnnotationType()124 @Override public boolean isAnnotationType() { 125 return hasModifierProperty("annotation"); 126 } 127 isEnum()128 @Override public boolean isEnum() { 129 return hasModifierProperty("enum"); 130 } 131 getSuperStatement()132 @Nullable public SmaliSuperStatement getSuperStatement() { 133 return findChildByClass(SmaliSuperStatement.class); 134 } 135 getExtendsList()136 @NotNull @Override public SmaliExtendsList getExtendsList() { 137 return getRequiredStubOrPsiChild(SmaliElementTypes.EXTENDS_LIST); 138 } 139 getImplementsStatements()140 @NotNull public SmaliImplementsStatement[] getImplementsStatements() { 141 return findChildrenByClass(SmaliImplementsStatement.class); 142 } 143 getImplementsList()144 @NotNull @Override public SmaliImplementsList getImplementsList() { 145 return getRequiredStubOrPsiChild(SmaliElementTypes.IMPLEMENTS_LIST); 146 } 147 getExtendsListTypes()148 @NotNull @Override public SmaliClassType[] getExtendsListTypes() { 149 return getExtendsList().getReferencedTypes(); 150 } 151 getImplementsListTypes()152 @NotNull @Override public SmaliClassType[] getImplementsListTypes() { 153 return getImplementsList().getReferencedTypes(); 154 } 155 getSuperClass()156 @Nullable @Override public PsiClass getSuperClass() { 157 return PsiClassImplUtil.getSuperClass(this); 158 } 159 getInterfaces()160 @Override public PsiClass[] getInterfaces() { 161 return PsiClassImplUtil.getInterfaces(this); 162 } 163 getSupers()164 @NotNull @Override public PsiClass[] getSupers() { 165 return PsiClassImplUtil.getSupers(this); 166 } 167 getSuperTypes()168 @NotNull @Override public PsiClassType[] getSuperTypes() { 169 return PsiClassImplUtil.getSuperTypes(this); 170 } 171 getFields()172 @NotNull @Override public SmaliField[] getFields() { 173 SmaliField[] fields = getStubOrPsiChildren(SmaliElementTypes.FIELD, new SmaliField[0]); 174 List<SmaliField> filteredFields = null; 175 for (int i=fields.length-1; i>=0; i--) { 176 SmaliField field = fields[i]; 177 if (field.getName() == null) { 178 if (filteredFields == null) { 179 filteredFields = Lists.newArrayList(fields); 180 } 181 filteredFields.remove(i); 182 } 183 } 184 if (filteredFields != null) { 185 return filteredFields.toArray(new SmaliField[filteredFields.size()]); 186 } 187 return fields; 188 } 189 getMethods()190 @NotNull @Override public SmaliMethod[] getMethods() { 191 return getStubOrPsiChildren(SmaliElementTypes.METHOD, new SmaliMethod[0]); 192 } 193 getConstructors()194 @NotNull @Override public PsiMethod[] getConstructors() { 195 return PsiImplUtil.getConstructors(this); 196 } 197 getInnerClasses()198 @NotNull @Override public PsiClass[] getInnerClasses() { 199 return new PsiClass[0]; 200 } 201 getInitializers()202 @NotNull @Override public PsiClassInitializer[] getInitializers() { 203 // TODO: do we need to return the <clinit> method here? 204 return new PsiClassInitializer[0]; 205 } 206 getAllFields()207 @NotNull @Override public PsiField[] getAllFields() { 208 return PsiClassImplUtil.getAllFields(this); 209 } 210 getAllMethods()211 @NotNull @Override public PsiMethod[] getAllMethods() { 212 return PsiClassImplUtil.getAllMethods(this); 213 } 214 getAllInnerClasses()215 @NotNull @Override public PsiClass[] getAllInnerClasses() { 216 return new PsiClass[0]; 217 } 218 findFieldByName(@onNls String name, boolean checkBases)219 @Nullable @Override public PsiField findFieldByName(@NonNls String name, boolean checkBases) { 220 return PsiClassImplUtil.findFieldByName(this, name, checkBases); 221 } 222 findMethodBySignature(PsiMethod patternMethod, boolean checkBases)223 @Nullable @Override public PsiMethod findMethodBySignature(PsiMethod patternMethod, boolean checkBases) { 224 return PsiClassImplUtil.findMethodBySignature(this, patternMethod, checkBases); 225 } 226 findMethodsBySignature(PsiMethod patternMethod, boolean checkBases)227 @NotNull @Override public PsiMethod[] findMethodsBySignature(PsiMethod patternMethod, boolean checkBases) { 228 return PsiClassImplUtil.findMethodsBySignature(this, patternMethod, checkBases); 229 } 230 findMethodsByName(@onNls String name, boolean checkBases)231 @NotNull @Override public PsiMethod[] findMethodsByName(@NonNls String name, boolean checkBases) { 232 return PsiClassImplUtil.findMethodsByName(this, name, checkBases); 233 } 234 235 @NotNull @Override findMethodsAndTheirSubstitutorsByName(@onNls String name, boolean checkBases)236 public List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(@NonNls String name, boolean checkBases) { 237 return PsiClassImplUtil.findMethodsAndTheirSubstitutorsByName(this, name, checkBases); 238 } 239 getAllMethodsAndTheirSubstitutors()240 @NotNull @Override public List<Pair<PsiMethod, PsiSubstitutor>> getAllMethodsAndTheirSubstitutors() { 241 return PsiClassImplUtil.getAllWithSubstitutorsByMap(this, PsiClassImplUtil.MemberType.METHOD); 242 } 243 findInnerClassByName(@onNls String name, boolean checkBases)244 @Nullable @Override public PsiClass findInnerClassByName(@NonNls String name, boolean checkBases) { 245 return null; 246 } 247 getLBrace()248 @Nullable @Override public PsiElement getLBrace() { 249 return null; 250 } 251 getRBrace()252 @Nullable @Override public PsiElement getRBrace() { 253 return null; 254 } 255 getClassStatement()256 @Nullable public SmaliClassStatement getClassStatement() { 257 return getStubOrPsiChild(SmaliElementTypes.CLASS_STATEMENT); 258 } 259 getNameIdentifier()260 @Nullable @Override public SmaliClassDescriptor getNameIdentifier() { 261 SmaliClassStatement classStatement = getClassStatement(); 262 if (classStatement == null) { 263 return null; 264 } 265 return classStatement.getNameIdentifier(); 266 } 267 getScope()268 @Override public PsiElement getScope() { 269 return null; 270 } 271 isInheritor(@otNull PsiClass baseClass, boolean checkDeep)272 @Override public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) { 273 return InheritanceImplUtil.isInheritor(this, baseClass, checkDeep); 274 } 275 isInheritorDeep(PsiClass baseClass, @Nullable PsiClass classToByPass)276 @Override public boolean isInheritorDeep(PsiClass baseClass, @Nullable PsiClass classToByPass) { 277 return InheritanceImplUtil.isInheritorDeep(this, baseClass, classToByPass); 278 } 279 getContainingClass()280 @Nullable @Override public PsiClass getContainingClass() { 281 return null; 282 } 283 getVisibleSignatures()284 @NotNull @Override public Collection<HierarchicalMethodSignature> getVisibleSignatures() { 285 return ImmutableList.of(); 286 } 287 setName(@onNls @otNull String name)288 @Override public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException { 289 SmaliClassStatement classStatement = getClassStatement(); 290 if (classStatement == null) { 291 throw new IncorrectOperationException(); 292 } 293 294 SmaliClassTypeElement classTypeElement = classStatement.getNameElement(); 295 if (classTypeElement == null) { 296 throw new IncorrectOperationException(); 297 } 298 299 String expectedPath = "/" + getName() + ".smali"; 300 301 VirtualFile virtualFile = this.getContainingFile().getVirtualFile(); 302 if (virtualFile != null) { 303 String actualPath = virtualFile.getPath(); 304 if (actualPath.endsWith(expectedPath)) { 305 getContainingFile().setName(name + ".smali"); 306 } 307 } 308 309 String packageName = this.getPackageName(); 310 String newName; 311 if (packageName.length() > 0) { 312 newName = packageName + "." + name; 313 } else { 314 newName = name; 315 } 316 classTypeElement.handleElementRename(newName); 317 return this; 318 } 319 setPackageName(@onNls @otNull String packageName)320 public void setPackageName(@NonNls @NotNull String packageName) { 321 SmaliClassStatement classStatement = getClassStatement(); 322 if (classStatement == null) { 323 throw new IncorrectOperationException(); 324 } 325 326 SmaliClassTypeElement classTypeElement = classStatement.getNameElement(); 327 if (classTypeElement == null) { 328 throw new IncorrectOperationException(); 329 } 330 331 String newName; 332 if (packageName.length() > 0) { 333 newName = packageName + "." + getName(); 334 } else { 335 newName = getName(); 336 } 337 338 classTypeElement.handleElementRename(newName); 339 } 340 getDocComment()341 @Nullable @Override public PsiDocComment getDocComment() { 342 return null; 343 } 344 isDeprecated()345 @Override public boolean isDeprecated() { 346 return false; 347 } 348 getTypeParameterList()349 @Nullable @Override public PsiTypeParameterList getTypeParameterList() { 350 return null; 351 } 352 getTypeParameters()353 @NotNull @Override public PsiTypeParameter[] getTypeParameters() { 354 return new PsiTypeParameter[0]; 355 } 356 getModifierList()357 @Nullable @Override public SmaliModifierList getModifierList() { 358 SmaliClassStatement classStatement = getStubOrPsiChild(SmaliElementTypes.CLASS_STATEMENT); 359 if (classStatement == null) { 360 return null; 361 } 362 return classStatement.getModifierList(); 363 } 364 hasModifierProperty(@odifierConstant @onNls @otNull String name)365 @Override public boolean hasModifierProperty(@ModifierConstant @NonNls @NotNull String name) { 366 SmaliModifierList smaliModifierList = getModifierList(); 367 return smaliModifierList != null && smaliModifierList.hasModifierProperty(name); 368 } 369 getAnnotations()370 @NotNull @Override public SmaliAnnotation[] getAnnotations() { 371 return getStubOrPsiChildren(SmaliElementTypes.ANNOTATION, new SmaliAnnotation[0]); 372 } 373 getApplicableAnnotations()374 @NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() { 375 return getAnnotations(); 376 } 377 findAnnotation(@otNull @onNls String qualifiedName)378 @Nullable @Override public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) { 379 for (SmaliAnnotation annotation: getAnnotations()) { 380 if (qualifiedName.equals(annotation.getQualifiedName())) { 381 return annotation; 382 } 383 } 384 return null; 385 } 386 addAnnotation(@otNull @onNls String qualifiedName)387 @NotNull @Override public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) { 388 // TODO: implement this 389 return null; 390 } 391 getLocationForSourcePosition(@onnull ReferenceType type, @Nonnull SourcePosition position)392 @Nullable public Location getLocationForSourcePosition(@Nonnull ReferenceType type, 393 @Nonnull SourcePosition position) { 394 395 SmaliMethod[] smaliMethods = findChildrenByType(SmaliElementTypes.METHOD, SmaliMethod.class); 396 397 for (SmaliMethod smaliMethod: smaliMethods) { 398 //TODO: check the start line+end line of the method 399 int offset = smaliMethod.getOffsetForLine(position.getLine()); 400 if (offset != -1) { 401 List<Method> methods = type.methodsByName(smaliMethod.getName(), 402 smaliMethod.getMethodPrototype().getText()); 403 if (methods.size() > 0) { 404 return methods.get(0).locationOfCodeIndex(offset/2); 405 } 406 } 407 } 408 return null; 409 } 410 411 @Override processDeclarations(@otNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place)412 public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, 413 PsiElement lastParent, @NotNull PsiElement place) { 414 return PsiClassImplUtil.processDeclarationsInClass(this, processor, state, null, lastParent, place, 415 PsiUtil.getLanguageLevel(place), false); 416 } 417 getElementIcon(@conFlags int flags)418 @Nullable @Override protected Icon getElementIcon(@IconFlags int flags) { 419 return SmaliIcons.SmaliIcon; 420 } 421 }