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.batch;
043:
044: import java.util.ArrayList;
045: import java.util.Set;
046: import org.netbeans.api.lexer.Language;
047: import org.netbeans.api.lexer.LanguagePath;
048: import org.netbeans.lib.lexer.EmbeddingContainer;
049: import org.netbeans.lib.lexer.LAState;
050: import org.netbeans.lib.lexer.TokenList;
051: import org.netbeans.lib.lexer.LexerInputOperation;
052: import org.netbeans.lib.lexer.LexerUtilsConstants;
053: import org.netbeans.api.lexer.InputAttributes;
054: import org.netbeans.api.lexer.Token;
055: import org.netbeans.api.lexer.TokenId;
056: import org.netbeans.lib.lexer.TokenHierarchyOperation;
057: import org.netbeans.lib.lexer.token.AbstractToken;
058: import org.netbeans.lib.lexer.token.TextToken;
059:
060: /**
061: * Token list used for root list for immutable inputs.
062: *
063: * @author Miloslav Metelka
064: * @version 1.00
065: */
066:
067: public abstract class BatchTokenList<T extends TokenId> extends
068: ArrayList<Object> implements TokenList<T> {
069:
070: /** Flag for additional correctness checks (may degrade performance). */
071: private static final boolean testing = Boolean
072: .getBoolean("netbeans.debug.lexer.test");
073:
074: private static boolean maintainLAState;
075:
076: /**
077: * Check whether lookaheads and states are stored for testing purposes.
078: */
079: public static boolean isMaintainLAState() {
080: return maintainLAState;
081: }
082:
083: public static void setMaintainLAState(boolean maintainLAState) {
084: BatchTokenList.maintainLAState = maintainLAState;
085: }
086:
087: private final TokenHierarchyOperation<?, T> tokenHierarchyOperation;
088:
089: private final LanguagePath languagePath;
090:
091: private final Set<T> skipTokenIds;
092:
093: private final InputAttributes inputAttributes;
094:
095: /**
096: * Lexer input used for lexing of the input.
097: */
098: private LexerInputOperation<T> lexerInputOperation;
099:
100: private LAState laState;
101:
102: private boolean inited;
103:
104: public BatchTokenList(
105: TokenHierarchyOperation<?, T> tokenHierarchyOperation,
106: Language<T> language, Set<T> skipTokenIds,
107: InputAttributes inputAttributes) {
108: this .tokenHierarchyOperation = tokenHierarchyOperation;
109: this .languagePath = LanguagePath.get(language);
110: this .skipTokenIds = skipTokenIds;
111: this .inputAttributes = inputAttributes;
112: if (testing) { // Maintain lookaheads and states when in test environment
113: laState = LAState.empty();
114: }
115: }
116:
117: public abstract char childTokenCharAt(int rawOffset, int index);
118:
119: protected abstract LexerInputOperation<T> createLexerInputOperation();
120:
121: protected void init() {
122: lexerInputOperation = createLexerInputOperation();
123: }
124:
125: public TokenList<?> root() {
126: return this ; // this list should always be the root list of the token hierarchy
127: }
128:
129: public TokenHierarchyOperation<?, ?> tokenHierarchyOperation() {
130: return tokenHierarchyOperation;
131: }
132:
133: public LanguagePath languagePath() {
134: return languagePath;
135: }
136:
137: public synchronized int tokenCount() {
138: if (!inited) {
139: init();
140: inited = true;
141: }
142: if (lexerInputOperation != null) { // still lexing
143: tokenOrEmbeddingContainerImpl(Integer.MAX_VALUE);
144: }
145: return size();
146: }
147:
148: public int tokenCountCurrent() {
149: return size();
150: }
151:
152: public int childTokenOffset(int rawOffset) {
153: // Children offsets should be absolute
154: return rawOffset;
155: }
156:
157: public int tokenOffset(int index) {
158: Token<T> token = existingToken(index);
159: int offset;
160: if (token.isFlyweight()) {
161: offset = 0;
162: while (--index >= 0) {
163: token = existingToken(index);
164: offset += token.length();
165: if (!token.isFlyweight()) {
166: offset += token.offset(null);
167: break;
168: }
169: }
170: } else { // non-flyweight offset
171: offset = token.offset(null);
172: }
173: return offset;
174: }
175:
176: public synchronized Object tokenOrEmbeddingContainer(int index) {
177: return tokenOrEmbeddingContainerImpl(index);
178: }
179:
180: private Object tokenOrEmbeddingContainerImpl(int index) {
181: if (!inited) {
182: init();
183: inited = true;
184: }
185: while (lexerInputOperation != null && index >= size()) {
186: Token<T> token = lexerInputOperation.nextToken();
187: if (token != null) { // lexer returned valid token
188: add(token);
189: if (laState != null) { // maintaining lookaheads and states
190: laState = laState.add(lexerInputOperation
191: .lookahead(), lexerInputOperation
192: .lexerState());
193: }
194: } else { // no more tokens from lexer
195: lexerInputOperation.release();
196: lexerInputOperation = null;
197: trimToSize();
198: }
199: }
200: return (index < size()) ? get(index) : null;
201: }
202:
203: private Token<T> existingToken(int index) {
204: return LexerUtilsConstants.token(get(index));
205: }
206:
207: public int lookahead(int index) {
208: return (laState != null) ? laState.lookahead(index) : -1;
209: }
210:
211: public Object state(int index) {
212: return (laState != null) ? laState.state(index) : null;
213: }
214:
215: public int startOffset() {
216: return 0;
217: }
218:
219: public int endOffset() {
220: int cntM1 = tokenCount() - 1;
221: if (cntM1 >= 0)
222: return tokenOffset(cntM1)
223: + LexerUtilsConstants.token(this , cntM1).length();
224: return 0;
225: }
226:
227: public boolean isRemoved() {
228: return false;
229: }
230:
231: public int modCount() {
232: return -1; // immutable input
233: }
234:
235: public synchronized AbstractToken<T> replaceFlyToken(int index,
236: AbstractToken<T> flyToken, int offset) {
237: TextToken<T> nonFlyToken = ((TextToken<T>) flyToken)
238: .createCopy(this , offset);
239: set(index, nonFlyToken);
240: return nonFlyToken;
241: }
242:
243: public void wrapToken(int index,
244: EmbeddingContainer embeddingContainer) {
245: set(index, embeddingContainer);
246: }
247:
248: public InputAttributes inputAttributes() {
249: return inputAttributes;
250: }
251:
252: public boolean isContinuous() {
253: return (skipTokenIds == null);
254: }
255:
256: public Set<T> skipTokenIds() {
257: return skipTokenIds;
258: }
259:
260: }
|