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 }