/* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.flex.compiler.internal.parsing; import antlr.Token; import org.apache.flex.compiler.common.ISourceLocation; import org.apache.flex.compiler.internal.common.Counter; import org.apache.flex.compiler.internal.parsing.as.ASTokenTypes; import org.apache.flex.compiler.internal.parsing.as.IncludeHandler; import org.apache.flex.compiler.parsing.ICMToken; /** * Base class of ASToken, MXMLToken, CSSToken */ public abstract class TokenBase extends Token implements ICMToken, ISourceLocation { /** * Constructor * * @param tokenType type of token * @param start location location information * @param end location location information * @param line location location information * @param column location location information * @param text actual text represented by token */ public TokenBase(int tokenType, int start, int end, int line, int column, CharSequence text) { type = tokenType; localStart = this.start = start; localEnd = this.end = end; this.line = line; this.column = column; this.text = text; this.endLine = line; this.endColumn = column + end - start; if (Counter.COUNT_TOKENS) countTokens(); } /** * Copy constructor * * @param o token to copy */ public TokenBase(TokenBase o) { type = o.type; start = o.start; end = o.end; line = o.line; column = o.column; endLine = o.endLine; endColumn = o.endColumn; text = o.text; localStart = o.localStart; localEnd = o.localEnd; sourcePath = o.sourcePath; if (Counter.COUNT_TOKENS) countTokens(); } /** * Text represented by this token */ private CharSequence text; /** * Start of this token */ private int start; /** * End offset of this token */ private int end; /** * Line of this token */ private int line; /** * Column of this token */ private int column; /** * End line of this token */ private int endLine; /** * End column of this token */ private int endColumn; /** * Flag to determine if this token is locked */ private boolean locked; /** * Local start offset. */ private int localStart; /** * Local end offset. */ private int localEnd; protected abstract String getTypeString(); /** * @return Local start offset. */ public final int getLocalStart() { return localStart; } /** * @return Local end offset. */ public final int getLocalEnd() { return localEnd; } public final void reuse(final int tokenType, final int start, final int end, final int line, final int column, final CharSequence text) { type = tokenType; this.start = start; this.end = end; this.line = line; this.column = column; this.endLine = line; this.endColumn = column + end - start; this.text = text; } /** * Locks this token. When locked, if this token is in a token pool, it will * not be overwritten */ public void lock() { locked = true; } /** * Returns whether this token can be overwritten when it is a member of a * token pool * * @return true if we are locked */ public boolean isLocked() { return locked; } /** * Get the text represented by this token * * @return text represented by this token * @see antlr.Token#getText() */ @Override public String getText() { //we're either going to be a String or a StringBuilder //String.toString returns itself //StringBuilder toString returns a new String if (text != null) return text.toString(); return ""; } /** * Returns the underlying CharSequence that represents the contents of this * token * * @return a {@link CharSequence} or null */ public CharSequence getCharSequence() { return text; } /** * Set the text represented by this token * * @param text text represented by this token * @see antlr.Token#setText(java.lang.String) */ @Override public void setText(String text) { this.text = text; } /** * Set the CharSequence represented by this token * * @param text text represented by this token */ public void setText(CharSequence text) { this.text = text; } public void setLocation(int start, int end, int line, int column) { this.start = start; this.end = end; this.line = line; this.column = column; this.endLine = line; this.endColumn = column + end - start; } @Override public int getStart() { return start; } public void setStart(int start) { this.start = start; } @Override public int getEnd() { return end; } public void setEnd(int end) { this.end = end; } @Override public int getLine() { return line; } @Override public void setLine(int line) { this.line = line; } public final boolean matchesLine(final TokenBase other) { return other != null && other.line == line; } @Override public int getColumn() { return column; } @Override public void setColumn(int column) { this.column = column; } @Override public int getEndLine() { return endLine; } public void setEndLine(int line) { endLine = line; } @Override public int getEndColumn() { return endColumn; } public void setEndColumn(int column) { endColumn = column; } /** * Determine whether or not this token is bogus (i.e. the start and end * offsets are the same, which implies that it was inserted from an included * file or during token fixup) * * @return true iff the token is bogus */ @Override public boolean isImplicit() { return start == end; } /** * For debugging only. */ private String getEscapedText() { String text = getText(); text = text.replaceAll("\n", "\\\\n"); text = text.replaceAll("\r", "\\\\r"); text = text.replaceAll("\t", "\\\\t"); return text; } /** * For debugging only. This format is nice in the Eclipse debugger. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('|'); sb.append(getEscapedText()); sb.append('|'); sb.append(' '); sb.append(getTypeString()); sb.append(' '); if (locked) sb.append("locked "); int line = getLine(); if (line != UNKNOWN) sb.append(line + 1); else sb.append('?'); sb.append(':'); int column = getColumn(); if (column != UNKNOWN) sb.append(column + 1); else sb.append('?'); sb.append(' '); int start = getStart(); if (start != UNKNOWN) sb.append(start); else sb.append('?'); sb.append('-'); int end = getEnd(); if (end != UNKNOWN) sb.append(end); else sb.append('?'); sb.append(' '); String sourcePath = getSourcePath(); if (sourcePath != null) { sb.append('"'); sb.append(sourcePath); sb.append('"'); } else { sb.append('?'); } return sb.toString(); } /** * For debugging only. This format is nice in a text file. */ public String toDumpString() { StringBuilder sb = new StringBuilder(); sb.append(getLine() + 1); sb.append('\t'); sb.append(getColumn() + 1); sb.append('\t'); sb.append(getStart()); sb.append('\t'); sb.append(getEnd()); sb.append('\t'); String typeString = getTypeString(); sb.append(typeString); int n = 28 - typeString.length(); for (int i = 0; i < n; i++) sb.append(' '); sb.append('\t'); sb.append('|'); sb.append(getEscapedText()); sb.append('|'); return sb.toString(); } /** * Reduce the span of the token by removing characters from the beginning * and end. This is used to remove quote characters and such from tokens. * * @param trimLeft number of characters to remove from the left of the token * @param trimRight number of characters to remove from the right of the * token */ public void truncate(int trimLeft, int trimRight) { String text = getText(); if (trimLeft + trimRight <= text.length()) { text = text.substring(trimLeft, text.length() - trimRight); setText(text); start += trimLeft; end -= trimRight; } } /** * Adjust all associated offsets by the adjustment amount * * @param offsetAdjustment amount to add to offsets */ public void adjustOffsets(int offsetAdjustment) { start += offsetAdjustment; end += offsetAdjustment; } /** * Adjust all associated offsets by the adjustment amount * * @param offsetAdjustment amount to add to offsets * @param lineAdjustment amount to add to the line number * @param columnAdjustment amount to add to the column number */ public void adjustLocation(int offsetAdjustment, int lineAdjustment, int columnAdjustment) { start += offsetAdjustment; end += offsetAdjustment; line += lineAdjustment; column += columnAdjustment; endLine += lineAdjustment; endColumn += columnAdjustment; } /** * Capture the current start/end offsets as this token's local offsets. This * method is called in {@code StreamingASTokenizer#nextTokenFromReader()} * after the token is initialized, and before being updated by * {@link IncludeHandler#onNextToken}. */ public final void storeLocalOffset() { this.localStart = start; this.localEnd = end; } private String sourcePath; @Override public final String getSourcePath() { return sourcePath; } public final void setSourcePath(String path) { this.sourcePath = path; } /** * Verifies that this token has its type and location information set. * <p> * This is used only in asserts. */ public boolean verify() { // Verify the token type. int type = getType(); assert type != 0 : "Token has no type: " + toString(); // Verify the source location (except for EOF tokens, // which are special and don't have a source location). if (type != ASTokenTypes.EOF) { assert getStart() != UNKNOWN : "Token has unknown start: " + toString(); assert getEnd() != UNKNOWN : "Token has unknown end: " + toString(); assert getLine() != UNKNOWN : "Token has an unknown line: " + toString(); assert getColumn() != UNKNOWN : "Token has an unknown column: " + toString(); assert getEndLine() != UNKNOWN : "Token has an unknown end line: " + toString(); assert getEndColumn() != UNKNOWN : "Token has an unknown end column: " + toString(); } return true; } /** * Counts various types of tokens that are created, as well as the total * number of tokens. */ private void countTokens() { Counter counter = Counter.getInstance(); counter.incrementCount(getClass().getSimpleName()); counter.incrementCount("tokens"); } @Override public int getAbsoluteEnd() { return getEnd(); } @Override public int getAbsoluteStart() { return getStart(); } }