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.token;
043:
044: import org.netbeans.api.lexer.PartType;
045: import org.netbeans.api.lexer.Token;
046: import org.netbeans.api.lexer.TokenHierarchy;
047: import org.netbeans.api.lexer.TokenId;
048: import org.netbeans.lib.editor.util.CharSequenceUtilities;
049: import org.netbeans.lib.lexer.EmbeddedTokenList;
050: import org.netbeans.lib.lexer.LexerApiPackageAccessor;
051: import org.netbeans.lib.lexer.LexerUtilsConstants;
052: import org.netbeans.lib.lexer.TokenList;
053:
054: /**
055: * Abstract token is base class of all token implementations used in the lexer module.
056: *
057: * @author Miloslav Metelka
058: * @version 1.00
059: */
060:
061: public abstract class AbstractToken<T extends TokenId> extends Token<T>
062: implements CharSequence {
063:
064: private final T id; // 12 bytes (8-super + 4)
065:
066: private TokenList<T> tokenList; // 16 bytes
067:
068: private int rawOffset; // 20 bytes
069:
070: /**
071: * @id non-null token id.
072: */
073: public AbstractToken(T id) {
074: assert (id != null);
075: this .id = id;
076: }
077:
078: AbstractToken(T id, TokenList<T> tokenList, int rawOffset) {
079: this .id = id;
080: this .tokenList = tokenList;
081: this .rawOffset = rawOffset;
082: }
083:
084: public abstract int length();
085:
086: /**
087: * Get identification of this token.
088: *
089: * @return non-null identification of this token.
090: */
091: @Override
092: public final T id() {
093: return id;
094: }
095:
096: /**
097: * Get text represented by this token.
098: */
099: @Override
100: public CharSequence text() {
101: if (tokenList != null) {
102: if (tokenList.getClass() == EmbeddedTokenList.class) {
103: EmbeddedTokenList<?> etl = (EmbeddedTokenList<?>) tokenList;
104: return etl.embeddingContainer().updateStatus() ? this
105: : null;
106: }
107: return this ;
108: } else {
109: return null;
110: }
111: }
112:
113: /**
114: * Get token list to which this token delegates its operation.
115: */
116: public final TokenList<T> tokenList() {
117: return tokenList;
118: }
119:
120: /**
121: * Release this token from being attached to its parent token list.
122: */
123: public final void setTokenList(TokenList<T> tokenList) {
124: this .tokenList = tokenList;
125: }
126:
127: /**
128: * Get raw offset of this token.
129: * <br/>
130: * Raw offset must be preprocessed before obtaining the real offset.
131: */
132: public final int rawOffset() {
133: return rawOffset;
134: }
135:
136: /**
137: * Set raw offset of this token.
138: *
139: * @param rawOffset new raw offset.
140: */
141: public final void setRawOffset(int rawOffset) {
142: this .rawOffset = rawOffset;
143: }
144:
145: @Override
146: public final boolean isFlyweight() {
147: return (rawOffset == -1);
148: }
149:
150: public final void makeFlyweight() {
151: setRawOffset(-1);
152: }
153:
154: @Override
155: public PartType partType() {
156: return PartType.COMPLETE;
157: }
158:
159: @Override
160: public boolean isCustomText() {
161: return false;
162: }
163:
164: @Override
165: public final int offset(TokenHierarchy<?> tokenHierarchy) {
166: if (rawOffset == -1) { // flyweight token
167: return -1;
168: }
169:
170: return (tokenList != null) ? tokenList
171: .childTokenOffset(rawOffset) : rawOffset;
172: // if (tokenHierarchy != null) {
173: // return LexerApiPackageAccessor.get().tokenHierarchyOperation(
174: // tokenHierarchy).tokenOffset(this, tokenList, rawOffset);
175: // } else {
176: // return (tokenList != null)
177: // ? tokenList.childTokenOffset(rawOffset)
178: // : rawOffset;
179: // }
180: }
181:
182: @Override
183: public boolean hasProperties() {
184: return false;
185: }
186:
187: @Override
188: public Object getProperty(Object key) {
189: return null;
190: }
191:
192: // CharSequence methods
193: /**
194: * Implementation of <code>CharSequence.charAt()</code>
195: */
196: public final char charAt(int index) {
197: if (index < 0 || index >= length()) {
198: throw new IndexOutOfBoundsException("index=" + index
199: + ", length=" + length() // NOI18N
200: );
201: }
202: if (tokenList == null) { // Should normally not happen
203: // A bit strange to throw IOOBE but it's more practical since
204: // TokenHierarchy's dump can overcome IOOBE and deliver a useful debug but not NPEs etc.
205: throw new IndexOutOfBoundsException("index=" + index
206: + ", length=" + length()
207: + " but tokenList==null for token "
208: + dumpInfo(null));
209: }
210: return tokenList.childTokenCharAt(rawOffset, index);
211: }
212:
213: public final CharSequence subSequence(int start, int end) {
214: return CharSequenceUtilities.toString(this , start, end);
215: }
216:
217: /**
218: * This method is in fact <code>CharSequence.toString()</code> implementation.
219: */
220: @Override
221: public String toString() {
222: // To prevent NPEs when token.toString() would called without checking
223: // (text() == null) there is an extra check for that.
224: CharSequence text = text();
225: return (text != null) ? CharSequenceUtilities.toString(this , 0,
226: length()) : "<null>";
227: }
228:
229: /**
230: * Dump various information about this token
231: * into a string for debugging purporses.
232: * <br>
233: * A regular <code>toString()</code> usually just returns
234: * a text of the token to satisfy acting of the token instance
235: * as <code>CharSequence</code>.
236: *
237: * @param tokenHierarchy <code>null</code> should be passed
238: * (the parameter is reserved for future use when token hierarchy snapshots will be implemented).
239: * @return dump of the thorough token information.
240: */
241: public String dumpInfo(TokenHierarchy<?> tokenHierarchy) {
242: StringBuilder sb = new StringBuilder();
243: CharSequence text = text();
244: if (text != null) {
245: sb.append('"');
246: for (int i = 0; i < text.length(); i++) {
247: try {
248: CharSequenceUtilities.debugChar(sb, text.charAt(i));
249: } catch (IndexOutOfBoundsException e) {
250: // For debugging purposes it's better than to completely fail
251: sb.append("IOOBE at index=").append(i)
252: .append("!!!"); // NOI18N
253: break;
254: }
255: }
256: sb.append('"');
257: } else {
258: sb.append("<null-text>"); // NOI18N
259: }
260: sb.append(' ');
261: if (isFlyweight()) {
262: sb.append("F(").append(length()).append(')');
263: } else {
264: int offset = offset(tokenHierarchy);
265: sb.append('<').append(offset); // NOI18N
266: sb.append(",").append(offset + length()).append('>'); // NOI18N
267: }
268: sb.append(' ').append(
269: id != null ? id.name() + '[' + id.ordinal() + ']'
270: : "<null-id>"); // NOI18N
271: sb.append(" ").append(dumpInfoTokenType());
272: return sb.toString();
273: }
274:
275: protected String dumpInfoTokenType() {
276: return "AbsT"; // NOI18N "AbstractToken"
277: }
278:
279: }
|