KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > lib > lexer > TokenHierarchyOperation


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.lib.lexer;
21
22 import java.io.Reader JavaDoc;
23 import java.lang.ref.Reference JavaDoc;
24 import java.lang.ref.WeakReference JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Set JavaDoc;
29 import javax.swing.event.EventListenerList JavaDoc;
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 /**
49  * Token hierarchy operation services tasks of its associated token hierarchy.
50  * <br/>
51  * There is one-to-one relationship between token hierarchy and its operation.
52  * <br/>
53  * Token hierarchy may be a snapshot of an original
54  *
55  * @author Miloslav Metelka
56  * @version 1.00
57  */

58
59 public final class TokenHierarchyOperation<I, T extends TokenId> { // "I" stands for input
60

61     /**
62      * The token hierarchy delegating to this operation.
63      * <br>
64      * There is one-to-one relationship between token hierarchy and its operation.
65      */

66     private TokenHierarchy<I> tokenHierarchy;
67     
68     /**
69      * Mutable text input for mutable token hierarchy or null otherwise.
70      */

71     private MutableTextInput<I> mutableTextInput;
72     
73     private TokenList<T> tokenList;
74     
75     /**
76      * The hierarchy can be made inactive to release the tokens
77      * and the memory that they consume temporarily.
78      * <br>
79      * By default the hierarchy is active so the tokens
80      * will be created lazily for it.
81      */

82     private boolean active = true;
83
84     /**
85      * Primary token hierarchy for snapshot.
86      */

87     private TokenHierarchyOperation<I,T> liveTokenHierarchyOperation;
88     
89     /**
90      * References to active snapshots.
91      */

92     private List JavaDoc<SnapshotRef> snapshotRefs;
93
94     /**
95      * Listener list solely for token change listeners.
96      */

97     private EventListenerList JavaDoc listenerList;
98     
99     private boolean snapshotReleased;
100     
101     private Set JavaDoc<LanguagePath> languagePaths;
102     
103     private Set JavaDoc<Language<? extends TokenId>> exploredLanguages;
104
105     /**
106      * Constructor for reader as input.
107      */

108     public TokenHierarchyOperation(Reader JavaDoc inputReader,
109     Language<T> language, Set JavaDoc<T> skipTokenIds, InputAttributes inputAttributes) {
110         this.tokenList = new CopyTextTokenList<T>(this, inputReader,
111                 language, skipTokenIds, inputAttributes);
112         init();
113     }
114
115     /**
116      * Constructor for character sequence as input.
117      */

118     public TokenHierarchyOperation(CharSequence JavaDoc inputText, boolean copyInputText,
119     Language<T> language, Set JavaDoc<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     /**
129      * Constructor for mutable input.
130      */

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         // Create listener list even for non-mutable hierarchies as there may be
148
// custom embeddings created that need to be notified
149
listenerList = new EventListenerList JavaDoc();
150         if (isMutable()) {
151             snapshotRefs = new ArrayList JavaDoc<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()) // Do nothing for snapshot
195
return;
196         if (active) {
197             IncTokenList<T> incTokenList = (IncTokenList<T>)tokenList;
198             incTokenList.incrementModCount();
199             TokenListChange<T> change = new TokenListChange<T>(incTokenList);
200             CharSequence JavaDoc 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); // Tokens will be recreated lazily
207

208             incTokenList.replaceTokens(eventInfo, change, incTokenList.tokenCountCurrent());
209             incTokenList.restartLexing(); // Will relex tokens lazily
210
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         } // not active - no changes fired
226
}
227     
228     public void fireTokenHierarchyChanged(TokenHierarchyEvent evt) {
229         Object JavaDoc[] 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 JavaDoc 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         } // not active - no changes fired
271
}
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 JavaDoc("unchecked")
283         Language<T> language = (Language<T>)l;
284         return language;
285     }
286     
287     public Set JavaDoc<LanguagePath> languagePaths() {
288         Set JavaDoc<LanguagePath> lps;
289         synchronized (this) {
290             lps = languagePaths;
291         }
292         if (lps == null) {
293             LanguageOperation<T> langOp = LexerUtilsConstants.languageOperation(language());
294             @SuppressWarnings JavaDoc("unchecked")
295             Set JavaDoc<LanguagePath> clps = (Set JavaDoc<LanguagePath>)
296                     ((HashSet JavaDoc<LanguagePath>)langOp.languagePaths()).clone();
297
298             @SuppressWarnings JavaDoc("unchecked")
299             Set JavaDoc<Language<? extends TokenId>> cel = (Set JavaDoc<Language<? extends TokenId>>)
300                     ((HashSet JavaDoc<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 JavaDoc<LanguagePath> elps = languagePaths(); // init if not inited yet
311
if (!elps.contains(lp)) {
312             // Add the new language path
313
Set JavaDoc<LanguagePath> lps = new HashSet JavaDoc<LanguagePath>();
314             LanguageOperation.findLanguagePaths(elps, lps, exploredLanguages, lp, null);
315             elps.addAll(lps);
316             // Fire the token hierarchy change event
317
}
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 JavaDoc("Not a snapshot");
331         }
332     }
333
334     private void checkSnapshotNotReleased() {
335         if (snapshotReleased) {
336             throw new IllegalStateException JavaDoc("Snapshot already released"); // NOI18N
337
}
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) { // only when "real" snapshot for mutable hierarchies
355
// Remove the reference from the snapshots array
356
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 JavaDoc ref = (Reference JavaDoc)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 JavaDoc("unchecked")
409                 SnapshotTokenList<TT> tlUC = (SnapshotTokenList<TT>)this.tokenList;
410                 return tlUC.tokenOffset(token, tokenList, rawOffset);
411             } else { // passed tokenList is null => token removed from EmbeddedTokenList
412
return rawOffset;
413             }
414         } else { // not a snapshot - regular situation
415
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 JavaDoc<TokenHierarchyOperation<I,T>> implements Runnable JavaDoc {
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                 // Remove the reference from the snapshots array
439
liveTokenHierarchyOperation.removeSnapshot(this);
440             }
441         }
442
443     }
444
445 }
446
Popular Tags