/**
* 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.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sourceforge.pmd.lang.DataFlowHandler;
import net.sourceforge.pmd.lang.ast.Node;
/**
* Structure contains only raw data. A set of nodes which represent a
* data flow and 2 stacks to link the nodes to each other.
*
* @author raik
*/
public class Structure {
private static final Logger LOGGER = Logger.getLogger(Structure.class.getName());
private final DataFlowHandler dataFlowHandler;
private List<DataFlowNode> dataFlow = new ArrayList<>();
private Stack<StackObject> braceStack = new Stack<>();
private Stack<StackObject> continueBreakReturnStack = new Stack<>();
public Structure(DataFlowHandler dataFlowHandler) {
this.dataFlowHandler = dataFlowHandler;
}
/**
* This class encapsulates the access to the DataFlowNode class. Is this
* worthwhile? TODO I think it's too confusing to have the DataFlowNode
* constructor add the created instance to the List. I think it'd be clearer
* if we did that more "procedurally", i.e., create the object, then add it
* to the list.
*/
public DataFlowNode createNewNode(Node node) {
return dataFlowHandler.createDataFlowNode(dataFlow, node);
}
public DataFlowNode createStartNode(int line) {
return new StartOrEndDataFlowNode(this.dataFlow, line, true);
}
public DataFlowNode createEndNode(int line) {
return new StartOrEndDataFlowNode(this.dataFlow, line, false);
}
public DataFlowNode getLast() {
return this.dataFlow.get(this.dataFlow.size() - 1);
}
public DataFlowNode getFirst() {
return this.dataFlow.get(0);
}
// ----------------------------------------------------------------------------
// STACK FUNCTIONS
/**
* The braceStack contains all nodes which are important to link the data
* flow nodes. The cbrStack contains continue, break, and return nodes.
* There are 2 Stacks because the have to process differently.
*/
public void pushOnStack(int type, DataFlowNode node) {
StackObject obj = new StackObject(type, node);
if (type == NodeType.RETURN_STATEMENT || type == NodeType.BREAK_STATEMENT || type == NodeType.CONTINUE_STATEMENT
|| type == NodeType.THROW_STATEMENT) {
// ugly solution - stores the type information in two ways
continueBreakReturnStack.push(obj);
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest("continueBreakReturnStack: line " + node.getNode().getBeginLine() + ", column "
+ node.getNode().getBeginColumn() + " - " + node.toString());
}
} else {
braceStack.push(obj);
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest("braceStack: line " + node.getNode().getBeginLine() + ", column "
+ node.getNode().getBeginColumn() + " - " + node.toString());
}
}
node.setType(type);
}
public List<StackObject> getBraceStack() {
return braceStack;
}
public List<StackObject> getContinueBreakReturnStack() {
return continueBreakReturnStack;
}
/**
*
* @return formatted dump of the DFA Structure's
*/
public String dump() {
StringBuilder stringDump = new StringBuilder(120).append("Data Flow Analysis Structure:\n")
.append(" Edge Nodes (ContinueBraceReturn) :");
for (StackObject stackObject : continueBreakReturnStack) {
stringDump.append("\nCBR => ").append(stackObject.toString());
}
stringDump.append("\n Scope Nodes:");
for (StackObject stackObject : braceStack) {
stringDump.append("\nBraces => ").append(stackObject.toString());
}
return stringDump.toString();
}
}