All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mockit.coverage.reporting.parsing.LineElement Maven / Gradle / Ivy

Go to download

JMockit is a Java toolkit for automated developer testing. It contains APIs for the creation of the objects to be tested, for mocking dependencies, and for faking external APIs; JUnit (4 & 5) and TestNG test runners are supported. It also contains an advanced code coverage tool.

There is a newer version: 1.49
Show newest version
/*
 * 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();
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy