001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.lib.lexer;
043:
044: import org.netbeans.api.lexer.Language;
045: import org.netbeans.api.lexer.LanguagePath;
046: import org.netbeans.api.lexer.TokenHierarchyEventType;
047: import org.netbeans.api.lexer.TokenId;
048: import org.netbeans.lib.lexer.inc.TokenChangeInfo;
049: import org.netbeans.lib.lexer.inc.TokenHierarchyEventInfo;
050: import org.netbeans.spi.lexer.LanguageEmbedding;
051: import org.netbeans.lib.lexer.token.AbstractToken;
052: import org.netbeans.spi.lexer.EmbeddingPresence;
053: import org.netbeans.spi.lexer.LanguageHierarchy;
054:
055: /**
056: * Embedding info contains information about all the embeddings
057: * for a particular token in a token list.
058: * <br>
059: * There can be one or more {@link EmbeddedTokenList} instances for each
060: * cotnained embedding.
061: * <p>
062: * There is an intent to not degrade performance significantly
063: * with each extra language embedding level so the token list maintains direct
064: * link to the root level.
065: *
066: * @author Miloslav Metelka
067: * @version 1.00
068: */
069:
070: public final class EmbeddingContainer<T extends TokenId> {
071:
072: /**
073: * Get embedded token list.
074: *
075: * @param tokenList non-null token list in which the token for which the embedding
076: * should be obtained resides.
077: * @param index >=0 index of the token in the token list where the embedding
078: * should be obtained.
079: * @param language whether only language embeddding of the particular language
080: * was requested. It may be null if any embedding should be returned.
081: */
082: public static <T extends TokenId, ET extends TokenId> EmbeddedTokenList<ET> embeddedTokenList(
083: TokenList<T> tokenList, int index,
084: Language<ET> embeddedLanguage) {
085: EmbeddingContainer<T> ec;
086: AbstractToken<T> token;
087: EmbeddingPresence ep;
088: TokenList<?> rootTokenList = tokenList.root();
089: synchronized (rootTokenList) {
090: Object tokenOrEmbeddingContainer = tokenList
091: .tokenOrEmbeddingContainer(index);
092: if (tokenOrEmbeddingContainer.getClass() == EmbeddingContainer.class) {
093: // Embedding container exists
094: @SuppressWarnings("unchecked")
095: EmbeddingContainer<T> ecUC = (EmbeddingContainer<T>) tokenOrEmbeddingContainer;
096: ec = ecUC;
097: token = ec.token();
098: ep = null;
099:
100: } else { // No embedding was created yet
101: ec = null;
102: @SuppressWarnings("unchecked")
103: AbstractToken<T> t = (AbstractToken<T>) tokenOrEmbeddingContainer;
104: token = t;
105: // Check embedding presence
106: ep = LexerUtilsConstants.innerLanguageOperation(
107: tokenList.languagePath()).embeddingPresence(
108: token.id());
109: if (ep == EmbeddingPresence.NONE) {
110: return null;
111: }
112: }
113:
114: // Now either ec == null for no embedding yet or linked list of embedded token lists of ec
115: // need to be processed to find the embedded token list for requested language.
116: EmbeddedTokenList<?> prevEtl;
117: if (ec != null) {
118: ec.updateStatusImpl();
119: EmbeddedTokenList<?> etl = ec.firstEmbeddedTokenList();
120: prevEtl = null;
121: while (etl != null) {
122: if (embeddedLanguage == null
123: || etl.languagePath().innerLanguage() == embeddedLanguage) {
124: @SuppressWarnings("unchecked")
125: EmbeddedTokenList<ET> etlUC = (EmbeddedTokenList<ET>) etl;
126: return etlUC;
127: }
128: prevEtl = etl;
129: etl = etl.nextEmbeddedTokenList();
130: }
131: // Requested etl not found
132: if (ec.defaultEmbeddedTokenList() != null) { // Already created or NO_DEFAULT_EMBEDDING
133: return null;
134: }
135:
136: } else { // No embedding yet
137: prevEtl = null;
138: }
139:
140: // If the tokenList is removed then do not create the embedding
141: if (tokenList.isRemoved())
142: return null;
143:
144: // Attempt to create the default embedding
145: LanguagePath languagePath = tokenList.languagePath();
146: LanguageHierarchy<T> languageHierarchy = LexerUtilsConstants
147: .innerLanguageHierarchy(languagePath);
148: @SuppressWarnings("unchecked")
149: LanguageEmbedding<ET> embedding = (LanguageEmbedding<ET>) LexerUtilsConstants
150: .findEmbedding(languageHierarchy, token,
151: languagePath, tokenList.inputAttributes());
152: if (ep == null) { // Needs to be retrieved to check for CACHED_FIRST_QUERY
153: ep = LexerUtilsConstants.innerLanguageOperation(
154: tokenList.languagePath()).embeddingPresence(
155: token.id());
156: }
157: if (embedding != null) {
158: // Update the embedding presence ALWAYS_QUERY
159: if (ep == EmbeddingPresence.CACHED_FIRST_QUERY) {
160: LexerUtilsConstants.innerLanguageOperation(
161: tokenList.languagePath())
162: .setEmbeddingPresence(token.id(),
163: EmbeddingPresence.ALWAYS_QUERY);
164: }
165: // Check whether the token contains enough text to satisfy embedding's start and end skip lengths
166: CharSequence text = token.text(); // Should not be null here but rather check
167: if (text == null
168: || embedding.startSkipLength()
169: + embedding.endSkipLength() > text
170: .length()) {
171: return null;
172: }
173: if (ec == null) {
174: ec = new EmbeddingContainer<T>(token, rootTokenList);
175: tokenList.wrapToken(index, ec);
176: }
177: LanguagePath embeddedLanguagePath = LanguagePath.get(
178: languagePath, embedding.language());
179: EmbeddedTokenList<ET> etl = new EmbeddedTokenList<ET>(
180: ec, embeddedLanguagePath, embedding, null);
181: // Preceding code should ensure that (prevEtl.nextEmbeddedTokenList == null)
182: // so no need to call etl.setNextEmbeddedTokenList(prevEtl.nextEmbeddedTokenList())
183: ec.addEmbeddedTokenList(prevEtl, etl, true);
184: return (embeddedLanguage == null || embeddedLanguage == embedding
185: .language()) ? etl : null;
186: }
187: // Update embedding presence to NONE
188: if (ep == EmbeddingPresence.CACHED_FIRST_QUERY) {
189: LexerUtilsConstants.innerLanguageOperation(
190: tokenList.languagePath()).setEmbeddingPresence(
191: token.id(), EmbeddingPresence.NONE);
192: }
193: return null;
194: }
195: }
196:
197: /**
198: * Create custom embedding.
199: *
200: * @param tokenList non-null token list in which the token for which the embedding
201: * should be created resides.
202: * @param index >=0 index of the token in the token list where the embedding
203: * should be created.
204: * @param embeddedLanguage non-null embedded language.
205: * @param startSkipLength >=0 number of characters in an initial part of the token
206: * for which the language embedding is being create that should be excluded
207: * from the embedded section. The excluded characters will not be lexed
208: * and there will be no tokens created for them.
209: * @param endSkipLength >=0 number of characters at the end of the token
210: * for which the language embedding is defined that should be excluded
211: * from the embedded section. The excluded characters will not be lexed
212: * and there will be no tokens created for them.
213: */
214: public static <T extends TokenId, ET extends TokenId> boolean createEmbedding(
215: TokenList<T> tokenList, int index,
216: Language<ET> embeddedLanguage, int startSkipLength,
217: int endSkipLength, boolean joinSections) {
218: TokenList<?> rootTokenList = tokenList.root();
219: // Only create embedddings for valid operations so not e.g. for removed token list
220: AbstractToken<T> token;
221: EmbeddingContainer<T> ec;
222: EmbeddedTokenList<ET> etl;
223: LanguageEmbedding<ET> embedding;
224: TokenHierarchyOperation<?, ?> tokenHierarchyOperation;
225: int tokenStartOffset;
226: TokenHierarchyEventInfo eventInfo;
227: synchronized (rootTokenList) {
228: // Check TL.isRemoved() under syncing of rootTokenList
229: if (tokenList.isRemoved()) // Do not create embedding for removed TLs
230: return false;
231: // If not removed then THO should be non-null
232: tokenHierarchyOperation = tokenList
233: .tokenHierarchyOperation();
234: tokenHierarchyOperation.ensureWriteLocked();
235:
236: Object tokenOrEmbeddingContainer = tokenList
237: .tokenOrEmbeddingContainer(index);
238: if (tokenOrEmbeddingContainer.getClass() == EmbeddingContainer.class) {
239: // Embedding container exists
240: @SuppressWarnings("unchecked")
241: EmbeddingContainer<T> ecUC = (EmbeddingContainer<T>) tokenOrEmbeddingContainer;
242: ec = ecUC;
243: EmbeddedTokenList etl2 = ec.firstEmbeddedTokenList();
244: while (etl2 != null) {
245: if (embeddedLanguage == etl2.languagePath()
246: .innerLanguage()) {
247: return false; // already exists
248: }
249: etl2 = etl2.nextEmbeddedTokenList();
250: }
251: token = ec.token();
252: } else {
253: @SuppressWarnings("unchecked")
254: AbstractToken<T> t = (AbstractToken<T>) tokenOrEmbeddingContainer;
255: token = t;
256: if (token.isFlyweight()) { // embedding cannot exist for this flyweight token
257: return false;
258: }
259: ec = new EmbeddingContainer<T>(token, rootTokenList);
260: tokenList.wrapToken(index, ec);
261: }
262:
263: // Token is now wrapped with the EmbeddingContainer and the embedding can be added
264: if (startSkipLength + endSkipLength > token.length()) // Check for appropriate size
265: return false;
266: // Add the new embedding as the first one in the single-linked list
267: embedding = LanguageEmbedding.create(embeddedLanguage,
268: startSkipLength, endSkipLength, joinSections);
269: LanguagePath languagePath = tokenList.languagePath();
270: LanguagePath embeddedLanguagePath = LanguagePath.get(
271: languagePath, embeddedLanguage);
272: tokenHierarchyOperation
273: .addLanguagePath(embeddedLanguagePath);
274: // Make the embedded token list to be the first in the list
275: etl = new EmbeddedTokenList<ET>(ec, embeddedLanguagePath,
276: embedding, ec.firstEmbeddedTokenList());
277: ec.addEmbeddedTokenList(null, etl, false);
278:
279: // Fire the embedding creation to the clients
280: // Threading model may need to be changed if necessary
281: tokenStartOffset = ec.tokenStartOffset();
282: eventInfo = new TokenHierarchyEventInfo(
283: tokenHierarchyOperation,
284: TokenHierarchyEventType.EMBEDDING_CREATED,
285: tokenStartOffset, 0, "", 0);
286: eventInfo.setMaxAffectedEndOffset(tokenStartOffset
287: + token.length());
288:
289: // Check presence of token list list for the embedded language path
290: // When joining sections ensure that the token list list gets created
291: // and the embedded tokens get created because they must exist
292: // before possible next updating of the token list.
293: TokenListList tll = tokenHierarchyOperation
294: .existingTokenListList(etl.languagePath());
295: if (tll != null) {
296: // Update tll by embedding creation
297: new TokenHierarchyUpdate(eventInfo)
298: .updateCreateEmbedding(etl);
299: } else { // tll == null
300: if (embedding.joinSections()) {
301: // Force token list list creation only when joining sections
302: tll = tokenHierarchyOperation.tokenListList(etl
303: .languagePath());
304: }
305: }
306:
307: }
308:
309: // Construct outer token change info
310: TokenChangeInfo<T> info = new TokenChangeInfo<T>(tokenList);
311: info.setIndex(index);
312: info.setOffset(tokenStartOffset);
313: //info.setAddedTokenCount(0);
314: eventInfo.setTokenChangeInfo(info);
315:
316: TokenChangeInfo<ET> embeddedInfo = new TokenChangeInfo<ET>(etl);
317: embeddedInfo.setIndex(0);
318: embeddedInfo.setOffset(tokenStartOffset
319: + embedding.startSkipLength());
320: // Should set number of added tokens directly?
321: // - would prevent further lazy embedded lexing so leave to zero for now
322: //info.setAddedTokenCount(0);
323: info.addEmbeddedChange(embeddedInfo);
324:
325: // Fire the change
326: tokenHierarchyOperation.fireTokenHierarchyChanged(eventInfo);
327: return true;
328: }
329:
330: public static <T extends TokenId, ET extends TokenId> boolean removeEmbedding(
331: TokenList<T> tokenList, int index,
332: Language<ET> embeddedLanguage) {
333: TokenList<?> rootTokenList = tokenList.root();
334: // Only create embedddings for valid operations so not e.g. for removed token list
335: EmbeddingContainer<T> ec;
336: synchronized (rootTokenList) {
337: // Check TL.isRemoved() under syncing of rootTokenList
338: if (tokenList.isRemoved()) // Do not create embedding for removed TLs
339: return false;
340: // If TL is not removed then THO should be non-null
341: TokenHierarchyOperation<?, ?> tokenHierarchyOperation = tokenList
342: .tokenHierarchyOperation();
343: tokenHierarchyOperation.ensureWriteLocked();
344:
345: Object tokenOrEmbeddingContainer = tokenList
346: .tokenOrEmbeddingContainer(index);
347: if (tokenOrEmbeddingContainer.getClass() == EmbeddingContainer.class) {
348: // Embedding container exists
349: @SuppressWarnings("unchecked")
350: EmbeddingContainer<T> ecUC = (EmbeddingContainer<T>) tokenOrEmbeddingContainer;
351: ec = ecUC;
352: ec.updateStatusImpl();
353: EmbeddedTokenList<?> etl = ec.firstEmbeddedTokenList();
354: EmbeddedTokenList<?> prevEtl = null;
355: while (etl != null) {
356: if (embeddedLanguage == etl.languagePath()
357: .innerLanguage()) {
358: // The embedding with the given language exists
359: // Remove it from the chain
360: ec.removeEmbeddedTokenList(prevEtl, etl);
361: // Do not increase the version of the hierarchy since
362: // all the existing token sequences would be invalidated.
363: // Instead invalidate only TSes for the etl only and all its children.
364: // Construct special EC just for the removed token list.
365: ec = new EmbeddingContainer<T>(ec);
366: // State that the removed embedding was not default - should not matter anyway
367: ec.addEmbeddedTokenList(null, etl, false);
368: etl.setEmbeddingContainer(ec);
369: ec.invalidateChildren();
370:
371: // Fire the embedding creation to the clients
372: int startOffset = ec.tokenStartOffset();
373: TokenHierarchyEventInfo eventInfo = new TokenHierarchyEventInfo(
374: tokenHierarchyOperation,
375: TokenHierarchyEventType.EMBEDDING_REMOVED,
376: startOffset, 0, "", 0
377:
378: );
379: eventInfo.setMaxAffectedEndOffset(startOffset
380: + ec.token().length());
381: // Construct outer token change info
382: TokenChangeInfo<T> info = new TokenChangeInfo<T>(
383: tokenList);
384: info.setIndex(index);
385: info.setOffset(startOffset);
386: //info.setAddedTokenCount(0);
387: eventInfo.setTokenChangeInfo(info);
388:
389: @SuppressWarnings("unchecked")
390: EmbeddedTokenList<ET> etlET = (EmbeddedTokenList<ET>) etl;
391: TokenChangeInfo<ET> embeddedInfo = new TokenChangeInfo<ET>(
392: etlET);
393: embeddedInfo.setIndex(0);
394: embeddedInfo.setOffset(startOffset
395: + etl.embedding().startSkipLength());
396: // For now do not set the removed contents (requires RemovedTokenList)
397: info.addEmbeddedChange(embeddedInfo);
398:
399: // Check for presence of etl in a token list list
400: TokenListList tll = tokenHierarchyOperation
401: .existingTokenListList(etl
402: .languagePath());
403: if (tll != null) {
404: // update-status already called
405: new TokenHierarchyUpdate(eventInfo)
406: .updateRemoveEmbedding(etl);
407: }
408:
409: // Fire the change
410: tokenHierarchyOperation
411: .fireTokenHierarchyChanged(eventInfo);
412: return true;
413: }
414: etl = etl.nextEmbeddedTokenList();
415: }
416: }
417: }
418: return false;
419: }
420:
421: private AbstractToken<T> token; // 12 bytes (8-super + 4)
422:
423: /**
424: * Cached modification count allows to determine whether the start offset
425: * needs to be recomputed.
426: */
427: private int cachedModCount; // 16 bytes
428:
429: /**
430: * Root token list of the hierarchy.
431: *
432: */
433: private final TokenList<?> rootTokenList; // 20 bytes
434:
435: /**
436: * The root embedding container to which this embedding container relates.
437: * <br/>
438: * It's used for getting of the start offset of the contained tokens
439: * and for getting of their text.
440: */
441: private AbstractToken<?> rootToken; // 24 bytes
442:
443: /**
444: * Cached start offset of the token for which this embedding container
445: * was created.
446: */
447: private int tokenStartOffset; // 28 bytes
448:
449: /**
450: * First embedded token list in the single-linked list.
451: */
452: private EmbeddedTokenList<?> firstEmbeddedTokenList; // 32 bytes
453:
454: /**
455: * Difference between start offset of the first token in this token list
456: * against the start offset of the root token.
457: * <br>
458: * The offset gets refreshed upon <code>updateStartOffset()</code>.
459: */
460: private int offsetShiftFromRootToken; // 36 bytes
461:
462: /**
463: * Embedded token list that represents the default embedding.
464: * It may be <code>EmbeddedTokenList.NO_DEFAULT_EMBEDDING</code>
465: * for failed attempt to create a default embedding.
466: */
467: private EmbeddedTokenList<?> defaultEmbeddedTokenList; // 40 bytes
468:
469: EmbeddingContainer(AbstractToken<T> token,
470: TokenList<?> rootTokenList) {
471: this .token = token;
472: this .rootTokenList = rootTokenList;
473: this .rootToken = token; // Has to be non-null since updateStatusImpl() would not update null rootToken
474: // cachedModCount must differ from root's one to sync offsets
475: // Root mod count can be >= 0 or -1 for non-incremental token lists
476: this .cachedModCount = -2;
477: // Update the tokenStartOffset etc. - this assumes that the token
478: // is already parented till the root token list.
479: updateStatusImpl();
480: }
481:
482: /**
483: * Constructor used when a custom embedding gets removed.
484: * Such removal does not increase token hierarchy version
485: * (to not destroy existing token sequences) but need to invalidate
486: * token sequences over the removed embedded token list and all its children.
487: * <br/>
488: * A new special embedding container gets created in such case
489: * that will carry null root token since begining and will have a special modCount
490: * so that the token sequences become invalid.
491: *
492: * @param ec non-null existing embedding container.
493: */
494: EmbeddingContainer(EmbeddingContainer<T> ec) {
495: this (ec.token(), ec.rootTokenList()); // Force init of tokenStartOffset and rootTokenOffsetShift
496: invalidate();
497: }
498:
499: private void invalidate() {
500: this .rootToken = null;
501: // Set cachedModCount to -2 which should not occur for regular cases
502: // which should force existing token sequences to be invalidated.
503: this .cachedModCount = -2;
504: }
505:
506: void invalidateChildren() {
507: EmbeddedTokenList etl = firstEmbeddedTokenList;
508: while (etl != null
509: && etl != EmbeddedTokenList.NO_DEFAULT_EMBEDDING) {
510: for (int i = etl.tokenCountCurrent() - 1; i >= 0; i--) {
511: Object tokenOrEC = etl
512: .tokenOrEmbeddingContainerUnsync(i);
513: if (tokenOrEC.getClass() == EmbeddingContainer.class) {
514: ((EmbeddingContainer) tokenOrEC)
515: .invalidateChildren();
516: }
517: }
518: etl = etl.nextEmbeddedTokenList();
519: }
520: }
521:
522: public int cachedModCount() {
523: return cachedModCount;
524: }
525:
526: /**
527: * Check if this embedding container is up-to-date (updateStatusImpl() was called on it)
528: * which is useful for missing-update-status checks.
529: */
530: public void checkStatusUpdated() {
531: if (cachedModCount != -2
532: && cachedModCount != rootTokenList.modCount()
533: && !checkStatusUpdatedThrowingException) {
534: // Prevent OOME because of nested throwing of exc
535: checkStatusUpdatedThrowingException = true;
536: String excMsg = "!!!INTERNAL ERROR!!! Status not updated on "
537: + this
538: + "\nin token hierarchy\n"
539: + rootTokenList.tokenHierarchyOperation();
540: checkStatusUpdatedThrowingException = false;
541: throw new IllegalStateException(excMsg);
542: }
543: }
544:
545: private static boolean checkStatusUpdatedThrowingException;
546:
547: public AbstractToken<T> token() {
548: return token;
549: }
550:
551: /**
552: * Make this container serve a different token.
553: * The updateStatusImpl() should be called afterwards to update tokenStartOffset etc.
554: */
555: public void reinit(AbstractToken<T> token) {
556: this .token = token;
557: TokenList<?> parentTokenList = token.tokenList();
558: assert (parentTokenList != null);
559: if (parentTokenList.getClass() == EmbeddedTokenList.class) {
560: rootToken = ((EmbeddedTokenList<?>) parentTokenList)
561: .rootToken();
562: } else { // parent is a root token list: rootToken == token
563: rootToken = token;
564: }
565: updateStatusImpl();
566: }
567:
568: public TokenList<?> rootTokenList() {
569: return rootTokenList;
570: }
571:
572: public AbstractToken<?> rootToken() {
573: return rootToken;
574: }
575:
576: public int tokenStartOffset() {
577: // checkStatusUpdated();
578: return tokenStartOffset;
579: }
580:
581: public int rootTokenOffsetShift() {
582: // checkStatusUpdated();
583: return offsetShiftFromRootToken;
584: }
585:
586: public char charAt(int tokenRelOffset) {
587: // checkStatusUpdated();
588: return rootToken.charAt(offsetShiftFromRootToken
589: + tokenRelOffset);
590: }
591:
592: public EmbeddedTokenList<?> firstEmbeddedTokenList() {
593: return firstEmbeddedTokenList;
594: }
595:
596: /**
597: * Add a new embedded token list to this container.
598: *
599: * @param prevEtl token list preceding the place of addition.
600: * Null means that the added one will be first in the chain.
601: * @param etl non-null token list to be added.
602: * @param defaultEmbedding whether the added etl is default embedding or not.
603: */
604: public void addEmbeddedTokenList(EmbeddedTokenList<?> prevEtl,
605: EmbeddedTokenList<?> etl, boolean defaultEmbedding) {
606: if (prevEtl != null) {
607: etl.setNextEmbeddedTokenList(prevEtl
608: .nextEmbeddedTokenList());
609: prevEtl.setNextEmbeddedTokenList(etl);
610: } else { // prevEtl is null
611: etl.setNextEmbeddedTokenList(firstEmbeddedTokenList);
612: firstEmbeddedTokenList = etl;
613: }
614: if (defaultEmbedding) {
615: defaultEmbeddedTokenList = etl;
616: }
617: }
618:
619: /**
620: * Remove embedded token list from this container.
621: * Clear reference to next item in the removed token list.
622: *
623: * @param prevEtl token list preceding the place of removal.
624: * Null means that the removed one is first in the chain.
625: * @param etl non-null token list to be removed.
626: * @return next token list linked originally to etl.
627: */
628: public EmbeddedTokenList<?> removeEmbeddedTokenList(
629: EmbeddedTokenList<?> prevEtl, EmbeddedTokenList<?> etl) {
630: EmbeddedTokenList<?> next = etl.nextEmbeddedTokenList();
631: if (prevEtl != null) {
632: prevEtl.setNextEmbeddedTokenList(next);
633: } else {
634: firstEmbeddedTokenList = next;
635: }
636: etl.setNextEmbeddedTokenList(null);
637: if (defaultEmbeddedTokenList == etl) {
638: defaultEmbeddedTokenList = null;
639: }
640: return next;
641: }
642:
643: public EmbeddedTokenList<?> defaultEmbeddedTokenList() {
644: return defaultEmbeddedTokenList;
645: }
646:
647: /**
648: * Check whether this embedding container is no longer present
649: * in the token hierarchy.
650: * <br/>
651: * This method should only be called after updateStatusImpl() was called
652: * (it updates rootToken variable).
653: */
654: public boolean isRemoved() {
655: // checkStatusUpdated();
656: return (rootToken == null);
657: }
658:
659: public void updateStatusAndInvalidate() {
660: updateStatusImpl();
661: invalidate();
662: }
663:
664: public boolean updateStatus() {
665: synchronized (rootTokenList) {
666: return (updateStatusImpl() != null);
667: }
668: }
669:
670: /**
671: * Update and return root token corresponding to this embedding container.
672: */
673: public AbstractToken<?> updateStatusImpl() {
674: if (rootToken == null)
675: return null; // Removed from hierarchy
676: int rootModCount;
677: if (cachedModCount != (rootModCount = rootTokenList.modCount())) {
678: cachedModCount = rootModCount;
679: TokenList<?> parentTokenList = token.tokenList();
680: if (parentTokenList == null) {
681: rootToken = null;
682: } else if (parentTokenList.getClass() == EmbeddedTokenList.class) {
683: EmbeddedTokenList<?> parentEtl = (EmbeddedTokenList<?>) parentTokenList;
684: rootToken = parentEtl.embeddingContainer()
685: .updateStatusImpl();
686: tokenStartOffset = parentEtl
687: .childTokenOffsetNoUpdate(token.rawOffset());
688: EmbeddingContainer parentEC = parentEtl
689: .embeddingContainer();
690: offsetShiftFromRootToken = tokenStartOffset
691: - parentEC.tokenStartOffset()
692: + parentEC.rootTokenOffsetShift();
693: } else { // parent is a root token list: rootToken == token
694: rootToken = token;
695: tokenStartOffset = token.offset(null);
696: offsetShiftFromRootToken = 0;
697: }
698: }
699: return rootToken;
700: }
701:
702: }
|