blob: a1cb4d28c3531e43dc8e755330ecf058d716932c [file] [log] [blame]
/*
* 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;
}
}