/* Copyright (C) 2013 Raquel Pau and Albert Coroleu. Walkmod is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Walkmod is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Walkmod. If not, see <http://www.gnu.org/licenses/>.*/ package org.walkmod.javalang.ast; import java.io.Serializable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.walkmod.javalang.visitors.DumpVisitor; import org.walkmod.javalang.visitors.EqualsVisitor; import org.walkmod.javalang.visitors.GenericVisitor; import org.walkmod.javalang.visitors.VoidVisitor; import org.walkmod.modelchecker.ConstrainedElement; import org.walkmod.modelchecker.Constraint; /** * Abstract class for all nodes of the AST. * * @author Julio Vilmar Gesser */ public abstract class Node implements Serializable, Cloneable, ConstrainedElement { private int beginLine; private int beginColumn; private int endLine; private int endColumn; /** * This attribute can store additional information. */ private Object data; private Node parentNode; private List<Constraint> constraints; private List<Constraint> constraintsAux; public Node() { } public void setConstraints(List<Constraint> constraints) { if (this.constraints != constraints) { this.constraints = constraints; if (this.constraints != null) { List<Node> children = getChildren(); if (children != null) { for (Node child : children) { child.setConstraints(constraints); } } } } } public boolean remove() { Node parent = getParentNode(); if (parent != null) { return parent.removeChild(this); } return false; } public abstract boolean removeChild(Node child); public abstract List<Node> getChildren(); @Override public List<Constraint> getConstraints() { return constraints; } public Node(int beginLine, int beginColumn, int endLine, int endColumn) { this.beginLine = beginLine; this.beginColumn = beginColumn; this.endLine = endLine; this.endColumn = endColumn; } /** * Accept method for visitor support. * * @param <R> * the type the return value of the visitor * @param <A> * the type the argument passed for the visitor * @param v * the visitor implementation * @param arg * any value relevant for the visitor * @return the result of the visit */ public abstract <R, A> R accept(GenericVisitor<R, A> v, A arg); /** * Accept method for visitor support. * * @param <A> * the type the argument passed for the visitor * @param v * the visitor implementation * @param arg * any value relevant for the visitor */ public abstract <A> void accept(VoidVisitor<A> v, A arg); /** * Return the begin column of this node. * * @return the begin column of this node */ public final int getBeginColumn() { return beginColumn; } /** * Return the begin line of this node. * * @return the begin line of this node */ public final int getBeginLine() { return beginLine; } /** * Use this to retrieve additional information associated to this node. * * @return additional information associated to this node. */ public final Object getData() { return data; } /** * Return the end column of this node. * * @return the end column of this node */ public final int getEndColumn() { return endColumn; } /** * Return the end line of this node. * * @return the end line of this node */ public final int getEndLine() { return endLine; } /** * Sets the begin column of this node. * * @param beginColumn * the begin column of this node */ public final void setBeginColumn(int beginColumn) { this.beginColumn = beginColumn; } /** * Sets the begin line of this node. * * @param beginLine * the begin line of this node */ public final void setBeginLine(int beginLine) { this.beginLine = beginLine; } /** * Use this to store additional information to this node. * * @param data * additional information to this node. */ public final void setData(Object data) { this.data = data; } /** * Sets the end column of this node. * * @param endColumn * the end column of this node */ public final void setEndColumn(int endColumn) { this.endColumn = endColumn; } /** * Sets the end line of this node. * * @param endLine * the end line of this node */ public final void setEndLine(int endLine) { this.endLine = endLine; } public void enableConstraints() { if (constraintsAux != null && constraints != null) { constraints.addAll(constraintsAux); } } public void diableConstraints() { if (constraints != null) { constraintsAux = new LinkedList<Constraint>(constraints); constraints.clear(); } } /** * Return the String representation of this node. * * @return the String representation of this node */ @Override public final String toString() { enableConstraints(); DumpVisitor visitor = new DumpVisitor(); accept(visitor, null); String aux = visitor.getSource(); diableConstraints(); return aux; } @Override public final int hashCode() { return toString().hashCode(); } @Override public boolean equals(Object obj) { return EqualsVisitor.equals(this, (Node) obj); } /** * Return if the node has not been retrieved from any input because its end column and begin line * is 0. * * @return if it is a new node. */ public boolean isNewNode() { return (0 == getEndLine() && 0 == getEndColumn() && getBeginLine() == 0); } /** * Return if this node contains another node according their line numbers and columns * * @param node2 * the probably contained node * @return if this node contains the argument as a child node by its position. */ public boolean contains(Node node2) { if ((getBeginLine() < node2.getBeginLine()) || ((getBeginLine() == node2.getBeginLine()) && getBeginColumn() <= node2.getBeginColumn())) { if (getEndLine() > node2.getEndLine()) { return true; } else if ((getEndLine() == node2.getEndLine()) && getEndColumn() >= node2.getEndColumn()) { return true; } } return false; } /** * Return if this node has the same columns and lines than another one. * * @param node2 * the node to compare * @return if this node has the same columns and lines than another one. */ public boolean isInEqualLocation(Node node2) { if (!isNewNode() && !node2.isNewNode()) { return getBeginLine() == node2.getBeginLine() && getBeginColumn() == node2.getBeginColumn() && getEndLine() == node2.getEndLine() && getEndColumn() == node2.getEndColumn(); } return false; } /** * Return if this node is previous than another one according their line and column numbers * * @param node * to compare * @return if this node is previous than another one according their line and */ public boolean isPreviousThan(Node node) { if (getEndLine() < node.getBeginLine()) { return true; } else if ((getEndLine() == node.getBeginLine()) && (getEndColumn() <= node.getBeginColumn())) { return true; } return false; } public String getPrettySource(char indentationChar, int indentationLevel, int indentationSize) { return getPrettySource(indentationChar, indentationLevel, indentationSize, null); } public String getPrettySource(char indentationChar, int indentationLevel, int indentationSize, List<Comment> comments) { DumpVisitor visitor = new DumpVisitor(); visitor.setIndentationChar(indentationChar); visitor.setIndentationLevel(indentationLevel); visitor.setIndentationSize(indentationSize); if (comments != null) { visitor.setComments(new LinkedList<Comment>(comments)); } accept(visitor, null); return visitor.getSource(); } public Node getParentNode() { return parentNode; } private void setParentNode(Node parent) { this.parentNode = parent; } public boolean isAncestorOf(Node other) { Node parent = other.getParentNode(); while (parent != null) { if (parent == this) { return true; } parent = parent.getParentNode(); } return false; } public Node getCommonAncestor(Node other) { Node parent = getParentNode(); while (parent != null) { Node parent2 = other; while (parent2 != null) { if (parent == parent2) { return parent; } parent2 = parent2.getParentNode(); } parent = parent.getParentNode(); } return null; } protected void setAsParentNodeOf(List<? extends Node> childNodes) { if (childNodes != null) { Iterator<? extends Node> it = childNodes.iterator(); while (it.hasNext()) { Node current = it.next(); current.setParentNode(this); } } } protected void setAsParentNodeOf(Node childNode) { if (childNode != null) { childNode.setParentNode(this); } } protected void updateReferences(Object o) { if (o instanceof SymbolReference) { SymbolReference sr = (SymbolReference) o; SymbolDefinition sd = sr.getSymbolDefinition(); if (sd != null) { List<SymbolReference> usages = sd.getUsages(); if (usages != null) { Iterator<SymbolReference> it = usages.iterator(); boolean found = false; while (it.hasNext() && !found) { SymbolReference current = it.next(); if (current == o) { it.remove(); found = true; } } } } } if (o instanceof SymbolDefinition) { SymbolDefinition sd = (SymbolDefinition) o; List<SymbolReference> usages = sd.getUsages(); if (usages != null) { Iterator<SymbolReference> it = usages.iterator(); while (it.hasNext()) { SymbolReference current = it.next(); current.setSymbolDefinition(null); it.remove(); } List<SymbolReference> bodyRefs = sd.getBodyReferences(); if (bodyRefs != null) { for (SymbolReference sr : bodyRefs) { updateReferences(sr); } } } } } @SuppressWarnings("unchecked") protected boolean replaceChildNodeInList(Node oldChild, Node newChild, List list) { if (list != null) { Iterator<?> it = list.iterator(); boolean updated = false; int i = 0; while (it.hasNext() && !updated) { Object current = it.next(); if (current == oldChild) { it.remove(); updateReferences(current); updated = true; } if (!updated) { i++; } } if (updated) { Node parent = oldChild.getParentNode(); if (parent != null) { parent.setAsParentNodeOf(newChild); } list.add(i, newChild); } return updated; } return false; } public abstract boolean replaceChildNode(Node oldChild, Node newChild); protected <T extends Node> List<T> clone(List<T> original) throws CloneNotSupportedException { List<T> aux = null; if (original != null) { aux = new LinkedList<T>(); for (T node : original) { aux.add(clone(node)); } } return aux; } protected <T extends Node> T clone(T node) throws CloneNotSupportedException { if (node == null) { return null; } return (T) node.clone(); } @Override public abstract Object clone() throws CloneNotSupportedException; @Override public boolean check() { if ((constraints == null) || constraints.isEmpty()) { return true; } else { Iterator<Constraint> it = constraints.iterator(); boolean isConstrained = false; while (it.hasNext() && !isConstrained) { Constraint c = it.next(); isConstrained = c.isConstrained(this); } return !isConstrained; } } }