1 19 20 package org.netbeans.lib.lexer; 21 22 import java.io.Reader ; 23 import java.lang.ref.Reference ; 24 import java.lang.ref.WeakReference ; 25 import java.util.ArrayList ; 26 import java.util.HashSet ; 27 import java.util.List ; 28 import java.util.Set ; 29 import javax.swing.event.EventListenerList ; 30 import org.netbeans.api.lexer.Language; 31 import org.netbeans.api.lexer.TokenHierarchyEvent; 32 import org.netbeans.api.lexer.TokenHierarchyListener; 33 import org.netbeans.api.lexer.TokenHierarchy; 34 import org.netbeans.lib.lexer.batch.CopyTextTokenList; 35 import org.netbeans.lib.lexer.batch.TextTokenList; 36 import org.netbeans.lib.lexer.inc.IncTokenList; 37 import org.netbeans.lib.lexer.inc.TokenHierarchyEventInfo; 38 import org.netbeans.lib.lexer.inc.TokenListUpdater; 39 import org.netbeans.spi.lexer.MutableTextInput; 40 import org.netbeans.api.lexer.InputAttributes; 41 import org.netbeans.api.lexer.LanguagePath; 42 import org.netbeans.api.lexer.TokenHierarchyEventType; 43 import org.netbeans.api.lexer.TokenId; 44 import org.netbeans.lib.lexer.inc.SnapshotTokenList; 45 import org.netbeans.lib.lexer.inc.TokenListChange; 46 import org.netbeans.lib.lexer.token.AbstractToken; 47 48 58 59 public final class TokenHierarchyOperation<I, T extends TokenId> { 61 66 private TokenHierarchy<I> tokenHierarchy; 67 68 71 private MutableTextInput<I> mutableTextInput; 72 73 private TokenList<T> tokenList; 74 75 82 private boolean active = true; 83 84 87 private TokenHierarchyOperation<I,T> liveTokenHierarchyOperation; 88 89 92 private List <SnapshotRef> snapshotRefs; 93 94 97 private EventListenerList listenerList; 98 99 private boolean snapshotReleased; 100 101 private Set <LanguagePath> languagePaths; 102 103 private Set <Language<? extends TokenId>> exploredLanguages; 104 105 108 public TokenHierarchyOperation(Reader inputReader, 109 Language<T> language, Set <T> skipTokenIds, InputAttributes inputAttributes) { 110 this.tokenList = new CopyTextTokenList<T>(this, inputReader, 111 language, skipTokenIds, inputAttributes); 112 init(); 113 } 114 115 118 public TokenHierarchyOperation(CharSequence inputText, boolean copyInputText, 119 Language<T> language, Set <T> skipTokenIds, InputAttributes inputAttributes) { 120 this.tokenList = copyInputText 121 ? new CopyTextTokenList<T>(this, inputText, 122 language, skipTokenIds, inputAttributes) 123 : new TextTokenList<T>(this, inputText, 124 language, skipTokenIds, inputAttributes); 125 init(); 126 } 127 128 131 public TokenHierarchyOperation(MutableTextInput<I> mutableTextInput, 132 Language<T> language) { 133 this.mutableTextInput = mutableTextInput; 134 this.tokenList = new IncTokenList<T>(this, mutableTextInput); 135 init(); 136 } 137 138 public TokenHierarchyOperation(TokenHierarchyOperation<I,T> liveTokenHierarchy) { 139 this.liveTokenHierarchyOperation = liveTokenHierarchy; 140 this.tokenList = new SnapshotTokenList<T>(this); 141 init(); 142 } 143 144 private void init() { 145 assert (tokenHierarchy == null); 146 tokenHierarchy = LexerApiPackageAccessor.get().createTokenHierarchy(this); 147 listenerList = new EventListenerList (); 150 if (isMutable()) { 151 snapshotRefs = new ArrayList <SnapshotRef>(1); 152 } 153 } 154 155 public TokenHierarchy<I> tokenHierarchy() { 156 return tokenHierarchy; 157 } 158 159 public TokenList<T> tokenList() { 160 return tokenList; 161 } 162 163 public TokenList<T> checkedTokenList() { 164 checkSnapshotNotReleased(); 165 return tokenList(); 166 } 167 168 public boolean isMutable() { 169 return (mutableTextInput != null); 170 } 171 172 public MutableTextInput mutableTextInput() { 173 return mutableTextInput; 174 } 175 176 public I mutableInputSource() { 177 return isMutable() 178 ? LexerSpiPackageAccessor.get().inputSource(mutableTextInput) 179 : null; 180 } 181 182 public void setActive(boolean active) { 183 assert (isMutable()); 184 if (this.active != active) { 185 this.active = active; 186 } 187 } 188 189 public boolean isActive() { 190 return active; 191 } 192 193 public void rebuild() { 194 if (isSnapshot()) return; 196 if (active) { 197 IncTokenList<T> incTokenList = (IncTokenList<T>)tokenList; 198 incTokenList.incrementModCount(); 199 TokenListChange<T> change = new TokenListChange<T>(incTokenList); 200 CharSequence text = LexerSpiPackageAccessor.get().text(mutableTextInput); 201 int endOffset = incTokenList.existingTokensEndOffset(); 202 TokenHierarchyEventInfo eventInfo = new TokenHierarchyEventInfo( 203 this, TokenHierarchyEventType.REBUILD, 0, 0, text, 0); 204 change.setIndex(0); 205 change.setOffset(0); 206 change.setAddedEndOffset(0); 208 incTokenList.replaceTokens(eventInfo, change, incTokenList.tokenCountCurrent()); 209 incTokenList.restartLexing(); incTokenList.incrementModCount(); 211 212 synchronized (snapshotRefs) { 213 for (int i = snapshotRefs.size() - 1; i >= 0; i--) { 214 TokenHierarchyOperation<I,T> op = snapshotRefs.get(i).get(); 215 if (op != null) { 216 ((SnapshotTokenList<T>)op.tokenList()).update(eventInfo, change); 217 } 218 } 219 } 220 eventInfo.setTokenChangeInfo(change.tokenChangeInfo()); 221 eventInfo.setAffectedStartOffset(0); 222 eventInfo.setAffectedEndOffset(text.length()); 223 fireTokenHierarchyChanged( 224 LexerApiPackageAccessor.get().createTokenChangeEvent(eventInfo)); 225 } } 227 228 public void fireTokenHierarchyChanged(TokenHierarchyEvent evt) { 229 Object [] listeners = listenerList.getListenerList(); 230 int listenersLength = listeners.length; 231 for (int i = 1; i < listenersLength; i += 2) { 232 ((TokenHierarchyListener)listeners[i]).tokenHierarchyChanged(evt); 233 } 234 } 235 236 public void addTokenHierarchyListener(TokenHierarchyListener listener) { 237 listenerList.add(TokenHierarchyListener.class, listener); 238 } 239 240 public void removeTokenHierarchyListener(TokenHierarchyListener listener) { 241 listenerList.remove(TokenHierarchyListener.class, listener); 242 } 243 244 public void textModified(int offset, int removedLength, CharSequence removedText, int insertedLength) { 245 TokenHierarchyEventInfo eventInfo = new TokenHierarchyEventInfo( 246 this, TokenHierarchyEventType.MODIFICATION, 247 offset, removedLength, removedText, insertedLength); 248 if (active) { 249 IncTokenList<T> incTokenList = (IncTokenList<T>)tokenList; 250 incTokenList.incrementModCount(); 251 TokenListChange<T> change = new TokenListChange<T>(incTokenList); 252 TokenListUpdater.update(incTokenList, eventInfo, change); 253 if (!incTokenList.isFullyLexed()) 254 incTokenList.refreshLexerInputOperation(); 255 256 synchronized (snapshotRefs) { 257 for (int i = snapshotRefs.size() - 1; i >= 0; i--) { 258 TokenHierarchyOperation<I,T> op = snapshotRefs.get(i).get(); 259 260 if (op != null) { 261 ((SnapshotTokenList<T>)op.tokenList()).update(eventInfo, change); 262 } 263 } 264 } 265 eventInfo.setTokenChangeInfo(change.tokenChangeInfo()); 266 eventInfo.setAffectedStartOffset(change.offset()); 267 eventInfo.setAffectedEndOffset(change.addedEndOffset()); 268 fireTokenHierarchyChanged( 269 LexerApiPackageAccessor.get().createTokenChangeEvent(eventInfo)); 270 } } 272 273 private Language<T> language() { 274 TokenList<T> tl = tokenList(); 275 Language<? extends TokenId> l; 276 if (tl != null) { 277 l = tokenList.languagePath().topLanguage(); 278 } else { 279 assert (mutableTextInput != null); 280 l = LexerSpiPackageAccessor.get().language(mutableTextInput); 281 } 282 @SuppressWarnings ("unchecked") 283 Language<T> language = (Language<T>)l; 284 return language; 285 } 286 287 public Set <LanguagePath> languagePaths() { 288 Set <LanguagePath> lps; 289 synchronized (this) { 290 lps = languagePaths; 291 } 292 if (lps == null) { 293 LanguageOperation<T> langOp = LexerUtilsConstants.languageOperation(language()); 294 @SuppressWarnings ("unchecked") 295 Set <LanguagePath> clps = (Set <LanguagePath>) 296 ((HashSet <LanguagePath>)langOp.languagePaths()).clone(); 297 298 @SuppressWarnings ("unchecked") 299 Set <Language<? extends TokenId>> cel = (Set <Language<? extends TokenId>>) 300 ((HashSet <Language<? extends TokenId>>)langOp.exploredLanguages()).clone(); 301 synchronized (this) { 302 languagePaths = lps; 303 exploredLanguages = cel; 304 } 305 } 306 return lps; 307 } 308 309 public void addLanguagePath(LanguagePath lp, Language language) { 310 Set <LanguagePath> elps = languagePaths(); if (!elps.contains(lp)) { 312 Set <LanguagePath> lps = new HashSet <LanguagePath>(); 314 LanguageOperation.findLanguagePaths(elps, lps, exploredLanguages, lp, null); 315 elps.addAll(lps); 316 } 318 } 319 320 public boolean isSnapshot() { 321 return (liveTokenHierarchyOperation != null); 322 } 323 324 public TokenHierarchy<I> snapshotOf() { 325 return (isSnapshot() ? liveTokenHierarchyOperation.tokenHierarchy() : null); 326 } 327 328 private void checkIsSnapshot() { 329 if (!isSnapshot()) { 330 throw new IllegalStateException ("Not a snapshot"); 331 } 332 } 333 334 private void checkSnapshotNotReleased() { 335 if (snapshotReleased) { 336 throw new IllegalStateException ("Snapshot already released"); } 338 } 339 340 public TokenHierarchy<I> createSnapshot() { 341 if (isMutable()) { 342 TokenHierarchyOperation<I,T> snapshot = new TokenHierarchyOperation<I,T>(this); 343 snapshotRefs.add(new SnapshotRef(snapshot)); 344 return snapshot.tokenHierarchy(); 345 } 346 return null; 347 } 348 349 public void snapshotRelease() { 350 checkIsSnapshot(); 351 checkSnapshotNotReleased(); 352 353 snapshotReleased = true; 354 if (liveTokenHierarchyOperation != null) { liveTokenHierarchyOperation.removeSnapshot(this); 357 } 358 } 359 360 public boolean isSnapshotReleased() { 361 return snapshotReleased; 362 } 363 364 void removeSnapshot(SnapshotRef snapshotRef) { 365 synchronized (snapshotRefs) { 366 snapshotRefs.remove(snapshotRef); 367 } 368 } 369 370 void removeSnapshot(TokenHierarchyOperation<I,T> snapshot) { 371 synchronized (snapshotRefs) { 372 for (int i = snapshotRefs.size() - 1; i >= 0; i--) { 373 Reference ref = (Reference )snapshotRefs.get(i); 374 if (ref.get() == snapshot) { 375 snapshotRefs.remove(i); 376 break; 377 } 378 } 379 } 380 } 381 382 private int snapshotCount() { 383 synchronized (snapshotRefs) { 384 return snapshotRefs.size(); 385 } 386 } 387 388 public boolean canModifyToken(int index, AbstractToken token) { 389 synchronized (snapshotRefs) { 390 for (int i = snapshotCount() - 1; i >= 0; i--) { 391 TokenHierarchyOperation op = snapshotRefs.get(i).get(); 392 393 if (op != null && ((SnapshotTokenList) op.tokenList()).canModifyToken(index, token)) { 394 return false; 395 } 396 } 397 } 398 return true; 399 } 400 401 public TokenHierarchyOperation<I,T> liveTokenHierarchyOperation() { 402 return liveTokenHierarchyOperation; 403 } 404 405 public <TT extends TokenId> int tokenOffset(AbstractToken<TT> token, TokenList<TT> tokenList, int rawOffset) { 406 if (this.tokenList.getClass() == SnapshotTokenList.class) { 407 if (tokenList != null) { 408 @SuppressWarnings ("unchecked") 409 SnapshotTokenList<TT> tlUC = (SnapshotTokenList<TT>)this.tokenList; 410 return tlUC.tokenOffset(token, tokenList, rawOffset); 411 } else { return rawOffset; 413 } 414 } else { return (tokenList != null) 416 ? tokenList.childTokenOffset(rawOffset) 417 : rawOffset; 418 } 419 } 420 421 public int tokenShiftStartOffset() { 422 return isSnapshot() ? ((SnapshotTokenList)tokenList).tokenShiftStartOffset() : -1; 423 } 424 425 public int tokenShiftEndOffset() { 426 return isSnapshot() ? ((SnapshotTokenList)tokenList).tokenShiftEndOffset() : -1; 427 } 428 429 430 private final class SnapshotRef extends WeakReference <TokenHierarchyOperation<I,T>> implements Runnable { 431 432 SnapshotRef(TokenHierarchyOperation<I,T> snapshot) { 433 super(snapshot, org.openide.util.Utilities.activeReferenceQueue()); 434 } 435 436 public void run() { 437 if (liveTokenHierarchyOperation != null) { 438 liveTokenHierarchyOperation.removeSnapshot(this); 440 } 441 } 442 443 } 444 445 } 446 | Popular Tags |