mockit.coverage.reporting.parsing.LineParser Maven / Gradle / Ivy
/*
* Copyright (c) 2006 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.coverage.reporting.parsing;
import javax.annotation.*;
import mockit.coverage.reporting.parsing.LineElement.*;
/**
* Parses a source line into one or more consecutive segments, identifying which ones contain Java code and which ones
* contain only comments.
* Block comments initiated in a previous line are kept track of until the end of the block is reached.
*/
public final class LineParser
{
private static final String SEPARATORS = ".,;()";
private int lineNum;
@Nonnull private String line;
@Nullable private LineElement initialElement;
private boolean inComments;
// Helper fields:
@Nullable private LineElement currentElement;
private int lineLength;
private int startPos;
private boolean inCodeElement;
private int pos;
private int currChar;
public int getNumber() { return lineNum; }
public boolean isInComments() { return inComments; }
public boolean isBlankLine()
{
int n = line.length();
for (int i = 0; i < n; i++) {
char c = line.charAt(i);
if (!Character.isWhitespace(c)) {
return false;
}
}
return true;
}
@Nonnull public LineElement getInitialElement() { assert initialElement != null; return initialElement; }
boolean parse(@Nonnull String lineToParse)
{
lineNum++;
initialElement = null;
currentElement = null;
line = lineToParse;
lineLength = lineToParse.length();
startPos = inComments ? 0 : -1;
inCodeElement = false;
for (pos = 0; pos < lineLength; pos++) {
currChar = lineToParse.codePointAt(pos);
if (parseComment()) {
break;
}
parseSeparatorsAndCode();
}
if (startPos >= 0) {
addFinalElement();
}
else if (initialElement == null) {
initialElement = new LineElement(ElementType.SEPARATOR, "");
return false;
}
return !inComments && !isBlankLine();
}
private void parseSeparatorsAndCode()
{
boolean separator = isSeparator();
if (separator && !inCodeElement) {
startNewElementIfNotYetStarted();
}
else if (!separator && !inCodeElement) {
if (startPos >= 0) {
addElement();
}
inCodeElement = true;
startPos = pos;
}
else if (separator) {
addElement();
inCodeElement = false;
startPos = pos;
}
}
private boolean isSeparator()
{
return Character.isWhitespace(currChar) || SEPARATORS.indexOf(currChar) >= 0;
}
private void startNewElementIfNotYetStarted()
{
if (startPos < 0) {
startPos = pos;
}
}
private boolean parseComment()
{
if (inComments && parseUntilEndOfLineOrEndOfComment()) {
return true;
}
while (currChar == '/' && pos < lineLength - 1) {
int c2 = line.codePointAt(pos + 1);
if (c2 == '/') {
endCodeElementIfPending();
startNewElementIfNotYetStarted();
inComments = true;
addFinalElement();
inComments = false;
startPos = -1;
return true;
}
else if (c2 == '*') {
endCodeElementIfPending();
startNewElementIfNotYetStarted();
inComments = true;
pos += 2;
if (parseUntilEndOfLineOrEndOfComment()) {
return true;
}
}
else {
break;
}
}
return false;
}
private void endCodeElementIfPending()
{
if (inCodeElement) {
addElement();
startPos = pos;
inCodeElement = false;
}
}
private boolean parseUntilEndOfLineOrEndOfComment()
{
while (pos < lineLength) {
currChar = line.codePointAt(pos);
if (currChar == '*' && pos < lineLength - 1 && line.codePointAt(pos + 1) == '/') {
pos += 2;
addElement();
startPos = -1;
inComments = false;
break;
}
pos++;
}
if (pos < lineLength) {
currChar = line.codePointAt(pos);
return false;
}
else {
return true;
}
}
private void addFinalElement()
{
String text = line.substring(startPos);
addElement(text);
}
private void addElement()
{
String text = pos > 0 ? line.substring(startPos, pos) : line.substring(startPos);
addElement(text);
}
private void addElement(@Nonnull String text)
{
ElementType type;
if (inComments) {
type = ElementType.COMMENT;
}
else if (inCodeElement) {
type = ElementType.CODE;
}
else {
type = ElementType.SEPARATOR;
}
LineElement newElement = new LineElement(type, text);
if (initialElement == null) {
initialElement = newElement;
}
else {
assert currentElement != null;
currentElement.setNext(newElement);
}
currentElement = newElement;
}
}