Lines 29-44
Link Here
|
29 |
import org.netbeans.lib.lexer.token.AbstractToken; |
29 |
import org.netbeans.lib.lexer.token.AbstractToken; |
30 |
|
30 |
|
31 |
/** |
31 |
/** |
32 |
* Token sequence allows to move between tokens |
32 |
* Token sequence allows to iterate between tokens |
33 |
* of a token hierarchy in forward/backward direction |
33 |
* of a token hierarchy. |
34 |
* and by index/offset positioning. |
|
|
35 |
* <br/> |
34 |
* <br/> |
36 |
* It may be obtained by {@link TokenHierarchy#tokenSequence()}. |
35 |
* Token sequence for top-level language of a token hierarchy |
37 |
* <br/> |
36 |
* may be obtained by {@link TokenHierarchy#tokenSequence()}. |
38 |
* A typical use is a forward iteration through the tokens: |
37 |
* |
|
|
38 |
* <p> |
39 |
* Use of token sequence is a two-step operation: |
40 |
* <ol> |
41 |
* <li> |
42 |
* Position token sequence before token that should first be retrieved |
43 |
* (or behind desired token when iterating backwards). |
44 |
* <br/> |
45 |
* One of the following ways may be used: |
46 |
* <ul> |
47 |
* <li> {@link #move(int)} positions TS before token that either starts |
48 |
* at the given offset or "contains" it. |
49 |
* </li> |
50 |
* <li> {@link #moveIndex(int)} positions TS before n-th token in the underlying |
51 |
* token list. |
52 |
* </li> |
53 |
* <li> {@link #moveStart()} positions TS before the first token. </li> |
54 |
* <li> {@link #moveEnd()} positions TS behind the last token. </li> |
55 |
* <li> Do nothing - TS is positioned before the first token automatically by default. </li> |
56 |
* </ul> |
57 |
* Token sequence will always be positioned between tokens |
58 |
* when using one of the operations above |
59 |
* ({@link #token()} will return <code>null</code> to signal between-tokens location). |
60 |
* <br/> |
61 |
* </li> |
62 |
* |
63 |
* <li> |
64 |
* Start iterating through the tokens in forward/backward direction |
65 |
* by using {@link #moveNext()} or {@link #movePrevious()}. |
66 |
* <br/> |
67 |
* If <code>moveNext()</code> or <code>movePrevious()</code> returned |
68 |
* <code>true</code> then TS is positioned |
69 |
* over a concrete token retrievable by {@link #token()}. |
70 |
* <br/> |
71 |
* Its offset can be retrieved by {@link #offset()}. |
72 |
* </li> |
73 |
* </ol> |
74 |
* </p> |
75 |
* |
76 |
* <p> |
77 |
* An example of forward iteration through the tokens: |
39 |
* <pre> |
78 |
* <pre> |
40 |
* TokenSequence ts = tokenHierarchy.tokenSequence(); |
79 |
* TokenSequence ts = tokenHierarchy.tokenSequence(); |
41 |
* // Possible positioning by ts.move() |
80 |
* // Possible positioning by ts.move(offset) or ts.moveIndex(index) |
42 |
* while (ts.moveNext()) { |
81 |
* while (ts.moveNext()) { |
43 |
* Token t = ts.token(); |
82 |
* Token t = ts.token(); |
44 |
* if (t.id() == ...) { ... } |
83 |
* if (t.id() == ...) { ... } |
Lines 46-55
Link Here
|
46 |
* if (ts.offset() == ...) { ... } |
85 |
* if (ts.offset() == ...) { ... } |
47 |
* } |
86 |
* } |
48 |
* </pre> |
87 |
* </pre> |
49 |
* <br/> |
88 |
* </p> |
50 |
* Token sequence provides correct offset information |
|
|
51 |
* for the token to which the sequence is positioned |
52 |
* (some tokens may be flyweight and do not hold the offset by themselves). |
53 |
* |
89 |
* |
54 |
* <p> |
90 |
* <p> |
55 |
* This class should be used by a single thread only. |
91 |
* This class should be used by a single thread only. |
Lines 65-71
Link Here
|
65 |
|
101 |
|
66 |
private AbstractToken<T> token; // 16 bytes |
102 |
private AbstractToken<T> token; // 16 bytes |
67 |
|
103 |
|
68 |
private int tokenIndex = -1; // 20 bytes |
104 |
private int tokenIndex; // 20 bytes |
69 |
|
105 |
|
70 |
/** |
106 |
/** |
71 |
* Offset in the input at which the current token is located |
107 |
* Offset in the input at which the current token is located |
Lines 113-131
Link Here
|
113 |
} |
149 |
} |
114 |
|
150 |
|
115 |
/** |
151 |
/** |
116 |
* Get instance of current token to which this token sequence points to. |
152 |
* Get token to which this token sequence points to or null |
|
|
153 |
* if TS is positioned between tokens |
154 |
* ({@link #moveNext()} or {@link #movePrevious()} were not called yet). |
117 |
* <br/> |
155 |
* <br/> |
118 |
* It is necessary to call {@link #moveNext()} before first calling this method. |
156 |
* A typical iteration usage: |
|
|
157 |
* <pre> |
158 |
* TokenSequence ts = tokenHierarchy.tokenSequence(); |
159 |
* // Possible positioning by ts.move(offset) or ts.moveIndex(index) |
160 |
* while (ts.moveNext()) { |
161 |
* Token t = ts.token(); |
162 |
* if (t.id() == ...) { ... } |
163 |
* if (TokenUtilities.equals(t.text(), "mytext")) { ... } |
164 |
* if (ts.offset() == ...) { ... } |
165 |
* } |
166 |
* </pre> |
119 |
* |
167 |
* |
120 |
* <p> |
|
|
121 |
* The returned token instance may be flyweight |
168 |
* The returned token instance may be flyweight |
122 |
* (returns true from {@link Token#isFlyweight()}) |
169 |
* ({@link Token#isFlyweight()} returns true) |
123 |
* which means that its {@link Token#offset(TokenHierarchy)} will return -1. |
170 |
* which means that its {@link Token#offset(TokenHierarchy)} will return -1. |
124 |
* <br/> |
171 |
* <br/> |
125 |
* To find a correct offset use {@link #offset()}. |
172 |
* To find a correct offset use {@link #offset()}. |
126 |
* <br/> |
173 |
* <br/> |
127 |
* Or if its necessary to have a non-flyweigt the {@link #offsetToken()} |
174 |
* Or if its necessary to revert to a regular non-flyweigt token |
128 |
* may be used. |
175 |
* the {@link #offsetToken()} may be used. |
129 |
* </p> |
176 |
* </p> |
130 |
* |
177 |
* |
131 |
* <p> |
178 |
* <p> |
Lines 133-145
Link Here
|
133 |
* The token instance should not be held across the input source modifications. |
180 |
* The token instance should not be held across the input source modifications. |
134 |
* </p> |
181 |
* </p> |
135 |
* |
182 |
* |
136 |
* @return non-null token instance. |
183 |
* @return token instance to which this token sequence is currently positioned |
|
|
184 |
* or null if this token sequence is not positioned to any token which may |
185 |
* happen after TS creation or after use of {@link #move(int)} or {@link moveIndex(int)}. |
186 |
* |
137 |
* @see #offsetToken() |
187 |
* @see #offsetToken() |
138 |
* @throws IllegalStateException if this token sequence was not positioned |
|
|
139 |
* to any token yet. |
140 |
*/ |
188 |
*/ |
141 |
public Token<T> token() { |
189 |
public Token<T> token() { |
142 |
checkToken(); |
|
|
143 |
return token; |
190 |
return token; |
144 |
} |
191 |
} |
145 |
|
192 |
|
Lines 149-154
Link Here
|
149 |
* <br/> |
196 |
* <br/> |
150 |
* If the current token is flyweight then this method replaces it |
197 |
* If the current token is flyweight then this method replaces it |
151 |
* with the corresponding non-flyweight token which it then returns. |
198 |
* with the corresponding non-flyweight token which it then returns. |
|
|
199 |
* <br/> |
200 |
* Subsequent calls to {@link #token()} will also return this non-flyweight token. |
152 |
* |
201 |
* |
153 |
* <p> |
202 |
* <p> |
154 |
* This method may be handy if the token instance is referenced in a standalone way |
203 |
* This method may be handy if the token instance is referenced in a standalone way |
Lines 156-166
Link Here
|
156 |
* to get the appropriate offset from the token itself |
205 |
* to get the appropriate offset from the token itself |
157 |
* later when a token sequence will not be available. |
206 |
* later when a token sequence will not be available. |
158 |
* </p> |
207 |
* </p> |
159 |
* @throws IllegalStateException if this token sequence was not positioned |
208 |
* @throws IllegalStateException if {@link #token()} returns null. |
160 |
* to any token yet. |
|
|
161 |
*/ |
209 |
*/ |
162 |
public Token<T> offsetToken() { |
210 |
public Token<T> offsetToken() { |
163 |
checkToken(); |
211 |
checkTokenNotNull(); |
164 |
if (token.isFlyweight()) { |
212 |
if (token.isFlyweight()) { |
165 |
token = tokenList.replaceFlyToken(tokenIndex, token, offset()); |
213 |
token = tokenList.replaceFlyToken(tokenIndex, token, offset()); |
166 |
} |
214 |
} |
Lines 179-189
Link Here
|
179 |
* best performance with a constant time complexity. |
227 |
* best performance with a constant time complexity. |
180 |
* |
228 |
* |
181 |
* @return >=0 absolute offset of the current token in the underlying input. |
229 |
* @return >=0 absolute offset of the current token in the underlying input. |
182 |
* @throws IllegalStateException if this token sequence was not positioned |
230 |
* @throws IllegalStateException if {@link #token()} returns null. |
183 |
* to any token yet. |
|
|
184 |
*/ |
231 |
*/ |
185 |
public int offset() { |
232 |
public int offset() { |
186 |
checkToken(); |
233 |
checkTokenNotNull(); |
187 |
if (tokenOffset == -1) { |
234 |
if (tokenOffset == -1) { |
188 |
tokenOffset = tokenList.tokenOffset(tokenIndex); |
235 |
tokenOffset = tokenList.tokenOffset(tokenIndex); |
189 |
} |
236 |
} |
Lines 191-200
Link Here
|
191 |
} |
238 |
} |
192 |
|
239 |
|
193 |
/** |
240 |
/** |
194 |
* Get the index of the current token in the complete list of tokens. |
241 |
* Get an index of token to which (or before which) this TS is currently positioned. |
195 |
* |
242 |
* <br/> |
196 |
* @return >=0 index of the current token or <code>-1</code> |
243 |
* <p> |
197 |
* if this token sequence is initially located in front of the first token. |
244 |
* Initially or after {@link #move(int)} or {@link #moveIndex(int)} |
|
|
245 |
* token sequence is positioned between tokens: |
246 |
* <pre> |
247 |
* Token[0] Token[1] ... Token[n] |
248 |
* ^ ^ ^ |
249 |
* Index: 0 1 n |
250 |
* </pre> |
251 |
* </p> |
252 |
* |
253 |
* <p> |
254 |
* After use of {@link #moveNext()} or {@link #movePrevious()} |
255 |
* the token sequence is positioned over one of the actual tokens: |
256 |
* <pre> |
257 |
* Token[0] Token[1] ... Token[n] |
258 |
* ^ ^ ^ |
259 |
* Index: 0 1 n |
260 |
* </pre> |
261 |
* </p> |
262 |
* |
263 |
* @return >=0 index of token to which (or before which) this TS is currently positioned. |
198 |
*/ |
264 |
*/ |
199 |
public int index() { |
265 |
public int index() { |
200 |
return tokenIndex; |
266 |
return tokenIndex; |
Lines 212-222
Link Here
|
212 |
* or <code>LanguageProvider</code>). |
278 |
* or <code>LanguageProvider</code>). |
213 |
* |
279 |
* |
214 |
* @return embedded sequence or null if no embedding exists for this token. |
280 |
* @return embedded sequence or null if no embedding exists for this token. |
215 |
* @throws IllegalStateException if this token sequence was not positioned |
281 |
* @throws IllegalStateException if {@link #token()} returns null. |
216 |
* to any token yet. |
|
|
217 |
*/ |
282 |
*/ |
218 |
public TokenSequence<? extends TokenId> embedded() { |
283 |
public TokenSequence<? extends TokenId> embedded() { |
219 |
checkToken(); |
284 |
checkTokenNotNull(); |
220 |
return embeddedImpl(null); |
285 |
return embeddedImpl(null); |
221 |
} |
286 |
} |
222 |
|
287 |
|
Lines 247-261
Link Here
|
247 |
* Get embedded token sequence if the token |
312 |
* Get embedded token sequence if the token |
248 |
* to which this token sequence is currently positioned |
313 |
* to which this token sequence is currently positioned |
249 |
* has a language embedding. |
314 |
* has a language embedding. |
|
|
315 |
* |
316 |
* @throws IllegalStateException if {@link #token()} returns null. |
250 |
*/ |
317 |
*/ |
251 |
public <ET extends TokenId> TokenSequence<ET> embedded(Language<ET> embeddedLanguage) { |
318 |
public <ET extends TokenId> TokenSequence<ET> embedded(Language<ET> embeddedLanguage) { |
252 |
checkToken(); |
319 |
checkTokenNotNull(); |
253 |
return embeddedImpl(embeddedLanguage); |
320 |
return embeddedImpl(embeddedLanguage); |
254 |
} |
321 |
} |
255 |
|
322 |
|
256 |
/** |
323 |
/** |
257 |
* Create language embedding without joining of the embedded sections. |
324 |
* Create language embedding without joining of the embedded sections. |
258 |
* |
325 |
* |
|
|
326 |
* @throws IllegalStateException if {@link #token()} returns null. |
259 |
* @see #createEmbedding(Language, int, int, boolean) |
327 |
* @see #createEmbedding(Language, int, int, boolean) |
260 |
*/ |
328 |
*/ |
261 |
public boolean createEmbedding(Language<? extends TokenId> embeddedLanguage, |
329 |
public boolean createEmbedding(Language<? extends TokenId> embeddedLanguage, |
Lines 291-349
Link Here
|
291 |
* Only the embedded sections with the same language path can be joined. |
359 |
* Only the embedded sections with the same language path can be joined. |
292 |
* @return true if the embedding was created successfully or false if an embedding |
360 |
* @return true if the embedding was created successfully or false if an embedding |
293 |
* with the given language already exists for this token. |
361 |
* with the given language already exists for this token. |
|
|
362 |
* @throws IllegalStateException if {@link #token()} returns null. |
294 |
*/ |
363 |
*/ |
295 |
public boolean createEmbedding(Language<? extends TokenId> embeddedLanguage, |
364 |
public boolean createEmbedding(Language<? extends TokenId> embeddedLanguage, |
296 |
int startSkipLength, int endSkipLength, boolean joinSections) { |
365 |
int startSkipLength, int endSkipLength, boolean joinSections) { |
297 |
checkToken(); |
366 |
checkTokenNotNull(); |
298 |
return EmbeddingContainer.createEmbedding(tokenList, tokenIndex, |
367 |
return EmbeddingContainer.createEmbedding(tokenList, tokenIndex, |
299 |
embeddedLanguage, startSkipLength, endSkipLength, joinSections); |
368 |
embeddedLanguage, startSkipLength, endSkipLength, joinSections); |
300 |
} |
369 |
} |
301 |
|
370 |
|
302 |
/** |
371 |
/** |
303 |
* Move to the next token in this token sequence. |
372 |
* Move to the next token in this token sequence. |
304 |
* <br/> |
373 |
* |
|
|
374 |
* <p> |
305 |
* The next token may not necessarily start at the offset where |
375 |
* The next token may not necessarily start at the offset where |
306 |
* the current token ends (there may be gaps between tokens |
376 |
* the previous token ends (there may be gaps between tokens |
307 |
* caused by use of a token id filter). |
377 |
* caused by token filtering). {@link #offset()} should be used |
|
|
378 |
* for offset retrieval. |
379 |
* </p> |
308 |
* |
380 |
* |
309 |
* @return true if the sequence was successfully moved to the next token |
381 |
* @return true if the sequence was successfully moved to the next token |
310 |
* or false if stays on the original token because there are no more tokens |
382 |
* or false if it was not moved before there are no more tokens |
311 |
* in the forward direction. |
383 |
* in the forward direction. |
312 |
* @throws ConcurrentModificationException if this token sequence |
384 |
* @throws ConcurrentModificationException if this token sequence |
313 |
* is no longer valid because of an underlying mutable input source modification. |
385 |
* is no longer valid because of an underlying mutable input source modification. |
314 |
*/ |
386 |
*/ |
315 |
public boolean moveNext() { |
387 |
public boolean moveNext() { |
316 |
checkModCount(); |
388 |
checkModCount(); |
317 |
tokenIndex++; |
389 |
if (token != null) // Token already fetched |
|
|
390 |
tokenIndex++; |
318 |
Object tokenOrEmbeddingContainer = tokenList.tokenOrEmbeddingContainer(tokenIndex); |
391 |
Object tokenOrEmbeddingContainer = tokenList.tokenOrEmbeddingContainer(tokenIndex); |
319 |
if (tokenOrEmbeddingContainer != null) { |
392 |
if (tokenOrEmbeddingContainer != null) { |
320 |
AbstractToken origToken = token; |
393 |
AbstractToken origToken = token; |
321 |
assignToken(tokenOrEmbeddingContainer); |
394 |
token = LexerUtilsConstants.token(tokenOrEmbeddingContainer); |
|
|
395 |
// If origToken == null then the right offset might already be pre-computed from move() |
322 |
if (tokenOffset != -1) { |
396 |
if (tokenOffset != -1) { |
323 |
// If the token list is continuous or the fetched token |
397 |
if (origToken != null) { |
324 |
// is flyweight (there cannot be a gap before flyweight token) |
398 |
// If the token list is continuous or the fetched token |
325 |
// the original offset can be just increased |
399 |
// is flyweight (there cannot be a gap before flyweight token) |
326 |
// by the original token's length. |
400 |
// the original offset can be just increased |
327 |
if (tokenList.isContinuous() || token.isFlyweight()) { |
401 |
// by the original token's length. |
328 |
tokenOffset += origToken.length(); // advance by previous token's length |
402 |
if (tokenList.isContinuous() || token.isFlyweight()) { |
329 |
} else // Offset must be recomputed |
403 |
tokenOffset += origToken.length(); // advance by previous token's length |
330 |
tokenOffset = -1; // mark the offset to be recomputed |
404 |
} else // Offset must be recomputed |
|
|
405 |
tokenOffset = -1; // mark the offset to be recomputed |
406 |
} else // Not valid token previously |
407 |
tokenOffset = -1; |
331 |
} |
408 |
} |
332 |
return true; |
409 |
return true; |
333 |
} |
410 |
} |
334 |
tokenIndex--; |
411 |
if (token != null) // Unsuccessful move from existing token |
|
|
412 |
tokenIndex--; |
335 |
return false; |
413 |
return false; |
336 |
} |
414 |
} |
337 |
|
415 |
|
338 |
/** |
416 |
/** |
339 |
* Move to the previous token in this token sequence. |
417 |
* Move to a previous token in this token sequence. |
340 |
* <br/> |
418 |
* |
341 |
* The next token may not necessarily end at the offset where |
419 |
* <p> |
342 |
* the present token starts (there may be gaps between tokens |
420 |
* The previous token may not necessarily end at the offset where |
343 |
* caused by use of a token id filter). |
421 |
* the previous token started (there may be gaps between tokens |
|
|
422 |
* caused by token filtering). {@link #offset()} should be used |
423 |
* for offset retrieval. |
424 |
* </p> |
344 |
* |
425 |
* |
345 |
* @return true if the sequence was successfully moved to the previous token |
426 |
* @return true if the sequence was successfully moved to the previous token |
346 |
* or false if stayed on the original token because there are no more tokens |
427 |
* or false if it was not moved because there are no more tokens |
347 |
* in the backward direction. |
428 |
* in the backward direction. |
348 |
* @throws ConcurrentModificationException if this token sequence |
429 |
* @throws ConcurrentModificationException if this token sequence |
349 |
* is no longer valid because of an underlying mutable input source modification. |
430 |
* is no longer valid because of an underlying mutable input source modification. |
Lines 353-359
Link Here
|
353 |
if (tokenIndex > 0) { |
434 |
if (tokenIndex > 0) { |
354 |
AbstractToken origToken = token; |
435 |
AbstractToken origToken = token; |
355 |
tokenIndex--; |
436 |
tokenIndex--; |
356 |
assignToken(); |
437 |
token = LexerUtilsConstants.token(tokenList.tokenOrEmbeddingContainer(tokenIndex)); |
357 |
if (tokenOffset != -1) { |
438 |
if (tokenOffset != -1) { |
358 |
// If the token list is continuous or the original token |
439 |
// If the token list is continuous or the original token |
359 |
// is flyweight (there cannot be a gap before flyweight token) |
440 |
// is flyweight (there cannot be a gap before flyweight token) |
Lines 364-371
Link Here
|
364 |
} else { // mark the offset to be computed upon call to offset() |
445 |
} else { // mark the offset to be computed upon call to offset() |
365 |
tokenOffset = -1; |
446 |
tokenOffset = -1; |
366 |
} |
447 |
} |
367 |
} else { |
|
|
368 |
tokenOffset = -1; // mark the offset to be computed upon call to offset() |
369 |
} |
448 |
} |
370 |
return true; |
449 |
return true; |
371 |
|
450 |
|
Lines 374-465
Link Here
|
374 |
} |
453 |
} |
375 |
|
454 |
|
376 |
/** |
455 |
/** |
377 |
* Move the token sequence to point to the token with the given index. |
456 |
* Position token sequence between <code>index-1</code> |
|
|
457 |
* and <code>index</code> tokens. |
458 |
* <br/> |
459 |
* TS will be positioned in the following way: |
460 |
* <pre> |
461 |
* Token[0] ... Token[index-1] Token[index] ... |
462 |
* ^ ^ ^ |
463 |
* Index: 0 index-1 index |
464 |
* </pre> |
465 |
* |
466 |
* <p> |
467 |
* Subsequent {@link #moveNext()} or {@link #movePrevious()} is needed to fetch |
468 |
* a concrete token in the desired direction. |
469 |
* <br/> |
470 |
* Subsequent {@link #moveNext()} will position TS over <code>Token[index]</code> |
471 |
* (or {@link #movePrevious()} will position TS over <code>Token[index-1]</code>) |
472 |
* so that <code>{@link #token()} != null</code>. |
378 |
* |
473 |
* |
379 |
* @param index index of the token to which this sequence |
474 |
* @param index index of the token to which this sequence |
380 |
* should be positioned. |
475 |
* should be positioned. |
381 |
* @return <code>true</code> if the sequence was moved to the token |
476 |
* <br/> |
382 |
* with the given index. Returns <code>false</code> |
477 |
* If <code>index >= {@link #tokenCount()}</code> |
383 |
* if <code>index < 0</code> or <code>index < tokenCount</code>. |
478 |
* then the TS will be positioned to {@link #tokenCount()}. |
384 |
* In such case the current token sequence's position stays unchanged. |
479 |
* <br/> |
|
|
480 |
* If <code>index < 0</code> then the TS will be positioned to index 0. |
481 |
* |
482 |
* @return difference between requested index and the index to which TS |
483 |
* is really set. |
385 |
* @throws ConcurrentModificationException if this token sequence |
484 |
* @throws ConcurrentModificationException if this token sequence |
386 |
* is no longer valid because of an underlying mutable input source modification. |
485 |
* is no longer valid because of an underlying mutable input source modification. |
387 |
*/ |
486 |
*/ |
388 |
public boolean moveIndex(int index) { |
487 |
public int moveIndex(int index) { |
389 |
checkModCount(); |
488 |
checkModCount(); |
390 |
if (index < 0) { |
489 |
if (index >= 0) { |
391 |
return false; |
490 |
Object tokenOrEmbeddingContainer = tokenList.tokenOrEmbeddingContainer(index); |
392 |
} |
491 |
if (tokenOrEmbeddingContainer != null) { // enough tokens |
393 |
Object tokenOrEmbeddingContainer = tokenList.tokenOrEmbeddingContainer(index); |
492 |
resetTokenIndex(index); |
394 |
if (tokenOrEmbeddingContainer != null) { // enough tokens |
493 |
} else // Token at the requested index does not exist - leave orig. index |
395 |
this.tokenIndex = index; |
494 |
resetTokenIndex(tokenCount()); |
396 |
assignToken(tokenOrEmbeddingContainer); |
495 |
} else // index < 0 |
397 |
tokenOffset = -1; |
496 |
resetTokenIndex(0); |
398 |
return true; |
497 |
return index - tokenIndex; |
399 |
|
|
|
400 |
} else // Token at the requested index does not exist - leave orig. index |
401 |
return false; |
402 |
} |
498 |
} |
403 |
|
499 |
|
404 |
/** |
500 |
/** |
405 |
* Move the token sequence to be positioned to the token |
501 |
* Move the token sequence to be positioned before the first token. |
406 |
* that "contains" the requested offset (the offset is at the begining |
502 |
* <br/> |
407 |
* or inside of the token). |
503 |
* This is equivalent to <code>moveIndex(0)</code>. |
408 |
* <br> |
504 |
*/ |
409 |
* If the offset is too big the token sequence will be positioned |
505 |
public void moveStart() { |
410 |
* to the last token and the return value will |
506 |
moveIndex(0); |
411 |
* be the distance between the requested offset |
507 |
} |
412 |
* and the start offset of the token to which the token sequence |
508 |
|
413 |
* will be positioned.. |
509 |
/** |
414 |
* <br> |
510 |
* Move the token sequence to be positioned behind the last token. |
415 |
* If there are no tokens in the sequence then {@link Integer#MAX_VALUE} |
511 |
* <br/> |
416 |
* will be returned. |
512 |
* This is equivalent to <code>moveIndex(tokenCount())</code>. |
417 |
* |
513 |
*/ |
418 |
* <p> |
514 |
public void moveEnd() { |
419 |
* The underlying token list may contain gaps that are not covered |
515 |
moveIndex(tokenCount()); |
420 |
* by any tokens and if the offset is contained in such gap then |
516 |
} |
421 |
* the token sequence will be positioned to the token that precedes the gap. |
517 |
|
422 |
* </p> |
518 |
/** |
423 |
* |
519 |
* Move token sequence to be positioned between <code>index-1</code> |
424 |
* Example: |
520 |
* and <code>index</code> tokens where Token[index] either starts at offset |
|
|
521 |
* or "contains" the offset. |
522 |
* <br/> |
425 |
* <pre> |
523 |
* <pre> |
426 |
* int diff = tokenSequence.move(targetOffset); |
524 |
* +----------+-----+----------------+--------------+------ |
427 |
* // diff equals to (targetOffset - tokenSequence.token().offset()) |
525 |
* | Token[0] | ... | Token[index-1] | Token[index] | ... |
428 |
* if (diff >= 0 && diff < tokenSequence.token().length()) { |
526 |
* | "public" | ... | "static" | "int" | ... |
429 |
* // Within token bounds - tokenSequence.token() starts at or "contains" targetOffset |
527 |
* +----------+-----+----------------+--------------+------ |
430 |
* |
528 |
* ^ ^ ^ |
431 |
* } else if (diff == Integer.MAX_VALUE) { |
529 |
* Index: 0 index-1 index |
432 |
* // No tokens in the token sequence at all. |
530 |
* Offset: ---^ (if offset points to 'i','n' or 't') |
433 |
* // Token sequence is not positioned to any token. |
|
|
434 |
* |
435 |
* } else { |
436 |
* // 1. diff >= tokenSequence.token().length() |
437 |
* // a) targetOffset is above the end of the last token in the sequence. |
438 |
* // Token sequence is positioned to the last token in the sequence. |
439 |
* // b) there are text areas not covered by any tokens |
440 |
* // due to skipped tokens (skipTokenIds was used |
441 |
* // in TokenHierarchy.create()) and the targetOffset points to such gap. |
442 |
* // Token sequence is positioned to the preceding token. |
443 |
* // |
444 |
* // 2. diff < 0 |
445 |
* // a) targetOffset < 0 |
446 |
* // b) targetOffset >= 0 but there is a text area |
447 |
* // at the begining that is not covered by any tokens |
448 |
* // (skipTokenIds was used in TokenHierarchy.create()) |
449 |
* // Token sequence is positioned to the first token in the sequence. |
450 |
* } |
451 |
* </pre> |
531 |
* </pre> |
452 |
* |
532 |
* |
|
|
533 |
* <p> |
534 |
* Subsequent {@link #moveNext()} or {@link #movePrevious()} is needed to fetch |
535 |
* a concrete token. |
536 |
* <br/> |
537 |
* If the offset is too big then the token sequence will be positioned |
538 |
* behind the last token. |
539 |
* </p> |
540 |
* |
541 |
* <p> |
542 |
* If token filtering is used there may be gaps that are not covered |
543 |
* by any tokens and if the offset is contained in such gap then |
544 |
* the token sequence will be positioned before the token that follows the gap. |
545 |
* </p> |
453 |
* |
546 |
* |
454 |
* @param offset absolute offset in the input to which |
|
|
455 |
* the token sequence should be moved. |
456 |
* |
547 |
* |
|
|
548 |
* @param offset absolute offset to which the token sequence should be moved. |
457 |
* @return difference between the reqeuested offset |
549 |
* @return difference between the reqeuested offset |
458 |
* and the absolute starting offset of the token |
550 |
* and the start offset of the token |
459 |
* to which the the token sequence gets moved. |
551 |
* before which the the token sequence gets positioned. |
460 |
* <br> |
|
|
461 |
* Returns {@link Integer#MAX_VALUE} if there are no tokens in the sequence. |
462 |
* In such case there is no active token. |
463 |
* |
552 |
* |
464 |
* @throws ConcurrentModificationException if this token sequence |
553 |
* @throws ConcurrentModificationException if this token sequence |
465 |
* is no longer valid because of an underlying mutable input source modification. |
554 |
* is no longer valid because of an underlying mutable input source modification. |
Lines 474-480
Link Here
|
474 |
if (tokenList.tokenOrEmbeddingContainer(0) == null) { // really no tokens at all |
563 |
if (tokenList.tokenOrEmbeddingContainer(0) == null) { // really no tokens at all |
475 |
// In this case the token sequence could not be positioned yet |
564 |
// In this case the token sequence could not be positioned yet |
476 |
// so no need to reset "index" or other vars |
565 |
// so no need to reset "index" or other vars |
477 |
return Integer.MAX_VALUE; |
566 |
resetTokenIndex(0); |
|
|
567 |
return offset; |
478 |
} |
568 |
} |
479 |
// Re-get the present token count (could be created a chunk of tokens at once) |
569 |
// Re-get the present token count (could be created a chunk of tokens at once) |
480 |
tokenCount = tokenList.tokenCountCurrent(); |
570 |
tokenCount = tokenList.tokenCountCurrent(); |
Lines 499-512
Link Here
|
499 |
tokenLength = t.length(); |
589 |
tokenLength = t.length(); |
500 |
tokenCount++; |
590 |
tokenCount++; |
501 |
|
591 |
|
502 |
} else { // no more tokens => break |
592 |
} else { // no more tokens => position behind last token |
503 |
break; |
593 |
resetTokenIndex(tokenCount); |
|
|
594 |
tokenOffset = prevTokenOffset + tokenLength; // May assign the token's offset in advance |
595 |
return offset - tokenOffset; |
504 |
} |
596 |
} |
505 |
} |
597 |
} |
506 |
tokenIndex = tokenCount - 1; |
598 |
resetTokenIndex(tokenCount - 1); |
507 |
// Absolute token's start offset |
599 |
tokenOffset = prevTokenOffset; // May assign the token's offset in advance |
508 |
tokenOffset = prevTokenOffset; |
|
|
509 |
assignToken(); |
510 |
return offset - prevTokenOffset; |
600 |
return offset - prevTokenOffset; |
511 |
} |
601 |
} |
512 |
|
602 |
|
Lines 525-571
Link Here
|
525 |
high = mid - 1; |
615 |
high = mid - 1; |
526 |
} else { |
616 |
} else { |
527 |
// Token starting exactly at offset found |
617 |
// Token starting exactly at offset found |
528 |
tokenIndex = mid; |
618 |
resetTokenIndex(mid); |
529 |
tokenOffset = midStartOffset; |
619 |
tokenOffset = midStartOffset; |
530 |
assignToken(); |
|
|
531 |
return 0; // right at the token begining |
620 |
return 0; // right at the token begining |
532 |
} |
621 |
} |
533 |
} |
622 |
} |
534 |
|
623 |
|
535 |
// Not found exactly and high + 1 == low => high < low |
624 |
// Not found exactly and high + 1 == low => high < low |
536 |
// Check whether the token at "high" contains the offset |
625 |
// BTW there may be gaps between tokens; if offset is in gap then position to higher token |
537 |
if (high >= 0) { // could be -1 |
626 |
if (high >= 0) { // could be -1 |
538 |
AbstractToken t = LexerUtilsConstants.token(tokenList, high); |
627 |
AbstractToken t = LexerUtilsConstants.token(tokenList, high); |
539 |
prevTokenOffset = tokenList.tokenOffset(high); |
628 |
prevTokenOffset = tokenList.tokenOffset(high); |
|
|
629 |
// If gaps allowed check whether the token at "high" contains the offset |
630 |
if (!tokenList.isContinuous() && offset > prevTokenOffset + t.length()) { |
631 |
// Offset in the gap above the "high" token |
632 |
high++; |
633 |
prevTokenOffset += t.length(); |
634 |
} |
540 |
} else { // at least one token exists => use token at index 0 |
635 |
} else { // at least one token exists => use token at index 0 |
541 |
high = 0; |
636 |
high = 0; |
542 |
prevTokenOffset = tokenList.tokenOffset(0); // result may differ from 0 |
637 |
prevTokenOffset = tokenList.tokenOffset(0); // result may differ from 0 |
543 |
} |
638 |
} |
544 |
|
639 |
resetTokenIndex(high); |
545 |
tokenIndex = high; |
|
|
546 |
tokenOffset = prevTokenOffset; |
640 |
tokenOffset = prevTokenOffset; |
547 |
assignToken(); |
|
|
548 |
return offset - prevTokenOffset; |
641 |
return offset - prevTokenOffset; |
549 |
} |
642 |
} |
550 |
|
|
|
551 |
/** |
552 |
* Move to the first token in this token sequence. |
553 |
* |
554 |
* @return true if the sequence was positioned on the first token |
555 |
* or false if there are no tokens in the sequence. |
556 |
*/ |
557 |
public boolean moveFirst() { |
558 |
return moveIndex(0); |
559 |
} |
560 |
|
643 |
|
561 |
/** |
644 |
/** |
562 |
* Move to the last token in this token sequence. |
645 |
* Check whether this TS contains zero tokens. |
563 |
* |
646 |
* <br/> |
564 |
* @return true if the sequence was positioned on the last token |
647 |
* This check is strongly preferred over <code>tokenCount() == 0</code>. |
565 |
* or false if there are no tokens in the sequence. |
648 |
* |
|
|
649 |
* @see #tokenCount() |
566 |
*/ |
650 |
*/ |
567 |
public boolean moveLast() { |
651 |
public boolean isEmpty() { |
568 |
return moveIndex(tokenCount() - 1); // Can be -1 but handled in move(index) |
652 |
return (tokenIndex == 0 && tokenList.tokenOrEmbeddingContainer(0) == null); |
569 |
} |
653 |
} |
570 |
|
654 |
|
571 |
/** |
655 |
/** |
Lines 628-642
Link Here
|
628 |
return parentTokenIndexes; |
712 |
return parentTokenIndexes; |
629 |
} |
713 |
} |
630 |
|
714 |
|
631 |
private void assignToken(Object tokenOrEmbeddingContainer) { |
715 |
private void resetTokenIndex(int index) { |
632 |
token = LexerUtilsConstants.token(tokenOrEmbeddingContainer); |
716 |
// Position to the given index e.g. by move() and moveIndex() |
633 |
} |
717 |
tokenIndex = index; |
634 |
|
718 |
token = null; |
635 |
private void assignToken() { |
719 |
tokenOffset = -1; |
636 |
assignToken(tokenList.tokenOrEmbeddingContainer(tokenIndex)); |
|
|
637 |
} |
720 |
} |
638 |
|
721 |
|
639 |
private void checkToken() { |
722 |
private void checkTokenNotNull() { |
640 |
if (token == null) { |
723 |
if (token == null) { |
641 |
throw new IllegalStateException( |
724 |
throw new IllegalStateException( |
642 |
"No token fetched by moveNext() from token sequence yet: index=" + tokenIndex |
725 |
"No token fetched by moveNext() from token sequence yet: index=" + tokenIndex |