1 19 package org.netbeans.modules.ruby; 20 21 import java.io.File ; 22 import java.io.IOException ; 23 import java.net.MalformedURLException ; 24 import java.net.MalformedURLException ; 25 import java.net.URL ; 26 import java.util.Collections ; 27 import java.util.EnumSet ; 28 import java.util.HashMap ; 29 import java.util.HashSet ; 30 import java.util.Map ; 31 import java.util.Set ; 32 33 import org.netbeans.api.gsf.Index; 34 import static org.netbeans.api.gsf.Index.*; 35 import org.netbeans.api.gsf.Modifier; 36 import org.netbeans.modules.ruby.elements.IndexedClass; 37 import org.netbeans.modules.ruby.elements.IndexedElement; 38 import org.netbeans.modules.ruby.elements.IndexedMethod; 39 import org.openide.filesystems.FileObject; 40 import org.openide.filesystems.URLMapper; 41 import org.openide.modules.InstalledFileLocator; 42 import org.openide.util.Exceptions; 43 44 45 53 public final class RubyIndex { 54 private static final String OBJECT = "Object"; private static final String CLASS = "Class"; private static final String MODULE = "Module"; private static final Set <SearchScope> ALL_SCOPE = EnumSet.allOf(SearchScope.class); 58 59 private static String clusterUrl = null; 60 private static final String CLUSTER_URL = "cluster:"; private Index index; 62 63 64 public RubyIndex(Index index) { 65 this.index = index; 66 } 67 68 public static RubyIndex get(Index index) { 69 return new RubyIndex(index); 70 } 71 72 private boolean search(String key, String name, NameKind kind, Set <SearchResult> result) { 73 try { 74 index.gsfSearch(key, name, kind, ALL_SCOPE, result); 75 76 return true; 77 } catch (IOException ioe) { 78 Exceptions.printStackTrace(ioe); 79 80 return false; 81 } 82 } 83 84 94 public Set <IndexedClass> getClasses(String name, final NameKind kind, boolean includeAll, 95 boolean skipClasses, boolean skipModules) { 96 String classFqn = null; 97 98 if (name != null) { 99 if (name.indexOf("::") != -1) { 101 int p = name.lastIndexOf("::"); classFqn = name.substring(0, p); 103 name = name.substring(p + 2); 104 } else if (name.endsWith(":")) { 105 classFqn = name.substring(0, name.length() - 1); 108 name = ""; 109 } 110 } 111 112 final Set <SearchResult> result = new HashSet <SearchResult>(); 113 114 String field = null; 119 120 switch (kind) { 121 case EXACT_NAME: 122 case PREFIX: 123 case CAMEL_CASE: 124 case REGEXP: 125 field = RubyIndexer.FIELD_CLASS_NAME; 126 127 break; 128 129 case CASE_INSENSITIVE_PREFIX: 130 case CASE_INSENSITIVE_REGEXP: 131 field = RubyIndexer.FIELD_CASE_INSENSITIVE_CLASS_NAME; 132 133 break; 134 135 default: 136 throw new UnsupportedOperationException (kind.toString()); 137 } 138 139 search(field, name, kind, result); 140 141 final Set <String > uniqueClasses; 143 144 if (includeAll) { 145 uniqueClasses = null; 146 } else { 147 uniqueClasses = new HashSet <String >(); } 149 150 final Set <IndexedClass> classes = new HashSet <IndexedClass>(); 151 152 for (SearchResult map : result) { 153 String clz = map.getValue(RubyIndexer.FIELD_CLASS_NAME); 154 155 if (clz == null) { 156 continue; 159 } 160 161 if ((kind == NameKind.PREFIX) && !clz.startsWith(name)) { 163 continue; 164 } 165 166 if ((classFqn != null) && !classFqn.equals(map.getValue(RubyIndexer.FIELD_IN))) { 167 continue; 168 } 169 170 String attrs = map.getValue(RubyIndexer.FIELD_CLASS_ATTRS); 171 boolean isClass = attrs.charAt(0) == 'C'; 172 173 if (skipClasses && isClass) { 174 continue; 175 } 176 177 if (skipModules && !isClass) { 178 continue; 179 } 180 181 String fqn = map.getValue(RubyIndexer.FIELD_FQN_NAME); 182 183 if (!includeAll) { 185 if (uniqueClasses.contains(fqn)) { 188 boolean replaced = false; 189 int documentedAt = attrs.indexOf('d'); 190 boolean isDocumented = documentedAt != -1; 191 192 if (isDocumented) { 193 int length = 0; 196 documentedAt = attrs.indexOf('(', documentedAt + 1); 197 198 if (documentedAt != -1) { 199 length = Integer.parseInt(attrs.substring(documentedAt + 1, 200 attrs.indexOf(')', documentedAt + 1))); 201 } 202 203 for (IndexedClass c : classes) { 205 if (c.getSignature().equals(fqn) && 206 (length > c.getDocumentationLength())) { 207 classes.remove(c); 208 replaced = true; 209 210 break; 211 } 212 } 213 } 214 215 if (!replaced) { 216 continue; 217 } 218 } else { 219 uniqueClasses.add(fqn); 220 } 221 } 222 223 classes.add(createClass(fqn, clz, map)); 224 } 225 226 return classes; 227 } 228 229 235 @SuppressWarnings ("unchecked") 237 public Set <IndexedMethod> getMethods(final String name, final String clz, NameKind kind) { 238 final Set <SearchResult> result = new HashSet <SearchResult>(); 240 241 String field = null; 246 NameKind originalKind = kind; 247 248 switch (kind) { 249 case EXACT_NAME: 256 kind = NameKind.PREFIX; 260 261 case PREFIX: 262 case CAMEL_CASE: 263 case REGEXP: 264 case CASE_INSENSITIVE_PREFIX: 265 case CASE_INSENSITIVE_REGEXP: 266 field = RubyIndexer.FIELD_METHOD_NAME; 267 268 break; 269 270 default: 271 throw new UnsupportedOperationException (kind.toString()); 272 } 273 274 search(field, name, kind, result); 275 276 278 final Set <IndexedMethod> methods = new HashSet <IndexedMethod>(); 280 281 for (SearchResult map : result) { 282 if (clz != null) { 283 String fqn = map.getValue(RubyIndexer.FIELD_FQN_NAME); 284 285 if (!(clz.equals(fqn))) { 286 continue; 287 } 288 } 289 290 String [] signatures = map.getValues(RubyIndexer.FIELD_METHOD_NAME); 291 292 if (signatures != null) { 293 for (String signature : signatures) { 294 if (((name == null) || (name.length() == 0)) && 296 !Character.isLowerCase(signature.charAt(0))) { 297 continue; 298 } 299 300 if ((kind == NameKind.PREFIX) && !signature.startsWith(name)) { 302 continue; 303 } else if (originalKind == NameKind.EXACT_NAME) { 304 if (((signature.length() > name.length()) && 308 (signature.charAt(name.length()) != '(')) && 309 (signature.charAt(name.length()) != ':')) { 310 continue; 311 } 312 } 313 314 assert map != null; 316 methods.add(createMethod(signature, map)); 317 } 318 } 319 } 320 321 return methods; 322 } 323 324 private IndexedMethod createMethod(String signature, SearchResult map) { 325 String clz = map.getValue(RubyIndexer.FIELD_CLASS_NAME); 326 String module = map.getValue(RubyIndexer.FIELD_IN); 327 328 if (clz == null) { 329 clz = module; 331 } else if ((module != null) && (module.length() > 0)) { 332 clz = module + "::" + clz; 333 } 334 335 String fileUrl = map.getValue(RubyIndexer.FIELD_FILENAME); 336 String fqn = map.getValue(RubyIndexer.FIELD_FQN_NAME); 337 String require = map.getValue(RubyIndexer.FIELD_REQUIRE); 338 339 int attributeIndex = signature.indexOf(':'); 341 Set <Modifier> modifiers; 342 343 if (attributeIndex != -1) { 344 modifiers = RubyIndexer.getModifiersFromString(signature, attributeIndex); 345 signature = signature.substring(0, attributeIndex); 346 } else { 347 modifiers = Collections.emptySet(); 348 } 349 350 IndexedMethod m = 351 IndexedMethod.create(this, signature, fqn, clz, fileUrl, require, modifiers); 352 353 return m; 354 } 355 356 private IndexedClass createClass(String fqn, String clz, SearchResult map) { 357 String require = map.getValue(RubyIndexer.FIELD_REQUIRE); 358 359 String fileUrl = map.getValue(RubyIndexer.FIELD_FILENAME); 362 363 if (clz == null) { 364 clz = map.getValue(RubyIndexer.FIELD_CLASS_NAME); 365 } 366 367 String attrs = map.getValue(RubyIndexer.FIELD_CLASS_ATTRS); 368 boolean isModule = attrs.charAt(0) == 'm'; 369 Set <Modifier> modifiers = Collections.emptySet(); 370 IndexedClass c = 371 IndexedClass.create(this, clz, fqn, fileUrl, require, isModule, modifiers, attrs); 372 373 return c; 374 } 375 376 public Set <String []> getRequires(final String name, final NameKind kind) { 378 final Set <SearchResult> result = new HashSet <SearchResult>(); 379 380 String field = RubyIndexer.FIELD_REQUIRE; 381 382 search(field, name, kind, result); 383 384 final Map <String , String > fqns = new HashMap <String , String >(); 386 387 for (SearchResult map : result) { 388 String [] r = map.getValues(field); 389 390 if (r != null) { 391 for (String require : r) { 392 if ((kind == NameKind.PREFIX) && !require.startsWith(name)) { 394 continue; 395 } 396 assert map != null; 397 398 String fqn = map.getValue(RubyIndexer.FIELD_FQN_NAME); 401 402 String there = fqns.get(require); 403 404 if ((there == null) || ((there != null) && (there.length() < fqn.length()))) { 405 fqns.put(require, fqn); 406 } 407 } 408 } 409 } 410 411 final Set <String []> requires = new HashSet <String []>(); 412 413 for (String require : fqns.keySet()) { 414 String fqn = fqns.get(require); 415 String [] item = new String [2]; 416 item[0] = require; 417 item[1] = fqn; 418 requires.add(item); 419 } 420 421 return requires; 422 } 423 424 public Set <String > getRequiresTransitively(Set <String > requires) { 425 return requires; 427 } 428 429 public IndexedClass getSuperclass(String fqn) { 430 final Set <SearchResult> result = new HashSet <SearchResult>(); 431 432 NameKind kind = NameKind.EXACT_NAME; 437 String field = RubyIndexer.FIELD_FQN_NAME; 438 439 search(field, fqn, kind, result); 440 441 for (SearchResult map : result) { 442 assert fqn.equals(map.getValue(RubyIndexer.FIELD_FQN_NAME)); 443 444 String extendsClass = map.getValue(RubyIndexer.FIELD_EXTENDS_NAME); 445 446 if (extendsClass != null) { 447 result.clear(); 449 450 if (!search(field, extendsClass, kind, result)) { 451 return null; 452 } 453 454 if (result.size() > 0) { 456 SearchResult superMap = result.iterator().next(); 457 String superFqn = superMap.getValue(RubyIndexer.FIELD_FQN_NAME); 458 459 return createClass(superFqn, extendsClass, superMap); 460 } else { 461 return null; 462 } 463 } 464 } 465 466 return null; 467 } 468 469 public Set <IndexedMethod> getInheritedMethods(String classFqn, String prefix) { 470 boolean haveRedirected = false; 471 472 if ((classFqn == null) || classFqn.equals(OBJECT)) { 473 classFqn = CLASS; 475 haveRedirected = true; 476 } else if (MODULE.equals(classFqn) || CLASS.equals(classFqn)) { 477 haveRedirected = true; 478 } 479 480 Set <IndexedMethod> methods = new HashSet <IndexedMethod>(); 482 Set <String > scannedClasses = new HashSet <String >(); 483 Set <String > seenSignatures = new HashSet <String >(); 484 485 addMethodsFromClass(prefix, classFqn, methods, seenSignatures, scannedClasses, 486 haveRedirected); 487 488 return methods; 489 } 490 491 private void addMethodsFromClass(String prefix, String classFqn, Set <IndexedMethod> methods, 492 Set <String > seenSignatures, Set <String > scannedClasses, boolean haveRedirected) { 493 if (scannedClasses.contains(classFqn)) { 495 return; 496 } 497 498 scannedClasses.add(classFqn); 499 500 String field = RubyIndexer.FIELD_FQN_NAME; 501 502 Set <SearchResult> result = new HashSet <SearchResult>(); 503 504 search(field, classFqn, NameKind.EXACT_NAME, result); 505 506 String extendsClass = null; 507 508 for (SearchResult map : result) { 509 assert map != null; 510 511 if (extendsClass == null) { 512 extendsClass = map.getValue(RubyIndexer.FIELD_EXTENDS_NAME); 513 } 514 515 String includes = map.getValue(RubyIndexer.FIELD_INCLUDES); 516 517 if (includes != null) { 518 String [] in = includes.split(","); 519 520 for (String include : in) { 521 addMethodsFromClass(prefix, include, methods, seenSignatures, scannedClasses, 522 haveRedirected); 523 } 524 } 525 526 String [] signatures = map.getValues(RubyIndexer.FIELD_METHOD_NAME); 527 528 if (signatures != null) { 529 for (String signature : signatures) { 530 if (((prefix == null) || (prefix.length() == 0)) && 532 !Character.isLowerCase(signature.charAt(0))) { 533 continue; 534 } 535 536 if (!seenSignatures.contains(signature)) { 538 if ((prefix == null) || signature.startsWith(prefix)) { 539 seenSignatures.add(signature); 540 methods.add(createMethod(signature, map)); 541 } 542 } 543 } 544 } 545 } 546 547 if (classFqn.equals(OBJECT)) { 548 return; 549 } 550 551 if (extendsClass == null) { 552 if (haveRedirected) { 553 addMethodsFromClass(prefix, OBJECT, methods, seenSignatures, scannedClasses, true); 554 } else { 555 addMethodsFromClass(prefix, CLASS, methods, seenSignatures, scannedClasses, true); 558 } 559 } else { 560 addMethodsFromClass(prefix, extendsClass, methods, seenSignatures, scannedClasses, 561 haveRedirected); 562 } 563 } 564 565 566 public Set <?extends IndexedElement> getDocumented(final String fqn) { 567 assert (fqn != null) && (fqn.length() > 0); 568 569 int index = fqn.indexOf('#'); 570 571 if (index == -1) { 572 return getDocumentedClasses(fqn); 574 } else { 575 String clz = fqn.substring(0, index); 577 String method = fqn.substring(index + 1); 578 579 return getDocumentedMethods(clz, method); 580 } 581 } 582 583 private Set <IndexedClass> getDocumentedClasses(final String fqn) { 584 final Set <SearchResult> result = new HashSet <SearchResult>(); 585 String field = RubyIndexer.FIELD_FQN_NAME; 586 587 search(field, fqn, NameKind.EXACT_NAME, result); 588 589 Set <IndexedClass> matches = new HashSet <IndexedClass>(); 590 591 for (SearchResult map : result) { 592 assert map != null; 593 594 String attributes = map.getValue(RubyIndexer.FIELD_CLASS_ATTRS); 595 596 if (attributes.indexOf('d') != -1) { 597 matches.add(createClass(fqn, null, map)); 598 } 599 } 600 601 return matches; 602 } 603 604 private Set <IndexedMethod> getDocumentedMethods(final String fqn, String method) { 605 final Set <SearchResult> result = new HashSet <SearchResult>(); 606 String field = RubyIndexer.FIELD_FQN_NAME; 607 608 search(field, fqn, NameKind.EXACT_NAME, result); 609 610 Set <IndexedMethod> matches = new HashSet <IndexedMethod>(); 611 612 for (SearchResult map : result) { 613 String [] signatures = map.getValues(RubyIndexer.FIELD_METHOD_NAME); 614 615 if (signatures != null) { 616 for (String signature : signatures) { 617 if (((method == null) || (method.length() == 0)) && 619 !Character.isLowerCase(signature.charAt(0))) { 620 continue; 621 } 622 623 if (!signature.startsWith(method)) { 624 continue; 625 } 626 627 if (((signature.length() > method.length()) && 631 (signature.charAt(method.length()) != '(')) && 632 (signature.charAt(method.length()) != ':')) { 633 continue; 634 } 635 636 int attributes = signature.indexOf(':', method.length()); 637 638 if (attributes == -1) { 639 continue; 640 } 641 642 if (signature.indexOf('d', attributes + 1) != -1) { 643 assert map != null; 645 matches.add(createMethod(signature, map)); 646 } 647 } 648 } 649 } 650 651 return matches; 652 } 653 654 655 public String getRequiredFileUrl(final String require) { 656 final Set <SearchResult> result = new HashSet <SearchResult>(); 657 658 String field = RubyIndexer.FIELD_REQUIRE; 659 660 search(field, require, NameKind.EXACT_NAME, result); 661 662 for (SearchResult map : result) { 664 String file = map.getValue(RubyIndexer.FIELD_FILENAME); 665 666 if (file != null) { 667 return file; 668 } 669 } 670 671 return null; 672 } 673 674 static String getClusterUrl() { 675 if (clusterUrl == null) { 676 File f = 677 InstalledFileLocator.getDefault() 678 .locate("modules/org-netbeans-modules-ruby.jar", null, false); 680 if (f == null) { 681 throw new RuntimeException ("Can't find cluster"); 682 } 683 684 f = new File (f.getParentFile().getParentFile().getAbsolutePath()); 685 686 try { 687 f = f.getCanonicalFile(); 688 clusterUrl = f.toURI().toURL().toExternalForm(); 689 } catch (IOException ioe) { 690 Exceptions.printStackTrace(ioe); 691 } 692 } 693 694 return clusterUrl; 695 } 696 697 static String getPreindexUrl(String url) { 698 String s = getClusterUrl(); 699 700 if (url.startsWith(s)) { 701 url = CLUSTER_URL + "/" + url.substring(s.length()); 702 } 703 704 return url; 705 } 706 707 708 public static FileObject getFileObject(String url) { 709 try { 710 if (url.startsWith(CLUSTER_URL)) { url = getClusterUrl() + "/" + url.substring(CLUSTER_URL.length()); 712 } 713 714 return URLMapper.findFileObject(new URL (url)); 715 } catch (MalformedURLException mue) { 716 Exceptions.printStackTrace(mue); 717 } 718 719 return null; 720 } 721 } 722 | Popular Tags |