/* * 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 flash.tools.debugger.concrete; /** * Contains the text contents of a script and is able * to map line numbers to specific regions of the script (i.e. string) */ public class ScriptText { private final String m_text; private int[] m_lineMap; // a 2-d array [2i] = startIndex and [2i+1] = endIndex for line i public ScriptText(String text) { m_text = text; } /* return a string containing the line number requested */ public String getLine(int lineNum) { determineLines(); int index = lineNum-1; if (index < 0) ; // throw /* look into our mapping array */ int start = m_lineMap[2*index]; int end = m_lineMap[(2*index)+1]; String s = m_text.substring(start, end); return s; } /* line count in module */ public int getLineCount() { determineLines(); return m_lineMap.length/2; } /** * Build mapping tables based on the line count of * the given source string. * * These tables allow us to compute starting and * ending locations of each line of the source file. * * The assumption using this technique is that most * lines of the source files will never be accessed * thus we only incur the overhead of 8 bytes * (start & end) per line, plus the actual string * contents each line a line is requested. * * Thus we need 8 * num_files * num_lines_per_file bytes * for all these maps. * * For example, say each file is 1000 lines and we have * 400 source files; We would consume 3.2MB. With * each request we would allocate an additional 20+ * bytes for the string (assuming a 20B avg line length). * * Allocating each line individually we would consume * 1000 * 400 * 20 = 8MB. * * It is debatable whether this scheme is more efficient * than actually builing an array of Strings to contain * the lines, but gut feel says it is ;) */ private synchronized void determineLines() { // determineLines() is done on demand in order to avoid wasting time // doing this for every file; so check if we've already done it if (m_lineMap != null) return; int count = lineCountFor(m_text) + 1; // add 1 to the line count to handle newline on last line // allocated our maps (really a 2-d array where [i] = startAt & [i+1] = endAt ) m_lineMap = new int[(2*count)+1]; int i = 0; int lineNum = 0; int startAt = 0; int endAt = 0; int length = m_text.length(); char c = '\0'; while(i < length) { /* end of line */ c = m_text.charAt(i++); if (c == '\n' || c == '\r') { m_lineMap[2*lineNum] = startAt; m_lineMap[(2*lineNum)+1] = endAt; lineNum++; /* do we need to chew a CR LF combo */ if (c == '\r' && i < length && m_text.charAt(i) == '\n') i++; startAt = i; endAt = i; } else endAt++; } /* need to add the last line? */ if (startAt != endAt) { /* add the last line if not empty */ m_lineMap[2*lineNum] = startAt; m_lineMap[(2*lineNum)+1] = endAt; } } /** * Count the number of lines within this string. */ public static int lineCountFor(String s) { int i = 0; int lineNum = 0; int length = s.length(); char c = '\0'; while(i < length) { /* end of line */ c = s.charAt(i++); if (c == '\n' || c == '\r') { lineNum++; /* do we need to chew a CR LF combo */ if (c == '\r' && i < length && s.charAt(i) == '\n') i++; } } return lineNum; } }