/* * Copyright 2009 Google Inc. * * Licensed 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 com.google.common.css.compiler.ast; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import com.google.common.primitives.Ints; import java.io.IOException; import java.util.List; /** * Efficient {@code String} based {@link CharStream} implementation. * * @author dgajda@google.com (Damian Gajda) */ public class StringCharStream implements CharStream { private static final IOException END_OF_STREAM = new IOException(); /** The input string. */ private final String input; private final int length; private int charPos; private int line; private int column; private char lastChar; private int tokenStart; private int beginLine; private int beginColumn; private int tabSize = 1; private boolean trackLineColumn; /** * This array (working as a map: lineNumber -> characterIndex) helps to * compute token locations efficiently. First element is not used as line * numbers are 1 based. */ private int[] lineToCharIndex; /** * Creates a character stream for a given string. * * @param inputString input string for this stream */ public StringCharStream(String inputString) { input = inputString; length = input.length(); lastChar = '\u0000'; charPos = -1; column = 0; line = 1; tokenStart = charPos; beginLine = line; beginColumn = column; initCharIndex(input); } private void initCharIndex(String source) { List<Integer> lineToCharIndexList = Lists.newArrayList(); int charIndex = -1; lineToCharIndexList.add(charIndex); do { charIndex++; lineToCharIndexList.add(charIndex); charIndex = source.indexOf('\n', charIndex); } while (charIndex >= 0); lineToCharIndex = Ints.toArray(lineToCharIndexList); } /** * Returns an absolute character location for given line and column location. * * @param lineNumber line number (1 based) * @param indexInLine column number (1 based) * @return 0 based absolute character index in the input string */ public int convertToCharacterIndex(int lineNumber, int indexInLine) { return lineToCharIndex[lineNumber] + indexInLine - 1; } /** * @return index of last read character */ public int getCharIndex() { return charPos; } /** * @return index of the first character of a token */ @VisibleForTesting int getTokenStart() { return tokenStart; } /** {@inheritDoc} */ @Override public char readChar() throws IOException { if (charPos + 1 == length) { throw END_OF_STREAM; } if (lastChar == '\n') { line++; column = 0; } if (lastChar == '\t') { column += (tabSize - (column % tabSize)); } else { column++; } lastChar = input.charAt(++charPos); return lastChar; } /** {@inheritDoc} */ @Deprecated @Override public int getColumn() { return getEndColumn(); } /** {@inheritDoc} */ @Deprecated @Override public int getLine() { return getEndLine(); } /** {@inheritDoc} */ @Override public int getEndColumn() { return column; } /** {@inheritDoc} */ @Override public int getEndLine() { return line; } /** {@inheritDoc} */ @Override public int getBeginColumn() { return beginColumn; } /** {@inheritDoc} */ @Override public int getBeginLine() { return beginLine; } /** {@inheritDoc} */ @Override public void backup(int amount) { charPos -= amount; while (line > 1 && lineToCharIndex[line] > charPos) { line--; } column = charPos - lineToCharIndex[line] + 1; lastChar = charPos < 0 ? '\u0000' : input.charAt(charPos); } /** {@inheritDoc} */ @Override public char BeginToken() throws IOException { readChar(); tokenStart = charPos; beginLine = line; beginColumn = column; return lastChar; } /** {@inheritDoc} */ @Override public String GetImage() { return input.substring(tokenStart, charPos + 1); } /** {@inheritDoc} */ @Override public char[] GetSuffix(int len) { int end = charPos + 1; int start = end - len; char[] chars = new char[end - start]; input.getChars(start, end, chars, 0); return chars; } /** {@inheritDoc} */ @Override public void Done() { // Does nothing since no resources need to be freed. } @Override public void setTabSize(int tabSize) { throw new UnsupportedOperationException("setTabSize() is not supported."); } @Override public int getTabSize() { return tabSize; } @Override public boolean getTrackLineColumn() { return trackLineColumn; } @Override public void setTrackLineColumn(boolean trackLineColumn) { this.trackLineColumn = trackLineColumn; } }