1 19 20 package org.netbeans.api.lexer; 21 22 import java.util.ArrayList ; 23 import java.util.Collection ; 24 import java.util.Collections ; 25 import java.util.EnumSet ; 26 import java.util.Map ; 27 import java.util.HashMap ; 28 import java.util.List ; 29 import java.util.Set ; 30 import org.netbeans.lib.lexer.LanguageManager; 31 import org.netbeans.lib.lexer.LexerApiPackageAccessor; 32 import org.netbeans.lib.lexer.LexerSpiPackageAccessor; 33 import org.netbeans.lib.lexer.TokenIdSet; 34 import org.netbeans.lib.lexer.TokenHierarchyOperation; 35 import org.netbeans.lib.lexer.inc.TokenChangeInfo; 36 import org.netbeans.lib.lexer.inc.TokenHierarchyEventInfo; 37 import org.netbeans.lib.lexer.inc.TokenListChange; 38 import org.netbeans.spi.lexer.LanguageHierarchy; 39 40 65 66 public final class Language<T extends TokenId> { 67 68 static { 69 LexerApiPackageAccessor.register(new Accessor()); 70 } 71 72 private final LanguageHierarchy<T> languageHierarchy; 73 74 private String mimeType; 75 76 private final int maxOrdinal; 77 78 private final Set <T> ids; 79 80 81 private TokenIdSet<T> indexedIds; 82 83 private final Map <String ,T> idName2id; 84 85 88 private final Map <String ,Set <T>> cat2ids; 89 90 95 private List <String >[] id2cats; 96 97 104 private List <String >[] id2nonPrimaryCats; 105 106 136 public static Language<? extends TokenId> find(String mimePath) { 137 return LanguageManager.getInstance().findLanguage(mimePath); 138 } 139 140 148 Language(LanguageHierarchy<T> languageHierarchy) { 149 this.languageHierarchy = languageHierarchy; 150 mimeType = LexerSpiPackageAccessor.get().mimeType(languageHierarchy); 151 checkMimeTypeValid(mimeType); 152 Collection <T> createdIds = LexerSpiPackageAccessor.get().createTokenIds(languageHierarchy); 154 if (createdIds == null) 155 throw new IllegalArgumentException ("Ids cannot be null"); maxOrdinal = TokenIdSet.findMaxOrdinal(createdIds); 157 158 if (createdIds instanceof EnumSet ) { 160 ids = (Set <T>)createdIds; 161 } else { ids = new TokenIdSet<T>(createdIds, maxOrdinal, true); 163 } 164 165 Map <String ,Collection <T>> createdCat2ids 167 = LexerSpiPackageAccessor.get().createTokenCategories(languageHierarchy); 168 if (createdCat2ids == null) { 169 createdCat2ids = Collections.emptyMap(); 170 } 171 cat2ids = new HashMap <String ,Set <T>>((int)(createdCat2ids.size() / 0.73f)); 172 for (Map.Entry <String ,Collection <T>> entry : createdCat2ids.entrySet()) { 173 Collection <T> createdCatIds = entry.getValue(); 174 TokenIdSet.checkIdsFromLanguage(createdCatIds, ids); 175 Set <T> catIds = new TokenIdSet<T>(createdCatIds, maxOrdinal, false); 183 cat2ids.put(entry.getKey(), catIds); 184 } 185 186 idName2id = new HashMap <String ,T>((int)(ids.size() / 0.73f)); 188 for (T id : ids) { 189 T sameNameId = idName2id.put(id.name(), id); 190 if (sameNameId != null && sameNameId != id) { throw new IllegalArgumentException (id + 192 " has duplicate name with " + sameNameId); 193 } 194 195 String cat = id.primaryCategory(); 196 if (cat != null) { 197 Set <T> catIds = cat2ids.get(cat); 198 if (catIds == null) { 199 catIds = new TokenIdSet<T>(null, maxOrdinal, false); 200 cat2ids.put(cat, catIds); 201 } 202 catIds.add(id); 203 } 204 } 205 } 206 207 214 public Set <T> tokenIds() { 215 return ids; 216 } 217 218 232 public T tokenId(int ordinal) { 233 synchronized (idName2id) { 234 if (indexedIds == null) { 235 if (ids instanceof EnumSet ) { 236 indexedIds = new TokenIdSet<T>(ids, maxOrdinal, false); 237 } else { indexedIds = (TokenIdSet<T>)ids; 239 } 240 } 241 return indexedIds.indexedIds()[ordinal]; 242 } 243 } 244 245 259 public T validTokenId(int ordinal) { 260 T id = tokenId(ordinal); 261 if (id == null) { 262 throw new IndexOutOfBoundsException ("No tokenId for ordinal=" + ordinal 263 + " in language " + this); 264 } 265 return id; 266 } 267 268 273 public T tokenId(String name) { 274 return idName2id.get(name); 275 } 276 277 281 public T validTokenId(String name) { 282 T id = tokenId(name); 283 if (id == null) { 284 throw new IllegalArgumentException ("No tokenId for name=\"" + name 285 + "\" in language " + this); 286 } 287 return id; 288 } 289 290 295 public int maxOrdinal() { 296 return maxOrdinal; 297 } 298 299 305 public Set <String > tokenCategories() { 306 return Collections.unmodifiableSet(cat2ids.keySet()); 307 } 308 309 315 public Set <T> tokenCategoryMembers(String tokenCategory) { 316 return Collections.unmodifiableSet(cat2ids.get(tokenCategory)); 317 } 318 319 329 public List <String > tokenCategories(T tokenId) { 330 checkMemberId(tokenId); 331 synchronized (idName2id) { 332 if (id2cats == null) { 333 buildTokenIdCategories(); 334 } 335 return id2cats[tokenId.ordinal()]; 336 } 337 } 338 339 352 public List <String > nonPrimaryTokenCategories(T tokenId) { 353 checkMemberId(tokenId); 354 synchronized (idName2id) { 355 if (id2nonPrimaryCats == null) { 356 buildTokenIdCategories(); 357 } 358 return id2nonPrimaryCats[tokenId.ordinal()]; 359 } 360 } 361 362 371 public Set <T> merge(Collection <T> tokenIds1, Collection <T> tokenIds2) { 372 TokenIdSet.checkIdsFromLanguage(tokenIds1, ids); 373 Set <T> ret = new TokenIdSet<T>(tokenIds1, maxOrdinal, false); 376 if (tokenIds2 != null) { 377 TokenIdSet.checkIdsFromLanguage(tokenIds2, ids); 378 ret.addAll(tokenIds2); 379 } 380 return ret; 381 } 382 383 388 public String mimeType() { 389 return mimeType; 390 } 391 392 393 public boolean equals(Object obj) { 394 return super.equals(obj); 395 } 396 397 398 public int hashCode() { 399 return super.hashCode(); 400 } 401 402 private void buildTokenIdCategories() { 403 assignCatArrays(); 404 List <Map <String ,Object >> catMapsList = new ArrayList <Map <String ,Object >>(4); 410 List <String > idCats = new ArrayList <String >(4); 412 for (T id : ids) { 413 for (Map.Entry <String ,Set <T>> e : cat2ids.entrySet()) { 418 if (e.getValue().contains(id)) { 419 idCats.add(e.getKey()); } 421 } 422 id2cats[id.ordinal()] = findCatList(catMapsList, idCats, 0); 424 id2nonPrimaryCats[id.ordinal()] = findCatList(catMapsList, idCats, 1); 425 426 idCats.clear(); } 428 } 429 430 448 private static List <String > findCatList(List <Map <String ,Object >> catMapsList, List <String > idCats, int startIndex) { 449 int size = idCats.size() - startIndex; 450 if (size <= 0) { 451 return Collections.emptyList(); 452 } 453 while (catMapsList.size() < size) { 454 catMapsList.add(new HashMap <String ,Object >()); 455 } 456 Map <String ,Object > m = catMapsList.get(--size); 458 for (int i = startIndex; i < size; i++) { 459 @SuppressWarnings ("unchecked") 460 Map <String ,Object > catMap = (Map <String ,Object >)m.get(idCats.get(i)); 461 if (catMap == null) { 462 catMap = new HashMap <String ,Object >(); 463 m.put(idCats.get(i), catMap); 465 } 466 m = catMap; 467 } 468 469 @SuppressWarnings ("unchecked") 470 List <String > catList = (List <String >)m.get(idCats.get(size)); 471 if (catList == null) { 472 catList = new ArrayList <String >(idCats.size() - startIndex); 473 catList.addAll((startIndex > 0) 474 ? idCats.subList(startIndex, idCats.size()) 475 : idCats); 476 m.put(idCats.get(size), catList); 477 } 478 return catList; 479 } 480 481 @SuppressWarnings ("unchecked") 482 private void assignCatArrays() { 483 id2cats = (List <String >[])new List [maxOrdinal + 1]; 484 id2nonPrimaryCats = (List <String >[])new List [maxOrdinal + 1]; 485 } 486 487 492 public String dumpInfo() { 493 StringBuilder sb = new StringBuilder (); 494 for (T id : ids) { 495 sb.append(id); 496 List <String > cats = tokenCategories(id); 497 if (cats.size() > 0) { 498 sb.append(": "); 499 for (int i = 0; i < cats.size(); i++) { 500 if (i > 0) { 501 sb.append(", "); 502 } 503 String cat = (String )cats.get(i); 504 sb.append('"'); 505 sb.append(cat); 506 sb.append('"'); 507 } 508 } 509 } 510 return ids.toString(); 511 } 512 513 public String toString() { 514 return mimeType + ", LH: " + languageHierarchy; 515 } 516 517 private void checkMemberId(T id) { 518 if (!ids.contains(id)) { 519 throw new IllegalArgumentException (id + " does not belong to language " + this); } 521 } 522 523 private static void checkMimeTypeValid(String mimeType) { 524 if (mimeType == null) { 525 throw new IllegalStateException ("mimeType cannot be null"); } 527 int slashIndex = mimeType.indexOf('/'); 528 if (slashIndex == -1) { throw new IllegalStateException ("mimeType=" + mimeType + " does not contain '/'"); } 531 if (mimeType.indexOf('/', slashIndex + 1) != -1) { 532 throw new IllegalStateException ("mimeType=" + mimeType + " contains more than one '/'"); } 534 } 535 536 541 LanguageHierarchy<T> languageHierarchy() { 542 return languageHierarchy; 543 } 544 545 549 private static final class Accessor extends LexerApiPackageAccessor { 550 551 public <T extends TokenId> Language<T> createLanguage( 552 LanguageHierarchy<T> languageHierarchy) { 553 return new Language<T>(languageHierarchy); 554 } 555 556 public <T extends TokenId> LanguageHierarchy<T> languageHierarchy( 557 Language<T> language) { 558 return language.languageHierarchy(); 559 } 560 561 public <I> TokenHierarchy<I> createTokenHierarchy( 562 TokenHierarchyOperation<I,?> tokenHierarchyOperation) { 563 return new TokenHierarchy<I>(tokenHierarchyOperation); 564 } 565 566 public TokenHierarchyEvent createTokenChangeEvent( 567 TokenHierarchyEventInfo info) { 568 return new TokenHierarchyEvent(info); 569 } 570 571 public <T extends TokenId> TokenChange<T> createTokenChange( 572 TokenChangeInfo<T> info) { 573 return new TokenChange<T>(info); 574 } 575 576 public <T extends TokenId> TokenChangeInfo<T> tokenChangeInfo( 577 TokenChange<T> tokenChange) { 578 return tokenChange.info(); 579 } 580 581 public <I> TokenHierarchyOperation<I,?> tokenHierarchyOperation( 582 TokenHierarchy<I> tokenHierarchy) { 583 return tokenHierarchy.operation(); 584 } 585 586 } 587 588 } 589 590 | Popular Tags |