/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.dfa;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Computes the first sequence in a list.
*
* <p>e.g. IF_START 0 WHILE_EXPR 1 WHILE_END 2 IF_END 3</p>
*
* <p>The first sequence is WHILE_EXPR and WHILE_END. It returns always the
* first inner nested scope.
* </p>
*
* @author raik
*/
public class SequenceChecker {
private static final Logger LOGGER = Logger.getLogger(SequenceChecker.class.getName());
/*
* Element of logical structure of brace nodes.
*/
private static class Status {
public static final int ROOT = -1;
private List<Status> nextSteps = new ArrayList<>();
// NodeType
private int type;
private boolean lastStep;
Status(int type) {
this(type, false);
}
Status(int type, boolean lastStep) {
this.type = type;
this.lastStep = lastStep;
}
public void addStep(Status type) {
nextSteps.add(type);
}
/**
*
* @param type
* candidate
* @return valid Status or null if NodeType is not a valid transition
* NodeType
*/
public Status step(int type) {
for (int i = 0; i < this.nextSteps.size(); i++) {
if (type == nextSteps.get(i).type) {
return nextSteps.get(i);
}
}
return null;
}
public boolean isLastStep() {
return this.lastStep;
}
public boolean hasMoreSteps() {
return this.nextSteps.size() > 1;
}
@Override
public String toString() {
return "NodeType=" + NodeType.stringFromType(type) + "(" + type + "), lastStep=" + lastStep;
}
}
private static Status root;
/**
* Create State transition map for the control structures
*/
static {
root = new Status(Status.ROOT);
Status ifNode = new Status(NodeType.IF_EXPR);
Status ifSt = new Status(NodeType.IF_LAST_STATEMENT);
Status ifStWithoutElse = new Status(NodeType.IF_LAST_STATEMENT_WITHOUT_ELSE, true);
Status elseSt = new Status(NodeType.ELSE_LAST_STATEMENT, true);
Status whileNode = new Status(NodeType.WHILE_EXPR);
Status whileSt = new Status(NodeType.WHILE_LAST_STATEMENT, true);
Status switchNode = new Status(NodeType.SWITCH_START);
Status caseSt = new Status(NodeType.CASE_LAST_STATEMENT);
Status switchDefault = new Status(NodeType.SWITCH_LAST_DEFAULT_STATEMENT);
Status switchEnd = new Status(NodeType.SWITCH_END, true);
Status forInit = new Status(NodeType.FOR_INIT);
Status forExpr = new Status(NodeType.FOR_EXPR);
Status forUpdate = new Status(NodeType.FOR_UPDATE);
Status forSt = new Status(NodeType.FOR_BEFORE_FIRST_STATEMENT);
Status forEnd = new Status(NodeType.FOR_END, true);
Status doSt = new Status(NodeType.DO_BEFORE_FIRST_STATEMENT);
Status doExpr = new Status(NodeType.DO_EXPR, true);
Status labelNode = new Status(NodeType.LABEL_STATEMENT);
Status labelEnd = new Status(NodeType.LABEL_LAST_STATEMENT, true);
root.addStep(ifNode);
root.addStep(whileNode);
root.addStep(switchNode);
root.addStep(forInit);
root.addStep(forExpr);
root.addStep(forUpdate);
root.addStep(forSt);
root.addStep(doSt);
root.addStep(labelNode);
ifNode.addStep(ifSt);
ifNode.addStep(ifStWithoutElse);
ifSt.addStep(elseSt);
ifStWithoutElse.addStep(root);
elseSt.addStep(root);
labelNode.addStep(labelEnd);
labelEnd.addStep(root);
whileNode.addStep(whileSt);
whileSt.addStep(root);
switchNode.addStep(caseSt);
switchNode.addStep(switchDefault);
switchNode.addStep(switchEnd);
caseSt.addStep(caseSt);
caseSt.addStep(switchDefault);
caseSt.addStep(switchEnd);
switchDefault.addStep(switchEnd);
switchDefault.addStep(caseSt);
switchEnd.addStep(root);
forInit.addStep(forExpr);
forInit.addStep(forUpdate);
forInit.addStep(forSt);
forExpr.addStep(forUpdate);
forExpr.addStep(forSt);
forUpdate.addStep(forSt);
forSt.addStep(forEnd);
forEnd.addStep(root);
doSt.addStep(doExpr);
doExpr.addStep(root);
}
private Status aktStatus;
private List<StackObject> bracesList;
private int firstIndex = -1;
private int lastIndex = -1;
/*
* Defines the logical structure.
*/
public SequenceChecker(List<StackObject> bracesList) {
this.aktStatus = root;
this.bracesList = bracesList;
}
/**
* Finds the first innermost sequence e.g IFStart & IFEnd. If the list has
* been exhausted (firstIndex==lastIndex) the method returns true.
*/
public boolean run() {
LOGGER.entering(this.getClass().getCanonicalName(), "run");
this.aktStatus = root;
this.firstIndex = 0;
this.lastIndex = 0;
boolean lookAhead = false;
/*
* Walk through the bracesList attempting to identify the first
* contiguous graph of Nodes from the initial Status to a final Status.
*
* There are 2 loop indexes:- i which ranges through the bracesList:
* this may be reset l serves as a control to cope with invalid lists of
* StackObjects, preventing infinite loops within the SequenceChecker.
*/
int maximumIterations = this.bracesList.size() * this.bracesList.size();
int l = -1;
for (int i = 0; i < this.bracesList.size(); i++) {
l++;
StackObject so = bracesList.get(i);
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest("Processing bracesList(l,i)=(" + l + "," + i + ") of Type "
+ NodeType.stringFromType(so.getType()) + " with State (aktStatus) = " + aktStatus);
// LOGGER.finest("StackObject of Type="+so.getType());
LOGGER.finest("DataFlowNode @ line " + so.getDataFlowNode().getLine() + " and index="
+ so.getDataFlowNode().getIndex());
}
// Attempt to get to this StackObject's NodeType from the current
// State
aktStatus = this.aktStatus.step(so.getType());
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest("Transition aktStatus=" + aktStatus);
}
if (aktStatus == null) { // Not a valid Node from the current State
if (lookAhead) {
this.lastIndex = i - 1;
LOGGER.finer("aktStatus is NULL (lookAhead): Invalid transition");
LOGGER.exiting(this.getClass().getCanonicalName(), "run", false);
return false;
} else if (l > maximumIterations) {
// Cope with incorrect bracesList contents
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.severe("aktStatus is NULL: maximum Iterations exceeded, abort " + i);
}
LOGGER.exiting(this.getClass().getCanonicalName(), "run", false);
return false;
} else {
this.aktStatus = root;
this.firstIndex = i;
i--;
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest("aktStatus is NULL: Restarting search continue i==" + i + ", firstIndex="
+ this.firstIndex);
}
continue;
}
} else {
// This NodeType _is_ a valid transition from the previous State
if (aktStatus.isLastStep() && !aktStatus.hasMoreSteps()) {
// Terminal State
this.lastIndex = i;
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest("aktStatus is NOT NULL: lastStep reached and no moreSteps - firstIndex="
+ firstIndex + ", lastIndex=" + lastIndex);
}
LOGGER.exiting(this.getClass().getCanonicalName(), "run", false);
return false;
} else if (aktStatus.isLastStep() && aktStatus.hasMoreSteps()) {
lookAhead = true;
this.lastIndex = i;
LOGGER.finest("aktStatus is NOT NULL: set lookAhead on");
}
}
}
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finer("Completed search: firstIndex=" + firstIndex + ", lastIndex=" + lastIndex);
}
LOGGER.exiting(this.getClass().getCanonicalName(), "run", this.firstIndex == this.lastIndex);
return this.firstIndex == this.lastIndex;
}
public int getFirstIndex() {
return this.firstIndex;
}
public int getLastIndex() {
return this.lastIndex;
}
}