mockit.coverage.reporting.parsing.LineElement Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmockit Show documentation
Show all versions of jmockit Show documentation
JMockit is a Java toolkit for automated developer testing.
It contains mocking/faking APIs and a code coverage tool, supporting both JUnit and TestNG.
The mocking APIs allow all kinds of Java code, without testability restrictions, to be tested
in isolation from selected dependencies.
/*
* 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 java.util.*;
import java.util.regex.*;
import javax.annotation.*;
import static java.util.Arrays.*;
public final class LineElement implements Iterable
{
private static final List CONDITIONAL_OPERATORS = asList("||", "&&", ":", "else", "?");
private static final Pattern OPEN_TAG = Pattern.compile("<");
private static final NoSuchElementException LAST_ELEMENT_REACHED = new NoSuchElementException();
enum ElementType { CODE, COMMENT, SEPARATOR }
enum ConditionalStatement
{
IF("if"), FOR("for"), WHILE("while");
@Nonnull private final String keyword;
ConditionalStatement(@Nonnull String keyword) { this.keyword = keyword; }
@Nullable
static ConditionalStatement find(@Nonnull String keyword)
{
for (ConditionalStatement statement : values()) {
if (statement.keyword.equals(keyword)) {
return statement;
}
}
return null;
}
}
@Nonnull private final ElementType type;
@Nonnull private final String text;
@Nullable private String openingTag;
@Nullable private String closingTag;
@Nullable private LineElement next;
@Nullable private ConditionalStatement conditionalStatement;
private int parenthesesBalance;
LineElement(@Nonnull ElementType type, @Nonnull String text)
{
this.type = type;
this.text = OPEN_TAG.matcher(text).replaceAll("<");
}
public boolean isCode() { return type == ElementType.CODE; }
public boolean isComment() { return type == ElementType.COMMENT; }
public boolean isKeyword(@Nonnull String keyword)
{
return isCode() && text.equals(keyword);
}
public boolean isDotSeparator()
{
return type == ElementType.SEPARATOR && text.charAt(0) == '.';
}
@Nonnull public String getText() { return text; }
@Nullable public LineElement getNext() { return next; }
void setNext(@Nullable LineElement next) { this.next = next; }
@Nullable
public LineElement getNextCodeElement()
{
if (next != null) {
for (LineElement element : next) {
if (element.isCode()) {
return element;
}
}
}
return null;
}
public void wrapText(@Nonnull String desiredOpeningTag, @Nonnull String desiredClosingTag)
{
openingTag = desiredOpeningTag;
closingTag = desiredClosingTag;
}
@Nullable
public LineElement appendUntilNextCodeElement(@Nonnull StringBuilder line)
{
LineElement element = this;
while (!element.isCode()) {
element.appendText(line);
element = element.next;
if (element == null) {
break;
}
copyConditionalTrackingState(element);
}
return element;
}
private void copyConditionalTrackingState(@Nonnull LineElement destination)
{
destination.conditionalStatement = conditionalStatement;
destination.parenthesesBalance = parenthesesBalance;
}
private void appendText(@Nonnull StringBuilder line)
{
if (openingTag == null) {
line.append(text);
}
else {
line.append(openingTag).append(text).append(closingTag);
}
}
@Nullable
public LineElement findNextBranchingPoint()
{
if (conditionalStatement == null) {
conditionalStatement = ConditionalStatement.find(text);
}
if (isBranchingElement()) {
if (next != null) {
copyConditionalTrackingState(next);
}
return this;
}
if (conditionalStatement != null) {
int balance = getParenthesisBalance();
parenthesesBalance += balance;
if (balance != 0 && parenthesesBalance == 0) {
return next;
}
}
if (next == null) {
return null;
}
copyConditionalTrackingState(next);
//noinspection TailRecursion
return next.findNextBranchingPoint();
}
public boolean isBranchingElement()
{
if (conditionalStatement == ConditionalStatement.FOR) {
int p = text.indexOf(':');
if (p < 0) p = text.indexOf(';');
return p >= 0 && text.trim().length() == 1;
}
return CONDITIONAL_OPERATORS.contains(text);
}
private int getParenthesisBalance()
{
int balance = 0;
int p = text.indexOf('(');
while (p >= 0) {
balance++;
p = text.indexOf('(', p + 1);
}
int q = text.indexOf(')');
while (q >= 0) {
balance--;
q = text.indexOf(')', q + 1);
}
return balance;
}
@Nullable
public LineElement findWord(@Nonnull String word)
{
for (LineElement element : this) {
if (element.isCode() && word.equals(element.text)) {
return element;
}
}
return null;
}
public int getBraceBalanceUntilEndOfLine()
{
int balance = 0;
for (LineElement element : this) {
balance += element.getBraceBalance();
}
return balance;
}
private int getBraceBalance()
{
if (isCode() && text.length() == 1) {
char c = text.charAt(0);
if (c == '{') {
return 1;
}
else if (c == '}') {
return -1;
}
}
return 0;
}
public void appendAllBefore(@Nonnull StringBuilder line, @Nullable LineElement elementToStopBefore)
{
LineElement elementToPrint = this;
do {
elementToPrint.appendText(line);
elementToPrint = elementToPrint.next;
}
while (elementToPrint != null && elementToPrint != elementToStopBefore);
}
@Nonnull @Override
public Iterator iterator()
{
return new Iterator() {
@Nullable private LineElement current = LineElement.this;
@Override public boolean hasNext() { return current != null; }
@Nonnull @Override
public LineElement next()
{
if (current == null) {
throw LAST_ELEMENT_REACHED;
}
LineElement nextElement = current;
current = current.next;
return nextElement;
}
@Override public void remove() {}
};
}
@Override
public String toString()
{
StringBuilder line = new StringBuilder(200);
for (LineElement element : this) {
element.appendText(line);
}
return line.toString();
}
}