1 19 package org.netbeans.modules.ruby; 20 21 import java.io.IOException ; 22 import java.net.MalformedURLException ; 23 import java.net.URL ; 24 import java.util.Collections ; 25 import java.util.HashSet ; 26 import java.util.List ; 27 import java.util.Set ; 28 29 import javax.swing.text.BadLocationException ; 30 import javax.swing.text.Document ; 31 32 import org.jruby.ast.AliasNode; 33 import org.jruby.ast.ArgsNode; 34 import org.jruby.ast.ArgumentNode; 35 import org.jruby.ast.CallNode; 36 import org.jruby.ast.ClassNode; 37 import org.jruby.ast.ClassVarDeclNode; 38 import org.jruby.ast.ClassVarNode; 39 import org.jruby.ast.Colon2Node; 40 import org.jruby.ast.ConstDeclNode; 41 import org.jruby.ast.ConstNode; 42 import org.jruby.ast.DAsgnNode; 43 import org.jruby.ast.DVarNode; 44 import org.jruby.ast.DefnNode; 45 import org.jruby.ast.DefsNode; 46 import org.jruby.ast.FCallNode; 47 import org.jruby.ast.GlobalAsgnNode; 48 import org.jruby.ast.GlobalVarNode; 49 import org.jruby.ast.InstAsgnNode; 50 import org.jruby.ast.InstVarNode; 51 import org.jruby.ast.ListNode; 52 import org.jruby.ast.LocalAsgnNode; 53 import org.jruby.ast.LocalVarNode; 54 import org.jruby.ast.Node; 55 import org.jruby.ast.SymbolNode; 56 import org.jruby.ast.VCallNode; 57 import org.jruby.ast.types.INameNode; 58 import org.netbeans.api.gsf.CompilationInfo; 59 import org.netbeans.api.gsf.DeclarationFinder.DeclarationLocation; 60 import org.netbeans.api.gsf.GsfTokenId; 61 import org.netbeans.api.gsf.Index.NameKind; 62 import org.netbeans.api.gsf.OffsetRange; 63 import org.netbeans.api.lexer.Token; 64 import org.netbeans.api.lexer.TokenHierarchy; 65 import org.netbeans.api.lexer.TokenId; 66 import org.netbeans.api.lexer.TokenSequence; 67 import org.netbeans.editor.BaseDocument; 68 import org.netbeans.modules.ruby.elements.IndexedClass; 69 import org.netbeans.modules.ruby.elements.IndexedElement; 70 import org.netbeans.modules.ruby.elements.IndexedMethod; 71 import org.netbeans.modules.ruby.lexer.LexUtilities; 72 import org.netbeans.modules.ruby.lexer.LexUtilities.Call; 73 import org.netbeans.modules.ruby.lexer.RubyCommentTokenId; 74 import org.netbeans.modules.ruby.lexer.RubyTokenId; 75 import org.openide.filesystems.FileObject; 76 import org.openide.util.Exceptions; 77 78 79 89 public class DeclarationFinder implements org.netbeans.api.gsf.DeclarationFinder { 90 91 private boolean ignoreAlias; 92 93 94 public DeclarationFinder() { 95 } 96 97 public OffsetRange getReferenceSpan(Document doc, int offset) { 98 TokenHierarchy<Document > th = TokenHierarchy.get(doc); 99 TokenSequence<?extends TokenId> ts = th.tokenSequence(RubyTokenId.language()); 100 101 if (ts == null) { 102 return OffsetRange.NONE; 103 } 104 105 ts.move(offset); 106 107 if (!ts.moveNext() && !ts.movePrevious()) { 108 return OffsetRange.NONE; 109 } 110 111 boolean isBetween = (offset == ts.offset()); 113 114 OffsetRange range = getReferenceSpan(ts, th, offset); 115 116 if ((range == OffsetRange.NONE) && isBetween) { 117 if (ts.movePrevious()) { 120 range = getReferenceSpan(ts, th, offset); 121 } 122 } 123 124 return range; 125 } 126 127 private OffsetRange getReferenceSpan(TokenSequence<?extends TokenId> ts, 128 TokenHierarchy<Document > th, int offset) { 129 Token<?extends TokenId> token = ts.token(); 130 TokenId id = token.id(); 131 132 if ((id == GsfTokenId.IDENTIFIER) || (id == GsfTokenId.CLASS_VAR) || 134 (id == GsfTokenId.GLOBAL_VAR) || (id == GsfTokenId.CONSTANT) || 135 (id == GsfTokenId.TYPE_SYMBOL) || (id == GsfTokenId.INSTANCE_VAR)) { 136 return new OffsetRange(ts.offset(), ts.offset() + token.length()); 137 } 138 139 TokenSequence<?extends TokenId> embedded = ts.embedded(); 141 142 if (embedded != null) { 143 ts = embedded; 144 embedded.move(offset); 145 146 if (embedded.moveNext()) { 147 Token<?extends TokenId> embeddedToken = embedded.token(); 148 149 if (embeddedToken.id() == RubyCommentTokenId.COMMENT_LINK) { 150 return new OffsetRange(embedded.offset(), 151 embedded.offset() + embeddedToken.length()); 152 } 153 } 154 } 155 156 if ((id == RubyTokenId.QUOTED_STRING_LITERAL) || (id == RubyTokenId.STRING_LITERAL)) { 158 int requireStart = LexUtilities.getRequireStringOffset(offset, th); 159 160 if (requireStart != -1) { 161 String require = LexUtilities.getStringAt(offset, th); 162 163 if (require != null) { 164 return new OffsetRange(requireStart, requireStart + require.length()); 165 } 166 } 167 } 168 169 return OffsetRange.NONE; 170 } 171 172 public DeclarationLocation findDeclaration(CompilationInfo info, int caretOffset) { 173 try { 175 Document doc = info.getDocument(); 176 177 OffsetRange range = getReferenceSpan(doc, caretOffset); 180 181 if (range == OffsetRange.NONE) { 182 return DeclarationLocation.NONE; 183 } 184 185 boolean leftSide = range.getEnd() <= caretOffset; 186 187 Node root = AstUtilities.getRoot(info); 188 189 if (root == null) { 190 String text = doc.getText(range.getStart(), range.getLength()); 193 RubyIndex index = RubyIndex.get(info.getIndex()); 194 195 if ((index == null) || (text.length() == 0)) { 196 return DeclarationLocation.NONE; 197 } 198 199 if (Character.isUpperCase(text.charAt(0))) { 200 Set <IndexedClass> classes = 202 index.getClasses(text, NameKind.EXACT_NAME, true, false, false); 203 204 if (classes.size() == 0) { 205 return DeclarationLocation.NONE; 206 } 207 208 try { 209 IndexedClass candidate = 210 findBestClassMatch(classes, (BaseDocument)info.getDocument(), null, 211 null, index); 212 213 if (candidate != null) { 214 IndexedElement com = candidate; 215 Node node = AstUtilities.getForeignNode(com); 216 217 return new DeclarationLocation(com.getFile().getFileObject(), 218 node.getPosition().getStartOffset()); 219 } 220 } catch (IOException ioe) { 221 Exceptions.printStackTrace(ioe); 222 } 223 } else { 224 Set <IndexedMethod> methods = index.getMethods(text, null, NameKind.EXACT_NAME); 226 227 try { 228 IndexedMethod candidate = 229 findBestMethodMatch(text, methods, (BaseDocument)info.getDocument(), 230 caretOffset, null, null, index); 231 232 if (candidate != null) { 233 IndexedElement com = candidate; 234 Node node = AstUtilities.getForeignNode(com); 235 236 return new DeclarationLocation(com.getFile().getFileObject(), 237 node.getPosition().getStartOffset()); 238 } 239 } catch (IOException ioe) { 240 Exceptions.printStackTrace(ioe); 241 } 242 } 244 return DeclarationLocation.NONE; 245 } 246 247 RubyIndex index = RubyIndex.get(info.getIndex()); 248 249 TokenHierarchy<Document > th = TokenHierarchy.get(doc); 250 251 int tokenOffset = caretOffset; 252 253 if (leftSide && (tokenOffset > 0)) { 254 tokenOffset--; 255 } 256 257 int requireStart = LexUtilities.getRequireStringOffset(tokenOffset, th); 259 260 if (requireStart != -1) { 261 String require = LexUtilities.getStringAt(tokenOffset, th); 262 263 if (require != null) { 264 String file = index.getRequiredFileUrl(require); 265 266 if (file != null) { 267 FileObject fo = RubyIndex.getFileObject(file); 268 269 return new DeclarationLocation(fo, 0); 270 } 271 } 272 273 return DeclarationLocation.NONE; 275 } 276 277 AstPath path = new AstPath(root, caretOffset); 278 Node closest = path.leaf(); 279 280 DeclarationLocation rdoc = findRDocMethod(doc, tokenOffset, root, path); 282 283 if (rdoc != DeclarationLocation.NONE) { 284 return fix(rdoc, info); 285 } 286 287 if (closest instanceof LocalVarNode || closest instanceof LocalAsgnNode) { 289 String name = ((INameNode)closest).getName(); 291 Node method = AstUtilities.findLocalScope(closest, path); 292 293 return fix(findLocal(method, name), info); 294 } else if (closest instanceof DVarNode) { 295 String name = ((DVarNode)closest).getName(); Node block = AstUtilities.findDynamicScope(closest, path); 298 299 return fix(findDynamic(block, name), info); 300 } else if (closest instanceof DAsgnNode) { 301 String name = ((INameNode)closest).getName(); 303 Node block = AstUtilities.findDynamicScope(closest, path); 304 305 return fix(findDynamic(block, name), info); 306 } else if (closest instanceof InstVarNode) { 307 return fix(findInstance(root, ((INameNode)closest).getName()), info); 309 } else if (closest instanceof ClassVarNode) { 310 return fix(findClassVar(root, ((ClassVarNode)closest).getName()), info); 312 } else if (closest instanceof GlobalVarNode) { 313 String name = ((GlobalVarNode)closest).getName(); 316 return fix(findGlobal(root, name), info); 317 } else if (closest instanceof FCallNode || closest instanceof VCallNode || 318 closest instanceof CallNode) { 319 String name = ((INameNode)closest).getName(); 321 322 Call call = LexUtilities.getCallType(doc, th, caretOffset); 323 324 if (call.getType() == null) { 331 if (name.equals("new")) { name = "initialize"; } 334 335 Arity arity = Arity.getCallArity(closest); 336 337 DeclarationLocation loc = fix(findMethod(root, name, arity), info); 338 339 if (loc != DeclarationLocation.NONE) { 340 return loc; 341 } 342 } 343 344 Set <IndexedMethod> methods = index.getMethods(name, null, NameKind.EXACT_NAME); 345 346 try { 347 IndexedMethod candidate = 348 findBestMethodMatch(name, methods, (BaseDocument)info.getDocument(), 349 caretOffset, path, closest, index); 350 351 if (candidate != null) { 352 IndexedElement com = candidate; 353 Node node = AstUtilities.getForeignNode(com); 354 355 return new DeclarationLocation(com.getFile().getFileObject(), 356 node.getPosition().getStartOffset()); 357 } 358 } catch (IOException ioe) { 359 Exceptions.printStackTrace(ioe); 360 } 361 } else if (closest instanceof ConstNode || closest instanceof Colon2Node) { 362 String name = ((INameNode)closest).getName(); 364 Node localClass = findClass(root, name); 365 366 if (localClass != null) { 367 if (closest instanceof Colon2Node) { 369 AstPath classPath = new AstPath(root, localClass); 370 371 if (classPath.leaf() != null) { 372 String fqn1 = AstUtilities.getFqn((Colon2Node)closest); 373 String fqn2 = AstUtilities.getFqnName(classPath); 374 375 if (fqn1.equals(fqn2)) { 376 return fix(getLocation(localClass), info); 377 } 378 } else { 379 assert false : localClass.toString(); 380 } 381 } else { 382 return fix(getLocation(localClass), info); 383 } 384 } 385 386 Set <IndexedClass> classes = Collections.emptySet(); 388 389 if (closest instanceof Colon2Node) { 390 name = AstUtilities.getFqn((Colon2Node)closest); 391 } 392 393 String fqn = AstUtilities.getFqnName(path); 397 398 while ((classes.size() == 0) && (fqn.length() > 0)) { 399 classes = index.getClasses(fqn + "::" + name, NameKind.EXACT_NAME, true, false, 400 false); 401 402 int f = fqn.lastIndexOf("::"); 403 404 if (f == -1) { 405 break; 406 } else { 407 fqn = fqn.substring(0, f); 408 } 409 } 410 411 if (classes.size() == 0) { 412 classes = index.getClasses(name, NameKind.EXACT_NAME, true, false, false); 413 } 414 415 try { 416 IndexedClass candidate = 417 findBestClassMatch(classes, (BaseDocument)info.getDocument(), path, 418 closest, index); 419 420 if (candidate != null) { 421 IndexedElement com = candidate; 422 Node node = AstUtilities.getForeignNode(com); 423 424 return new DeclarationLocation(com.getFile().getFileObject(), 425 node.getPosition().getStartOffset()); 426 } 427 } catch (IOException ioe) { 428 Exceptions.printStackTrace(ioe); 429 } 430 } else if (closest instanceof SymbolNode) { 431 String name = ((SymbolNode)closest).getName(); 432 433 Arity arity = Arity.UNKNOWN; 435 DeclarationLocation location = findMethod(root, name, arity); 436 437 if (location == DeclarationLocation.NONE) { 438 location = findInstance(root, name); 439 } 440 441 if (location == DeclarationLocation.NONE) { 442 location = findClassVar(root, name); 443 } 444 445 if (location == DeclarationLocation.NONE) { 446 location = findGlobal(root, name); 447 } 448 449 if (location == DeclarationLocation.NONE) { 450 Node clz = findClass(root, ((INameNode)closest).getName()); 451 452 if (clz != null) { 453 location = getLocation(clz); 454 } 455 } 456 457 return fix(location, info); 458 } else if (closest instanceof AliasNode) { 459 AliasNode an = (AliasNode)closest; 460 461 String newName = an.getNewName(); 463 464 int newLength = newName.length(); 472 int aliasPos = an.getPosition().getStartOffset(); 473 474 if (caretOffset > (aliasPos + 1)) { 478 if (caretOffset > (aliasPos + 6 + newLength)) { 479 String name = an.getOldName(); 484 ignoreAlias = true; 485 486 try { 487 DeclarationLocation location = 488 findLocal(AstUtilities.findLocalScope(closest, path), name); 489 490 if (location == DeclarationLocation.NONE) { 491 location = findDynamic(AstUtilities.findDynamicScope(closest, path), 492 name); 493 } 494 495 if (location == DeclarationLocation.NONE) { 496 location = findMethod(root, name, Arity.UNKNOWN); 497 } 498 499 if (location == DeclarationLocation.NONE) { 500 location = findInstance(root, name); 501 } 502 503 if (location == DeclarationLocation.NONE) { 504 location = findClassVar(root, name); 505 } 506 507 if (location == DeclarationLocation.NONE) { 508 location = findGlobal(root, name); 509 } 510 511 if (location == DeclarationLocation.NONE) { 512 Node clz = findClass(root, name); 513 514 if (clz != null) { 515 location = getLocation(clz); 516 } 517 } 518 519 if (location == DeclarationLocation.NONE) { 522 return location; 523 } else { 524 return fix(location, info); 525 } 526 } finally { 527 ignoreAlias = false; 528 } 529 } else { 530 return new DeclarationLocation(info.getFileObject(), aliasPos + 4); 533 } 534 } 535 } else if (closest instanceof ArgumentNode) { 536 String name = ((ArgumentNode)closest).getName(); 539 Node parent = path.leafParent(); 540 541 if (parent != null) { 542 if (parent instanceof DefnNode || parent instanceof DefsNode) { 543 return DeclarationLocation.NONE; 545 } else { 546 Node method = AstUtilities.findLocalScope(closest, path); 548 549 return fix(findLocal(method, name), info); 550 } 551 } 552 } 553 } catch (IOException ioe) { 554 Exceptions.printStackTrace(ioe); 555 } catch (BadLocationException ble) { 556 Exceptions.printStackTrace(ble); 557 } 558 559 return DeclarationLocation.NONE; 560 } 561 562 private DeclarationLocation fix(DeclarationLocation location, CompilationInfo info) { 563 if ((location != DeclarationLocation.NONE) && (location.getFileObject() == null) && 564 (location.getUrl() == null)) { 565 return new DeclarationLocation(info.getFileObject(), location.getOffset()); 566 } 567 568 return location; 569 } 570 571 private DeclarationLocation getLocation(Node node) { 572 return new DeclarationLocation(null, node.getPosition().getStartOffset()); 573 } 574 575 private DeclarationLocation findRDocMethod(Document doc, int offset, Node root, AstPath path) { 576 TokenHierarchy<Document > th = TokenHierarchy.get(doc); 577 TokenSequence<?extends TokenId> ts = th.tokenSequence(RubyTokenId.language()); 578 579 if (ts == null) { 580 return DeclarationLocation.NONE; 581 } 582 583 ts.move(offset); 584 585 if (!ts.moveNext() && !ts.movePrevious()) { 586 return DeclarationLocation.NONE; 587 } 588 589 Token<?extends TokenId> token = ts.token(); 590 591 TokenSequence<?extends TokenId> embedded = ts.embedded(); 592 593 if (embedded != null) { 594 ts = embedded; 595 596 embedded.move(offset); 597 598 if (!embedded.moveNext() && !embedded.movePrevious()) { 599 return DeclarationLocation.NONE; 600 } 601 602 token = embedded.token(); 603 } 604 605 if ((token != null) && (token.id() == RubyCommentTokenId.COMMENT_LINK)) { 607 String method = token.text().toString(); 608 609 if (method.startsWith("#")) { 610 method = method.substring(1); 611 612 DeclarationLocation loc = findMethod(root, method, Arity.UNKNOWN); 613 614 if (loc == DeclarationLocation.NONE) { 618 loc = findInstance(root, "@" + method); 619 } 620 621 return loc; 622 } else { 623 try { 625 URL url = new URL (method); 626 627 return new DeclarationLocation(url); 628 } catch (MalformedURLException mue) { 629 ; 631 } 632 } 633 } 634 635 return DeclarationLocation.NONE; 636 } 637 638 private IndexedClass findBestClassMatch(Set <IndexedClass> classes, BaseDocument doc, 639 AstPath path, Node reference, RubyIndex index) { 640 while (!classes.isEmpty()) { 643 IndexedClass clz = findBestClassMatchHelper(classes, doc, path, reference, index); 644 Node node = AstUtilities.getForeignNode(clz); 645 646 if (node != null) { 647 return clz; 648 } 649 650 classes.remove(clz); 651 } 652 653 return null; 654 } 655 656 private IndexedClass findBestClassMatchHelper(Set <IndexedClass> classes, BaseDocument doc, 659 AstPath path, Node reference, RubyIndex index) { 660 Set <IndexedClass> candidates = new HashSet <IndexedClass>(); 665 666 if (reference instanceof Colon2Node) { 667 String fqn = AstUtilities.getFqn((Colon2Node)reference); 668 669 while ((fqn != null) && (fqn.length() > 0)) { 670 for (IndexedClass clz : classes) { 671 if (fqn.equals(clz.getSignature())) { 672 candidates.add(clz); 673 } 674 } 675 676 678 IndexedClass superClass = index.getSuperclass(fqn); 682 683 if (superClass != null) { 684 fqn = superClass.getSignature(); 685 } else { 686 break; 687 } 688 } 689 } 690 691 if (candidates.size() == 1) { 692 return candidates.iterator().next(); 693 } else if (!candidates.isEmpty()) { 694 classes = candidates; 695 } 696 697 701 Set <String > requires = null; 704 705 if (path != null) { 706 candidates = new HashSet <IndexedClass>(); 707 708 requires = AstUtilities.getRequires(path.root()); 709 710 for (IndexedClass clz : classes) { 711 String require = clz.getRequire(); 712 713 if (requires.contains(require)) { 714 candidates.add(clz); 715 } 716 } 717 718 if (candidates.size() == 1) { 719 return candidates.iterator().next(); 720 } else if (!candidates.isEmpty()) { 721 classes = candidates; 722 } 723 } 724 725 candidates = new HashSet <IndexedClass>(); 728 729 for (IndexedClass clz : classes) { 730 String url = clz.getFileUrl(); 731 732 if (url.indexOf("rubystubs") != -1) { 733 candidates.add(clz); 734 } 735 } 736 737 if (candidates.size() == 1) { 738 return candidates.iterator().next(); 739 } else if (!candidates.isEmpty()) { 740 classes = candidates; 741 } 742 743 candidates = new HashSet <IndexedClass>(); 745 746 for (IndexedClass clz : classes) { 747 } 749 750 if ((index != null) && (requires != null)) { 753 candidates = new HashSet <IndexedClass>(); 754 755 Set <String > allRequires = index.getRequiresTransitively(requires); 756 757 for (IndexedClass clz : classes) { 758 String require = clz.getRequire(); 759 760 if (allRequires.contains(require)) { 761 candidates.add(clz); 762 } 763 } 764 765 if (candidates.size() == 1) { 766 return candidates.iterator().next(); 767 } else if (!candidates.isEmpty()) { 768 classes = candidates; 769 } 770 } 771 772 777 candidates = new HashSet <IndexedClass>(); 781 782 if (classes.size() > 0) { 784 return classes.iterator().next(); 785 } else { 786 return null; 787 } 788 } 789 790 private IndexedMethod findBestMethodMatch(String name, Set <IndexedMethod> methods, 791 BaseDocument doc, int offset, AstPath path, Node call, RubyIndex index) { 792 while (!methods.isEmpty()) { 795 IndexedMethod method = 796 findBestMethodMatchHelper(name, methods, doc, offset, path, call, index); 797 Node node = AstUtilities.getForeignNode(method); 798 799 if (node != null) { 800 return method; 801 } 802 803 methods.remove(method); 804 } 805 806 return null; 807 } 808 809 private IndexedMethod findBestMethodMatchHelper(String name, Set <IndexedMethod> methods, 810 BaseDocument doc, int offset, AstPath path, Node callNode, RubyIndex index) { 811 Set <IndexedMethod> candidates = new HashSet <IndexedMethod>(); 812 813 if (callNode instanceof CallNode) { 818 Node node = ((CallNode)callNode).getReceiverNode(); 819 String fqn = null; 820 821 if (node instanceof Colon2Node) { 822 fqn = AstUtilities.getFqn((Colon2Node)node); 823 } else if (node instanceof ConstNode) { 824 fqn = ((ConstNode)node).getName(); 825 } 826 827 if (fqn != null) { 828 while ((fqn != null) && (fqn.length() > 0)) { 829 for (IndexedMethod method : methods) { 830 if (fqn.equals(method.getClz())) { 831 candidates.add(method); 832 } 833 } 834 835 IndexedClass superClass = index.getSuperclass(fqn); 839 840 if (superClass != null) { 841 fqn = superClass.getSignature(); 842 } else { 843 break; 844 } 845 } 846 } 847 } 848 849 if (candidates.size() == 1) { 850 return candidates.iterator().next(); 851 } else if (!candidates.isEmpty()) { 852 methods = candidates; 853 } 854 855 TokenHierarchy<Document > th = TokenHierarchy.get((Document )doc); 860 861 Call call = LexUtilities.getCallType(doc, th, offset); 862 boolean skipPrivate = true; 863 864 if ((path != null) && (callNode != null) && (call != Call.LOCAL) && (call != Call.NONE)) { 865 boolean skipInstanceMethods = call.isStatic(); 866 867 candidates = new HashSet <IndexedMethod>(); 868 869 String type = call.getType(); 870 871 if ((type != null) && (type.length() > 0)) { 874 String lhs = call.getLhs(); 875 876 String fqn = AstUtilities.getFqnName(path); 877 878 if ("self".equals(lhs)) { 882 type = fqn; 883 skipPrivate = false; 884 } else if ("super".equals(lhs)) { 885 skipPrivate = false; 886 887 IndexedClass sc = index.getSuperclass(fqn); 888 889 if (sc != null) { 890 type = sc.getFqn(); 891 } else { 892 ClassNode cls = AstUtilities.findClass(callNode, path); 893 894 if (cls != null) { 895 type = AstUtilities.getSuperclass(cls); 896 } 897 } 898 } 899 900 if ((type != null) && (type.length() > 0)) { 901 while (candidates.size() == 0) { 906 candidates = index.getInheritedMethods(fqn + "::" + type, name); 907 908 int f = fqn.lastIndexOf("::"); 909 910 if (f == -1) { 911 break; 912 } else { 913 fqn = fqn.substring(0, f); 914 } 915 } 916 917 candidates = index.getInheritedMethods(type, name); 920 } 921 } 922 923 if (skipPrivate || skipInstanceMethods) { 924 Set <IndexedMethod> m = new HashSet <IndexedMethod>(); 925 926 for (IndexedMethod method : candidates) { 927 if (skipPrivate && (method.isPrivate() && !"new".equals(method.getName()))) { 929 continue; 932 } 933 934 if (skipInstanceMethods && !method.isStatic()) { 936 continue; 937 } 938 939 m.add(method); 940 } 941 942 candidates = m; 943 } 944 } 945 946 if (candidates.size() == 1) { 947 return candidates.iterator().next(); 948 } else if (!candidates.isEmpty()) { 949 methods = candidates; 950 } 951 952 958 Set <String > requires = null; 961 962 if (path != null) { 963 candidates = new HashSet <IndexedMethod>(); 964 965 requires = AstUtilities.getRequires(path.root()); 966 967 for (IndexedMethod method : methods) { 968 String require = method.getRequire(); 969 970 if (requires.contains(require)) { 971 candidates.add(method); 972 } 973 } 974 975 if (candidates.size() == 1) { 976 return candidates.iterator().next(); 977 } else if (!candidates.isEmpty()) { 978 methods = candidates; 979 } 980 } 981 982 candidates = new HashSet <IndexedMethod>(); 985 986 for (IndexedMethod method : methods) { 987 String url = method.getFileUrl(); 988 989 if (url.indexOf("rubystubs") != -1) { 990 candidates.add(method); 991 } 992 } 993 994 if (candidates.size() == 1) { 995 return candidates.iterator().next(); 996 } else if (!candidates.isEmpty()) { 997 methods = candidates; 998 } 999 1000 candidates = new HashSet <IndexedMethod>(); 1002 1003 for (IndexedMethod method : methods) { 1004 } 1006 1007 if ((index != null) && (requires != null)) { 1010 candidates = new HashSet <IndexedMethod>(); 1011 1012 Set <String > allRequires = index.getRequiresTransitively(requires); 1013 1014 for (IndexedMethod method : methods) { 1015 String require = method.getRequire(); 1016 1017 if (allRequires.contains(require)) { 1018 candidates.add(method); 1019 } 1020 } 1021 1022 if (candidates.size() == 1) { 1023 return candidates.iterator().next(); 1024 } else if (!candidates.isEmpty()) { 1025 methods = candidates; 1026 } 1027 } 1028 1029 1034 1038 if (methods.size() > 0) { 1040 return methods.iterator().next(); 1041 } else { 1042 return null; 1043 } 1044 } 1045 1046 @SuppressWarnings ("unchecked") 1047 private DeclarationLocation findLocal(Node node, String name) { 1048 if (node instanceof LocalAsgnNode) { 1049 if (((INameNode)node).getName().equals(name)) { 1050 return getLocation(node); 1051 } 1052 } else if (!ignoreAlias && node instanceof AliasNode) { 1053 if (((AliasNode)node).getNewName().equals(name)) { 1054 return getLocation(node); 1055 } 1056 } else if (node instanceof ArgsNode) { 1057 ArgsNode an = (ArgsNode)node; 1058 1059 if (an.getArgsCount() > 0) { 1060 List <Node> args = (List <Node>)an.childNodes(); 1061 1062 for (Node arg : args) { 1063 if (arg instanceof ListNode) { 1064 List <Node> args2 = (List <Node>)arg.childNodes(); 1065 1066 for (Node arg2 : args2) { 1067 if (arg2 instanceof ArgumentNode) { 1068 if (((ArgumentNode)arg2).getName().equals(name)) { 1069 return getLocation(arg2); 1070 } 1071 } else if (arg2 instanceof LocalAsgnNode) { 1072 if (((LocalAsgnNode)arg2).getName().equals(name)) { 1073 return getLocation(arg2); 1074 } 1075 } 1076 } 1077 } 1078 } 1079 } 1080 } 1081 1082 List <Node> list = node.childNodes(); 1083 1084 for (Node child : list) { 1085 DeclarationLocation location = findLocal(child, name); 1086 1087 if (location != DeclarationLocation.NONE) { 1088 return location; 1089 } 1090 } 1091 1092 return DeclarationLocation.NONE; 1093 } 1094 1095 private DeclarationLocation findDynamic(Node node, String name) { 1096 if (node instanceof DAsgnNode) { 1097 if (((INameNode)node).getName().equals(name)) { 1098 return getLocation(node); 1099 } 1100 1101 } else if (!ignoreAlias && node instanceof AliasNode) { 1126 if (((AliasNode)node).getNewName().equals(name)) { 1127 return getLocation(node); 1128 } 1129 } 1130 1131 @SuppressWarnings ("unchecked") 1132 List <Node> list = node.childNodes(); 1133 1134 for (Node child : list) { 1135 DeclarationLocation location = findDynamic(child, name); 1136 1137 if (location != DeclarationLocation.NONE) { 1138 return location; 1139 } 1140 } 1141 1142 return DeclarationLocation.NONE; 1143 } 1144 1145 private DeclarationLocation findInstance(Node node, String name) { 1146 if (node instanceof InstAsgnNode) { 1147 if (((INameNode)node).getName().equals(name)) { 1148 return getLocation(node); 1149 } 1150 } else if (!ignoreAlias && node instanceof AliasNode) { 1151 if (((AliasNode)node).getNewName().equals(name)) { 1152 return getLocation(node); 1153 } 1154 } else if (AstUtilities.isAttr(node)) { 1155 SymbolNode[] symbols = AstUtilities.getAttrSymbols(node); 1158 1159 for (int i = 0; i < symbols.length; i++) { 1160 if (name.equals("@" + symbols[i].getName())) { 1161 return getLocation(symbols[i]); 1162 } 1163 } 1164 } 1165 1166 @SuppressWarnings ("unchecked") 1167 List <Node> list = node.childNodes(); 1168 1169 for (Node child : list) { 1170 DeclarationLocation location = findInstance(child, name); 1171 1172 if (location != DeclarationLocation.NONE) { 1173 return location; 1174 } 1175 } 1176 1177 return DeclarationLocation.NONE; 1178 } 1179 1180 private DeclarationLocation findClassVar(Node node, String name) { 1181 if (node instanceof ClassVarDeclNode) { 1182 if (((INameNode)node).getName().equals(name)) { 1183 return getLocation(node); 1184 } 1185 } else if (!ignoreAlias && node instanceof AliasNode) { 1186 if (((AliasNode)node).getNewName().equals(name)) { 1187 return getLocation(node); 1188 } 1189 1190 } 1202 1203 @SuppressWarnings ("unchecked") 1204 List <Node> list = node.childNodes(); 1205 1206 for (Node child : list) { 1207 DeclarationLocation location = findClassVar(child, name); 1208 1209 if (location != DeclarationLocation.NONE) { 1210 return location; 1211 } 1212 } 1213 1214 return DeclarationLocation.NONE; 1215 } 1216 1217 private DeclarationLocation findGlobal(Node node, String name) { 1218 if (node instanceof GlobalAsgnNode) { 1219 if (((INameNode)node).getName().equals(name)) { 1220 return getLocation(node); 1221 } 1222 } else if (!ignoreAlias && node instanceof AliasNode) { 1223 if (((AliasNode)node).getNewName().equals(name)) { 1224 return getLocation(node); 1225 } 1226 } 1227 1228 @SuppressWarnings ("unchecked") 1229 List <Node> list = node.childNodes(); 1230 1231 for (Node child : list) { 1232 DeclarationLocation location = findGlobal(child, name); 1233 1234 if (location != DeclarationLocation.NONE) { 1235 return location; 1236 } 1237 } 1238 1239 return DeclarationLocation.NONE; 1240 } 1241 1242 private DeclarationLocation findMethod(Node node, String name, Arity arity) { 1243 if (node instanceof DefnNode) { 1245 if (((DefnNode)node).getName().equals(name) && 1247 Arity.matches(arity, Arity.getDefArity(node))) { 1248 return getLocation(node); 1249 } 1250 } else if (node instanceof DefsNode) { 1251 if (((DefsNode)node).getName().equals(name) && 1253 Arity.matches(arity, Arity.getDefArity(node))) { 1254 return getLocation(node); 1255 } 1256 } else if (!ignoreAlias && node instanceof AliasNode) { 1257 if (((AliasNode)node).getNewName().equals(name)) { 1258 return getLocation(node); 1260 } 1261 } 1262 1263 @SuppressWarnings ("unchecked") 1264 List <Node> list = node.childNodes(); 1265 1266 for (Node child : list) { 1267 DeclarationLocation location = findMethod(child, name, arity); 1268 1269 if (location != DeclarationLocation.NONE) { 1270 return location; 1271 } 1272 } 1273 1274 return DeclarationLocation.NONE; 1275 } 1276 1277 private Node findClass(Node node, String name) { 1278 if (node instanceof ClassNode) { 1279 String n = AstUtilities.getClassOrModuleName((ClassNode)node); 1280 1281 if (n.equals(name)) { 1282 return node; 1283 } 1284 } else if (node instanceof ConstDeclNode) { 1285 if (((INameNode)node).getName().equals(name)) { 1286 return node; 1287 } 1288 } else if (!ignoreAlias && node instanceof AliasNode) { 1289 if (((AliasNode)node).getNewName().equals(name)) { 1290 return node; 1291 } 1292 } 1293 1294 @SuppressWarnings ("unchecked") 1295 List <Node> list = node.childNodes(); 1296 1297 for (Node child : list) { 1298 Node match = findClass(child, name); 1299 1300 if (match != null) { 1301 return match; 1302 } 1303 } 1304 1305 return null; 1306 } 1307} 1308 | Popular Tags |