156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/* 256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Copyright (C) 2010 Google Inc. 356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Licensed under the Apache License, Version 2.0 (the "License"); 556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * you may not use this file except in compliance with the License. 656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * You may obtain a copy of the License at 756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * http://www.apache.org/licenses/LICENSE-2.0 956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 1056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Unless required by applicable law or agreed to in writing, software 1156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * distributed under the License is distributed on an "AS IS" BASIS, 1256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * See the License for the specific language governing permissions and 1456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * limitations under the License. 1556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 1656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 1756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpackage com.google.streamhtmlparser.util; 1856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 1956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.common.base.Preconditions; 2056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 2156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.Arrays; 2256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 2356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/** 2456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Implements a circular (ring) buffer of characters with specialized 2556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * application logic in order to determine the context of some 2656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Javascript content that is being parsed. 2756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 2856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * This is a specialized class - of no use to external code - 2956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * which aims to be 100% compatible with the corresponding logic 3056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * in the C-version of the HtmlParser, specifically 3156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <code>jsparser.c</code>. In particular: 3256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <ul> 3356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <li> The API is odd, using negative indexes to access content in 3456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * the buffer. Changing the API would mean changing the test 3556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * cases and have more difficulty determining whether we are 3656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * remaining compatible with the C-version. It is left as an 3756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * exercise for once the code is very stable and proven. 3856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <li> Repeated whitespace is folded into just one character to 3956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * use the space available efficiently. 4056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <li> The buffer size is fixed. There is currently no need to 4156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * make it variable so we avoid the need for constructors. 4256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * </ul> 4356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 4456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpublic class JavascriptTokenBuffer { 4556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 4656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 4756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Size of the ring buffer used to lookup the last token in the javascript 4856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * stream. The size is somewhat arbitrary but must be larger than 4956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * the biggest token we want to lookup plus three: Two delimiters plus 5056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * an empty ring buffer slot. 5156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 5256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private static final int BUFFER_SIZE = 18; 5356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 5456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** Storage implementing the circular buffer. */ 5556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private final char[] buffer; 5656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 5756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** Index of the first item in our circular buffer. */ 5856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private int startIndex; 5956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 6056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** Index of the last item in our circular buffer. */ 6156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private int endIndex; 6256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 6356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 6456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Constructs an empty javascript token buffer. The size is fixed, 6556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * see {@link #BUFFER_SIZE}. 6656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 6756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public JavascriptTokenBuffer() { 6856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson buffer = new char[BUFFER_SIZE]; 6956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson startIndex = 0; 7056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson endIndex = 0; 7156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 7256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 7356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 7456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Constructs a javascript token buffer that is identical to 7556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * the one given. In particular, it has the same size and contents. 7656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 7756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @param aJavascriptTokenBuffer the {@code JavascriptTokenBuffer} to copy 7856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 7956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public JavascriptTokenBuffer(JavascriptTokenBuffer aJavascriptTokenBuffer) { 8056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson buffer = Arrays.copyOf(aJavascriptTokenBuffer.buffer, 8156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson aJavascriptTokenBuffer.buffer.length); 8256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson startIndex = aJavascriptTokenBuffer.startIndex; 8356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson endIndex = aJavascriptTokenBuffer.endIndex; 8456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 8556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 8656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 8756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * A simple wrapper over <code>appendChar</code>, it appends a string 8856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * to the buffer. Sequences of whitespace and newlines 8956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * are folded into one character to save space. Null strings are 9056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * not allowed. 9156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 9256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @param input the {@code String} to append, cannot be {@code null} 9356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 9456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // TODO: Move to testing since not used in code. 9556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void appendString(String input) { 9656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (input == null) { 9756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new NullPointerException("input == null is not allowed"); 9856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 9956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson for (int i = 0; i < input.length(); i++) { 10056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson appendChar(input.charAt(i)); 10156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 10256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 10356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 10456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 10556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Appends a character to the buffer. We fold sequences of whitespace and 10656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * newlines into one to save space. 10756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 10856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @param input the {@code char} to append 10956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 11056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void appendChar(char input) { 11156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (HtmlUtils.isJavascriptWhitespace(input) && 11256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson HtmlUtils.isJavascriptWhitespace(getChar(-1))) { 11356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return; 11456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 11556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson buffer[endIndex] = input; 11656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson endIndex = (endIndex + 1) % buffer.length; 11756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (endIndex == startIndex) { 11856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson startIndex = (endIndex + 1) % buffer.length; 11956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 12056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 12156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 12256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 12356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Returns the last character in the buffer and removes it from the buffer 12456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * or the NUL character '\0' if the buffer is empty. 12556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 12656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @return last character in the buffer or '\0' if the buffer is empty 12756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 12856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public char popChar() { 12956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (startIndex == endIndex) { 13056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return '\0'; 13156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 13256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson endIndex--; 13356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (endIndex < 0) { 13456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson endIndex += buffer.length; 13556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 13656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return buffer[endIndex]; 13756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 13856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 13956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 14056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Returns the character at a given index in the buffer or nul ('\0') 14156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * if the index is outside the range of the buffer. Such could happen 14256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * if the buffer is not filled enough or the index is larger than the 14356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * size of the buffer. 14456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 14556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <p>Position must be negative where -1 is the index of the last 14656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * character in the buffer. 14756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 14856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @param position The index into the buffer 14956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 15056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @return character at the requested index 15156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 15256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public char getChar(int position) { 15356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson assert(position < 0); // Developer error if it triggers. 15456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 15556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson int absolutePosition = getAbsolutePosition(position); 15656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (absolutePosition < 0) { 15756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return '\0'; 15856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 15956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 16056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return buffer[absolutePosition]; 16156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 16256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 16356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 16456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Sets the given {@code input} at the given {@code position} of the buffer. 16556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Returns {@code true} if we succeeded or {@code false} if we 16656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * failed (i.e. the write was beyond the buffer boundary). 16756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 16856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <p>Index positions are negative where -1 is the index of the 16956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * last character in the buffer. 17056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 17156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @param position The index at which to set the character 17256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @param input The character to set in the buffer 17356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @return {@code true} if we succeeded, {@code false} otherwise 17456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 17556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public boolean setChar(int position, char input) { 17656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson assert(position < 0); // Developer error if it triggers. 17756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 17856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson int absolutePosition = getAbsolutePosition(position); 17956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (absolutePosition < 0) { 18056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return false; 18156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 18256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 18356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson buffer[absolutePosition] = input; 18456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return true; 18556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 18656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 18756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 18856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 18956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Returns the last javascript identifier/keyword in the buffer. 19056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 19156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @return the last identifier or {@code null} if none was found 19256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 19356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public String getLastIdentifier() { 19456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson int end = -1; 19556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 19656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (HtmlUtils.isJavascriptWhitespace(getChar(-1))) { 19756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson end--; 19856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 19956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson int position; 20056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson for (position = end; HtmlUtils.isJavascriptIdentifier(getChar(position)); 20156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson position--) { 20256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 20356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if ((position + 1) >= end) { 20456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return null; 20556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 20656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return slice(position + 1, end); 20756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 20856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 20956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 21056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Returns a slice of the buffer delimited by the given indices. 21156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 21256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * The start and end indexes represent the start and end of the 21356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * slice to copy. If the start argument extends beyond the beginning 21456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * of the buffer, the slice will only contain characters 21556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * starting from the beginning of the buffer. 21656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 21756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @param start The index of the first character the copy 21856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @param end the index of the last character to copy 21956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 22056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @return {@code String} between the given indices 22156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 22256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public String slice(int start, int end) { 22356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Developer error if any of the asserts below fail. 22456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson Preconditions.checkArgument(start <= end); 22556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson Preconditions.checkArgument(start < 0); 22656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson Preconditions.checkArgument(end < 0); 22756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 22856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson StringBuffer output = new StringBuffer(); 22956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson for (int position = start; position <= end; position++) { 23056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson char c = getChar(position); 23156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (c != '\0') { 23256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson output.append(c); 23356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 23456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 23556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return new String(output); 23656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 23756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 23856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 23956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Returns the position relative to the start of the buffer or -1 24056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * if the position is past the size of the buffer. 24156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 24256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @param position the index to be translated 24356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @return the position relative to the start of the buffer 24456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 24556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private int getAbsolutePosition(int position) { 24656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson assert (position < 0); // Developer error if it triggers. 24756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (position <= -buffer.length) { 24856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return -1; 24956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 25056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson int len = endIndex - startIndex; 25156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (len < 0) { 25256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson len += buffer.length; 25356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 25456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (position < -len) { 25556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return -1; 25656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 25756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson int absolutePosition = (position + endIndex) % buffer.length; 25856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (absolutePosition < 0) { 25956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson absolutePosition += buffer.length; 26056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 26156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return absolutePosition; 26256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 26356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson} 264