| Home >> All >> com >> port80 >> eclipse >> jdt >> [ graph Javadoc ] |
Source code: com/port80/eclipse/jdt/graph/MethodReferenceASTVisitor.java
1 package com.port80.eclipse.jdt.graph; 2 3 import java.util.HashMap; 4 import java.util.HashSet; 5 import java.util.Iterator; 6 import java.util.List; 7 import java.util.Map; 8 import java.util.Set; 9 import java.util.Stack; 10 11 import org.eclipse.jdt.core.dom.ASTNode; 12 import org.eclipse.jdt.core.dom.ASTVisitor; 13 import org.eclipse.jdt.core.dom.ArrayType; 14 import org.eclipse.jdt.core.dom.ClassInstanceCreation; 15 import org.eclipse.jdt.core.dom.Expression; 16 import org.eclipse.jdt.core.dom.FieldAccess; 17 import org.eclipse.jdt.core.dom.IBinding; 18 import org.eclipse.jdt.core.dom.IMethodBinding; 19 import org.eclipse.jdt.core.dom.ITypeBinding; 20 import org.eclipse.jdt.core.dom.MethodDeclaration; 21 import org.eclipse.jdt.core.dom.MethodInvocation; 22 import org.eclipse.jdt.core.dom.Name; 23 import org.eclipse.jdt.core.dom.PackageDeclaration; 24 import org.eclipse.jdt.core.dom.QualifiedName; 25 import org.eclipse.jdt.core.dom.SimpleName; 26 import org.eclipse.jdt.core.dom.SimpleType; 27 import org.eclipse.jdt.core.dom.SuperConstructorInvocation; 28 import org.eclipse.jdt.core.dom.SuperMethodInvocation; 29 import org.eclipse.jdt.core.dom.ThisExpression; 30 import org.eclipse.jdt.core.dom.TypeDeclaration; 31 32 import com.port80.util.Msg; 33 34 /** 35 * Find method referenced in a given CompilationUnit. 36 * 37 * @author chrisl 38 */ 39 public class MethodReferenceASTVisitor extends ASTVisitor { 40 41 //////////////////////////////////////////////////////////////////////// 42 43 private static final String NAME = "MethodReferenceASTVisitor"; 44 private static final boolean DEBUG = true; 45 private static final boolean TERSE = false; 46 47 public static class Result { 48 /** 49 * Declared types. FullyQualifiedName->ITypeBinding mapping. 50 */ 51 public Set fDeclares; 52 /** 53 * Methods declared in this unit. 54 */ 55 public Set fMethods; 56 /** 57 * Super classes. Fully qualified typename -> ITypeBinding. 58 */ 59 public Map fSuperClasses; 60 /** 61 * FullQualifiedName->ITypeBinding mapping. 62 */ 63 public Map fSuperInterfaces; 64 /** 65 * Referenced types. Source TypeWrapper ->Set(Dest. TypeWrapper )mapping. 66 */ 67 public Map fReferences; 68 /** 69 * Caller(TypeWrapper/MethodWrapper)->Set(callee(MethodWrapper)) mapping. 70 */ 71 public Map fCalls; 72 73 /** Stack of TypeWrapper for type being declared.*/ 74 private Stack fTypeStack; 75 /** Stack of MethodWrapper for method being declared.*/ 76 private Stack fMethodStack; 77 78 public Result() { 79 fReferences = new HashMap(); 80 fSuperClasses = new HashMap(); 81 fSuperInterfaces = new HashMap(); 82 fDeclares = new HashSet(); 83 fMethods = new HashSet(); 84 fCalls = new HashMap(); 85 fTypeStack = new Stack(); 86 fMethodStack = new Stack(); 87 } 88 public Set getDeclares() { 89 return fDeclares; 90 } 91 public Map getSuperClasses() { 92 return fSuperClasses; 93 } 94 public Map getReferences() { 95 return fReferences; 96 } 97 public Map getSuperInterfaces() { 98 return fSuperInterfaces; 99 } 100 public Set getMethods() { 101 return fMethods; 102 } 103 public Map getCalls() { 104 return fCalls; 105 } 106 public void declareTypeStart(ITypeBinding type) { 107 if (type == null) { 108 Msg.err(NAME + ".declareTypeStart(): type==null"); 109 return; 110 } 111 TypeWrapper wrapper = new TypeWrapper(type); 112 fTypeStack.push(wrapper); 113 } 114 public void declareTypeEnd(ITypeBinding type) { 115 if (type != null) 116 fTypeStack.pop(); 117 } 118 public void declareMethodStart(IMethodBinding method) { 119 if (method == null) { 120 Msg.err(NAME + ".declareMethodStart(): method==null"); 121 return; 122 } 123 MethodWrapper wrapper = new MethodWrapper(method); 124 fMethodStack.push(wrapper); 125 fMethods.add(wrapper); 126 if (DEBUG) 127 Msg.println(NAME + ".declareMethod(): " + wrapper.getNotSoSimpleSignature()); 128 } 129 public void declareMethodEnd(IMethodBinding method) { 130 if (method != null) 131 fMethodStack.pop(); 132 } 133 public TypeWrapper getCurrentType() { 134 if (fTypeStack.isEmpty()) 135 return null; 136 return (TypeWrapper) fTypeStack.peek(); 137 } 138 public MethodWrapper getCurrentMethod() { 139 if (fMethodStack.isEmpty()) 140 return null; 141 return (MethodWrapper) fMethodStack.peek(); 142 } 143 public void addCall(IMethodBinding method) { 144 MethodWrapper callee = new MethodWrapper(method); 145 MethodWrapper caller = null; 146 if (!fMethodStack.isEmpty()) 147 caller = (MethodWrapper) fMethodStack.peek(); 148 if (caller != null) { 149 Set callees = (Set) fCalls.get(caller); 150 if (callees == null) { 151 callees = new HashSet(); 152 fCalls.put(caller, callees); 153 } 154 callees.add(callee); 155 if (DEBUG) 156 Msg.println( 157 NAME 158 + ".Result.addCall(): method:\n\tcaller=" 159 + caller.getNotSoSimpleSignature() 160 + "\n\tcallee=" 161 + callee.getNotSoSimpleSignature()); 162 } else { 163 TypeWrapper type = (TypeWrapper) fTypeStack.peek(); 164 Set callees = (Set) fCalls.get(type); 165 if (callees == null) { 166 callees = new HashSet(); 167 fCalls.put(type, callees); 168 } 169 callees.add(callee); 170 if (DEBUG) 171 Msg.println( 172 NAME 173 + ".Result.addCall(): type:\n\ttype=" 174 + type.getFullName() 175 + "\n\tcallee=" 176 + callee.getNotSoSimpleSignature()); 177 } 178 } 179 public void addReference(ITypeBinding type) { 180 TypeWrapper referencee = new TypeWrapper(type); 181 TypeWrapper referer = getCurrentType(); 182 if (referer == null) { 183 Msg.err("No caller"); 184 return; 185 } 186 Set referencees = (Set) fReferences.get(referer); 187 if (referencees == null) { 188 referencees = new HashSet(); 189 fReferences.put(referer, referencees); 190 } 191 referencees.add(referencee); 192 if (DEBUG) 193 Msg.println( 194 NAME 195 + ".Result.addReference(): referer\n\treferer=" 196 + referer.getFullName() 197 + "\n\treferencee=" 198 + referencee.getFullName()); 199 } 200 } 201 202 private Result fResult; 203 204 //////////////////////////////////////////////////////////////////////// 205 206 /** 207 * @return Type references in a (String fullTypeName) -> (ITypeBinding type) hash. 208 */ 209 public static MethodReferenceASTVisitor startAt(ASTNode root) { 210 MethodReferenceASTVisitor visitor = new MethodReferenceASTVisitor(); 211 root.accept(visitor); 212 return visitor; 213 } 214 215 public Result getResult() { 216 return fResult; 217 } 218 219 public MethodReferenceASTVisitor() { 220 fResult = new Result(); 221 } 222 223 private ITypeBinding addDeclaredType(Name node) { 224 ITypeBinding type = resolveType(node); 225 if (type == null) { 226 Msg.err(NAME + ".addDeclaredType(): type==null: node=" + node); 227 } else { 228 fResult.fDeclares.add(new TypeWrapper(type)); 229 } 230 return type; 231 } 232 233 private IMethodBinding addCall(Name node) { 234 IMethodBinding binding = resolveMethod(node); 235 if (binding != null) 236 fResult.addCall(binding); 237 else 238 Msg.err(NAME + ".addCall(): binding==null: node=" + node); 239 return binding; 240 } 241 242 private ITypeBinding resolveType(Name node) { 243 while (node != null) { 244 IBinding binding = node.resolveBinding(); 245 if (DEBUG) 246 Msg.println(NAME + ".resolveType(): " + node); 247 if (binding != null && binding.getKind() == IBinding.TYPE) { 248 return (ITypeBinding) binding; 249 } 250 if (node.isQualifiedName()) 251 node = ((QualifiedName) node).getQualifier(); 252 } 253 return null; 254 } 255 256 /** 257 * If a nested type is referenced, only the nested type is counted. 258 * Its enclosing type is not counted. 259 */ 260 private ITypeBinding typeRefFound(Name node) { 261 if (DEBUG) 262 System.err.println(NAME + ".typeRefFound(): " + node); 263 while (node != null) { 264 IBinding binding = node.resolveBinding(); 265 if (binding != null && binding.getKind() == IBinding.TYPE) { 266 fResult.addReference((ITypeBinding) binding); 267 return (ITypeBinding) binding; 268 } 269 if (node.isQualifiedName()) 270 node = ((QualifiedName) node).getQualifier(); 271 else 272 break; 273 } 274 return null; 275 } 276 277 private void possibleTypeRefFound(Name node) { 278 if (DEBUG) 279 System.err.println(NAME + ".possibleTypeRefFound(): " + node); 280 while (node != null) { 281 IBinding binding = node.resolveBinding(); 282 if (binding != null && binding.getKind() == IBinding.TYPE) { 283 fResult.addReference((ITypeBinding) binding); 284 break; 285 } 286 if (node.isQualifiedName()) 287 node = ((QualifiedName) node).getQualifier(); 288 else 289 break; 290 } 291 } 292 293 //////////////////////////////////////////////////////////////////////// 294 295 private IMethodBinding resolveMethod(Name node) { 296 if (node == null) 297 return null; 298 // A more general call to resolve binding. 299 // getAST().getBindingResolver().resolveConstructor(node); 300 IBinding binding = node.resolveBinding(); 301 if (DEBUG) { 302 System.err.println(NAME + ".resolveMethod(): node=" + node); 303 } 304 if (binding != null && binding.getKind() == IBinding.METHOD) { 305 return (IMethodBinding) binding; 306 } else { 307 Msg.err(NAME + ".resolveMethod(): can't resolve method: node=" + node + ", binding=" + binding); 308 } 309 return null; 310 } 311 312 //////////////////////////////////////////////////////////////////////// 313 314 private void visitChildren(List elements) { 315 int nElements = elements.size(); 316 for (int i = 0; i < nElements; i++) { 317 ((ASTNode) elements.get(i)).accept(this); 318 } 319 } 320 321 //////////////////////////////////////////////////////////////////////// 322 323 /* 324 * @see ASTVisitor#visit(ArrayType) 325 */ 326 public boolean visit(ArrayType node) { 327 node.getElementType().accept(this); 328 return false; 329 } 330 331 /* 332 * @see ASTVisitor#visit(SimpleType) 333 */ 334 public boolean visit(SimpleType node) { 335 typeRefFound(node.getName()); 336 return false; 337 } 338 339 /* 340 * @see ASTVisitor#visit(QualifiedName) 341 */ 342 public boolean visit(QualifiedName node) { 343 possibleTypeRefFound(node); // possible ref 344 return false; 345 } 346 347 /* 348 * @see ASTVisitor#visit(PackageDeclaration) 349 */ 350 public boolean visit(PackageDeclaration node) { 351 return false; 352 } 353 354 /* 355 * @see ASTVisitor#visit(ThisExpression) 356 */ 357 public boolean visit(ThisExpression node) { 358 typeRefFound(node.getQualifier()); 359 return false; 360 } 361 362 private void evalQualifyingExpression(Expression expr) { 363 if (expr != null) { 364 if (expr instanceof Name) { 365 possibleTypeRefFound((Name) expr); 366 } else { 367 expr.accept(this); 368 } 369 } 370 } 371 372 /* 373 * @see ASTVisitor#visit(ClassInstanceCreation) 374 */ 375 public boolean visit(ClassInstanceCreation node) { 376 typeRefFound(node.getName()); 377 evalQualifyingExpression(node.getExpression()); 378 // 379 IMethodBinding method = node.resolveConstructorBinding(); 380 if (method == null) { 381 Msg.err(NAME + ".visit(ClassInstanceCreation): method==null: node=" + node); 382 } else { 383 fResult.addCall(method); 384 } 385 // 386 if (node.getAnonymousClassDeclaration() != null) { 387 node.getAnonymousClassDeclaration().accept(this); 388 } 389 visitChildren(node.arguments()); 390 return false; 391 } 392 393 /* 394 * @see ASTVisitor#endVisit(MethodInvocation) 395 */ 396 public boolean visit(MethodInvocation node) { 397 if (DEBUG) 398 System.err.println(NAME + ".visit(MethodInvocation): node=" + node); 399 evalQualifyingExpression(node.getExpression()); 400 addCall(node.getName()); 401 visitChildren(node.arguments()); 402 return false; 403 } 404 405 /* 406 * @see ASTVisitor#visit(SuperConstructorInvocation) 407 */ 408 public boolean visit(SuperConstructorInvocation node) { 409 evalQualifyingExpression(node.getExpression()); 410 // 411 IMethodBinding method = node.resolveConstructorBinding(); 412 if (method == null) { 413 Msg.err(NAME + ".visit(SuperConstructorInvocation): method==null: node=" + node); 414 } else { 415 fResult.addCall(method); 416 } 417 // 418 visitChildren(node.arguments()); 419 return false; 420 } 421 422 public boolean visit(SuperMethodInvocation node) { 423 addCall(node.getName()); 424 // 425 visitChildren(node.arguments()); 426 return false; 427 } 428 /* 429 * @see ASTVisitor#visit(FieldAccess) 430 */ 431 public boolean visit(FieldAccess node) { 432 evalQualifyingExpression(node.getExpression()); 433 return false; 434 } 435 436 /* 437 * @see ASTVisitor#visit(SimpleName) 438 */ 439 public boolean visit(SimpleName node) { 440 // if the call gets here, it can only be a variable reference 441 return false; 442 } 443 444 /* 445 * @see ASTVisitor#visit(TypeDeclaration) 446 */ 447 public boolean visit(TypeDeclaration node) { 448 ITypeBinding type = addDeclaredType(node.getName()); 449 fResult.declareTypeStart(type); 450 visitChildren(node.bodyDeclarations()); 451 fResult.declareTypeEnd(type); 452 return false; 453 } 454 455 /* 456 * @see ASTVisitor#visit(MethodDeclaration) 457 */ 458 public boolean visit(MethodDeclaration node) { 459 if (!node.isConstructor()) { 460 node.getReturnType().accept(this); 461 } 462 visitChildren(node.parameters()); 463 Iterator iter = node.thrownExceptions().iterator(); 464 while (iter.hasNext()) { 465 typeRefFound((Name) iter.next()); 466 } 467 IMethodBinding method = resolveMethod(node.getName()); 468 if (method == null) { 469 Msg.err(NAME + ".visit(MethodDeclaration(): method==null: node=" + node); 470 } else { 471 fResult.declareMethodStart(method); 472 } 473 if (node.getBody() != null) { 474 node.getBody().accept(this); 475 } 476 fResult.declareMethodEnd(method); 477 return false; 478 } 479 480 /* 481 * @see ASTVisitor#preVisit(ASTNode) 482 */ 483 public void preVisit(ASTNode node) { 484 if ((node.getFlags() & ASTNode.MALFORMED) != 0) { 485 throw new ASTError(node); 486 } 487 } 488 489 //////////////////////////////////////////////////////////////////////// 490 491 }