1 19 package org.netbeans.modules.ruby; 20 21 import java.io.IOException ; 22 import java.util.Collections ; 23 import java.util.EnumSet ; 24 import java.util.HashMap ; 25 import java.util.HashSet ; 26 import java.util.List ; 27 import java.util.Map ; 28 import java.util.Set ; 29 30 import org.jruby.ast.ClassNode; 31 import org.jruby.ast.Node; 32 import org.netbeans.api.gsf.Element; 33 import org.netbeans.api.gsf.ElementKind; 34 import org.netbeans.api.gsf.Index; 35 import org.netbeans.api.gsf.Indexer; 36 import org.netbeans.api.gsf.Modifier; 37 import org.netbeans.api.gsf.ParserFile; 38 import org.netbeans.api.gsf.ParserResult; 39 import org.netbeans.editor.BaseDocument; 40 import org.netbeans.modules.ruby.elements.AstElement; 41 import org.netbeans.modules.ruby.elements.ClassElement; 42 import org.openide.filesystems.FileObject; 43 import org.openide.util.Exceptions; 44 45 46 54 public class RubyIndexer implements Indexer { 55 private static final boolean INDEX_UNDOCUMENTED = Boolean.getBoolean("ruby.index.undocumented"); 56 private static final boolean PREINDEXING = Boolean.getBoolean("gsf.preindexing"); 57 58 static final String FIELD_EXTENDS_NAME = "extends"; static final String FIELD_FQN_NAME = "fqn"; static final String FIELD_IN = "in"; static final String FIELD_FILENAME = "source"; static final String FIELD_CLASS_NAME = "class"; static final String FIELD_CASE_INSENSITIVE_CLASS_NAME = "class-ig"; static final String FIELD_REQUIRE = "require"; static final String FIELD_REQUIRES = "requires"; static final String FIELD_INCLUDES = "includes"; 69 70 static final String FIELD_METHOD_NAME = "method"; 72 73 static final String FIELD_FIELD_NAME = "field"; static final String FIELD_ATTRIBUTE_NAME = "attribute"; static final String FIELD_CONSTANT_NAME = "constant"; 77 78 static final String FIELD_CLASS_ATTRS = "attrs"; 81 86 89 public RubyIndexer() { 93 } 94 95 public void updateIndex(Index index, ParserResult result) 96 throws IOException { 97 Node root = AstUtilities.getRoot(result); 98 RubyParseResult r = (RubyParseResult)result; 99 100 if (root == null) { 101 return; 102 } 103 104 if (r.isSanitizedSource()) { 108 return; 109 } 110 111 TreeAnalyzer analyzer = new TreeAnalyzer(index, r); 112 analyzer.analyze(); 113 } 114 115 public boolean isIndexable(ParserFile file) { 116 return file.getNameExt().endsWith(".rb"); 118 } 119 120 private static String getModifierString(Set <Modifier> modifiers) { 121 if (modifiers.contains(Modifier.STATIC)) { 122 if (modifiers.contains(Modifier.PRIVATE)) { 123 return "si"; 124 } else if (modifiers.contains(Modifier.PROTECTED)) { 125 return "so"; 126 } else { 127 return "s"; 128 } 129 } else if (modifiers.contains(Modifier.PRIVATE)) { 130 return "i"; 131 } else if (modifiers.contains(Modifier.PROTECTED)) { 132 return "o"; 133 } else { 134 return null; 135 } 136 } 137 138 public static Set <Modifier> getModifiersFromString(String modifierString, int startIndex) { 139 Set <Modifier> modifiers = Collections.emptySet(); 140 Modifier access = Modifier.PUBLIC; 141 boolean instance = true; 142 143 for (int i = startIndex + 1; i < modifierString.length(); i++) { 144 char c = modifierString.charAt(i); 145 146 switch (c) { 147 case 'i': 148 access = Modifier.PRIVATE; 149 150 break; 151 152 case 'o': 153 access = Modifier.PROTECTED; 154 155 break; 156 157 case 's': 158 instance = false; 159 160 break; 161 } 162 } 163 164 if (access != Modifier.PUBLIC) { 165 if (instance) { 166 modifiers = EnumSet.of(access); 167 } else { 168 modifiers = EnumSet.of(access, Modifier.STATIC); 169 } 170 } else if (!instance) { 171 modifiers = EnumSet.of(Modifier.STATIC); 172 } 173 174 return modifiers; 175 } 176 177 private static class TreeAnalyzer { 178 private ParserFile file; 179 private String url; 180 private String requires; 181 private RubyParseResult result; 182 private BaseDocument doc; 183 private Index index; 184 185 private TreeAnalyzer(Index index, RubyParseResult result) { 186 this.index = index; 187 this.result = result; 188 this.file = result.getFile(); 189 190 FileObject fo = file.getFileObject(); 191 192 if (fo != null) { 193 this.doc = AstUtilities.getBaseDocument(fo, true); 194 } 195 196 try { 197 url = file.getFileObject().getURL().toExternalForm(); 198 199 if (PREINDEXING) { 200 url = RubyIndex.getPreindexUrl(url); 202 } 203 } catch (IOException ioe) { 204 Exceptions.printStackTrace(ioe); 205 } 206 } 207 208 public void analyze() throws IOException { 209 if (!file.isPlatform()) { 211 Set <Map <String , String >> indexedList = Collections.emptySet(); 212 Set <Map <String , String >> notIndexedList = Collections.emptySet(); 213 Map <String , String > toDelete = new HashMap <String ,String >(); 214 toDelete.put(FIELD_FILENAME, url); 215 try { 216 index.gsfStore(indexedList, notIndexedList, toDelete); 217 } catch (IOException ioe) { 218 Exceptions.printStackTrace(ioe); 219 } 220 } 221 222 224 Set <String > requireSet = result.getRequires(); 228 229 if ((requireSet != null) && (requireSet.size() > 0)) { 230 StringBuilder sb = new StringBuilder (20 * requireSet.size()); 231 232 for (String s : requireSet) { 233 if (sb.length() > 0) { 234 sb.append(","); } 236 237 sb.append(s); 238 } 239 240 requires = sb.toString(); 241 } 242 243 StructureAnalyzer.analyze(result); 244 245 List <?extends Element> structure = result.getStructure(); 246 247 if ((structure == null) || (structure.size() == 0)) { 248 return; 249 } 250 251 for (Element o : structure) { 252 AstElement jn = (AstElement)o; 255 analyze(jn); 256 } 257 } 258 259 private void analyze(AstElement element) { 260 switch (element.getKind()) { 261 case MODULE: 262 case CLASS: 263 analyzeClassOrModule(element); 264 265 break; 266 267 case CONSTRUCTOR: 268 case METHOD: 269 case FIELD: 270 case ATTRIBUTE: 271 case CONSTANT: 272 273 276 break; 278 } 279 } 280 281 private void analyzeClassOrModule(AstElement element) { 282 Set <Map <String , String >> indexedList = new HashSet <Map <String , String >>(); 284 Set <Map <String , String >> notIndexedList = new HashSet <Map <String , String >>(); 285 286 Map <String , String > indexed = new HashMap <String , String >(); 288 indexedList.add(indexed); 289 290 Map <String , String > notIndexed = new HashMap <String , String >(); 291 notIndexedList.add(notIndexed); 292 293 String attributes; 294 String fqn; 295 296 Node node = element.getNode(); 297 298 if (element.getKind() == ElementKind.CLASS) { 299 ClassElement classElement = (ClassElement)element; 300 fqn = classElement.getFqn(); 301 302 ClassNode clz = (ClassNode)node; 303 Node superNode = clz.getSuperNode(); 304 String superClass = null; 305 306 if (superNode != null) { 307 superClass = AstUtilities.getSuperclass(clz); 308 } 309 310 if (superClass != null) { 311 notIndexed.put(FIELD_EXTENDS_NAME, superClass); 312 } 313 314 Set <String > includes = classElement.getIncludes(); 315 316 if ((includes != null) && (includes.size() > 0)) { 317 StringBuilder sb = new StringBuilder (); 318 319 for (String include : includes) { 320 if (sb.length() > 0) { 321 sb.append(","); 322 } 323 324 sb.append(include); 325 } 326 327 notIndexed.put(FIELD_INCLUDES, sb.toString()); 328 } 329 330 attributes = "c"; 331 } else { 332 assert element.getKind() == ElementKind.MODULE; 333 334 ClassElement moduleElement = (ClassElement)element; 335 fqn = moduleElement.getFqn(); 336 337 attributes = "m"; 338 } 339 340 String name = element.getName(); 341 342 String in; 343 int classIndex = fqn.lastIndexOf("::"); 344 345 if (classIndex != -1) { 346 in = fqn.substring(0, classIndex); 347 } else { 348 in = null; 349 } 350 351 boolean isDocumented = isDocumented(node); 352 int documentSize = getDocumentSize(node); 353 354 if (documentSize > 0) { 355 attributes = attributes + "d(" + documentSize + ")"; 356 } 357 358 notIndexed.put(FIELD_CLASS_ATTRS, attributes); 359 360 363 if (file.isPlatform() && (element.getKind() == ElementKind.CLASS) && 364 !INDEX_UNDOCUMENTED && !isDocumented) { 365 return; 369 } 370 371 indexed.put(FIELD_FQN_NAME, fqn); 372 indexed.put(FIELD_CASE_INSENSITIVE_CLASS_NAME, name.toLowerCase()); 373 indexed.put(FIELD_CLASS_NAME, name); 374 375 if (in != null) { 376 notIndexed.put(FIELD_IN, in); 377 } 378 379 addRequire(indexed); 380 381 if (requires != null) { 384 notIndexed.put(FIELD_REQUIRES, requires); 385 } 386 387 indexed.put(FIELD_FILENAME, url); 389 390 for (AstElement child : element.getChildren()) { 392 switch (child.getKind()) { 393 case CLASS: 394 case MODULE: { 395 analyzeClassOrModule(child); 396 397 break; 398 } 399 400 case CONSTRUCTOR: 401 case METHOD: { 402 Map <String , String > ru; 403 ru = new HashMap <String , String >(); 404 indexedList.add(ru); 405 406 Node childNode = child.getNode(); 407 String signature = AstUtilities.getDefSignature(childNode); 408 409 String modifierString = getModifierString(child.getModifiers()); 410 411 boolean methodIsDocumented = isDocumented(childNode); 412 413 if (methodIsDocumented) { 414 if (modifierString == null) { 415 modifierString = "d"; 416 } else { 417 modifierString = modifierString + "d"; 418 } 419 } 420 421 if (modifierString != null) { 422 signature = signature + ":" + modifierString; 423 } 424 425 ru.put(FIELD_METHOD_NAME, signature); 426 427 if (child.getName().equals("initialize")) { 433 Map <String , String > ru2; 435 ru2 = new HashMap <String , String >(); 436 indexedList.add(ru2); 437 438 signature = signature.replaceFirst("initialize", "new"); 442 signature = signature + "s"; 443 ru2.put(FIELD_METHOD_NAME, signature); 444 } 445 446 break; 447 } 448 449 case FIELD: { 450 Map <String , String > ru; 451 ru = new HashMap <String , String >(); 452 indexedList.add(ru); 453 454 String signature = child.getName(); 455 String modifierString = getModifierString(child.getModifiers()); 456 457 if (modifierString != null) { 458 signature = signature + ":" + modifierString; 459 } 460 461 ru.put(FIELD_FIELD_NAME, signature); 463 464 break; 465 } 466 467 case ATTRIBUTE: { 468 Map <String , String > ru; 469 ru = new HashMap <String , String >(); 470 indexedList.add(ru); 471 472 ru.put(FIELD_ATTRIBUTE_NAME, child.getName()); 473 474 break; 475 } 476 477 case CONSTANT: { 478 Map <String , String > ru; 479 ru = new HashMap <String , String >(); 480 indexedList.add(ru); 481 482 ru.put(FIELD_CONSTANT_NAME, child.getName()); 484 485 break; 486 } 487 } 488 } 489 490 try { 491 Map <String , String > toDelete = Collections.emptyMap(); 492 index.gsfStore(indexedList, notIndexedList, toDelete); 493 } catch (IOException ioe) { 494 Exceptions.printStackTrace(ioe); 495 } 496 } 497 498 private int getDocumentSize(Node node) { 499 if (doc != null) { 500 List <String > comments = AstUtilities.gatherDocumentation(doc, node); 501 502 if ((comments != null) && (comments.size() > 0)) { 503 int size = 0; 504 505 for (String line : comments) { 506 size += line.length(); 507 } 508 509 return size; 510 } 511 } 512 513 return 0; 514 } 515 516 private boolean isDocumented(Node node) { 517 if (doc != null) { 518 List <String > comments = AstUtilities.gatherDocumentation(doc, node); 519 520 if ((comments != null) && (comments.size() > 0)) { 521 return true; 522 } 523 } 524 525 return false; 526 } 527 528 private void addRequire(Map <String , String > ru) { 529 FileObject fo = file.getFileObject(); 534 String folder = fo.getParent().getNameExt(); 535 536 if (folder.equals("rubystubs") && fo.getName().startsWith("stub_")) { 537 return; 538 } 539 540 String relative = file.getRelativePath(); 542 543 if (relative != null) { 544 if (relative.endsWith(".rb")) { relative = relative.substring(0, relative.length() - 3); 546 ru.put(FIELD_REQUIRE, relative); 547 } 548 } 549 } 550 } 551 } 552 | Popular Tags |