package cide.gast; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; public abstract class ASTNode implements IASTNode { public class StartPositionSorter implements Comparator<IASTNode> { public int compare(IASTNode o1, IASTNode o2) { if (o1.getStartPosition() < o2.getStartPosition()) return -1; if (o1.getStartPosition() > o2.getStartPosition()) return 1; return 0; } } protected final List<Property> properties; private Property parentProperty; public final IToken firstToken; public final IToken lastToken; private int offset; private int length; private int startLine; private int endLine; protected ASTNode(List<Property> properties, IToken firstToken, IToken lastToken) { this.properties = properties; this.firstToken = firstToken; this.lastToken = lastToken; this.offset = firstToken.getOffset(); this.length = calcLength(); this.startLine = firstToken.getLine(); this.endLine = lastToken.getLine(); for (Property p : properties) p.setParent(this); if (getLength() < 0) throw new RuntimeException( "ASTNode created with illegal length of " + getLength()); // assert getLength()>=0:"ASTNode created with illegal length of // "+getLength(); } protected ASTNode(Property[] properties, IToken ft, IToken lt) { this(Arrays.asList(properties), ft, lt); } public void accept(IASTVisitor visitor) { if (visitor.visit(this)) { List<IASTNode> children = getChildren(); for (IASTNode child : children) child.accept(visitor); } visitor.postVisit(this); } private List<IASTNode> childrenCache; /** * never call this method from outside this package */ public void notifyPropertyChanged(Property p) { childrenCache = null; } /** * cache children instead of calculating them for each tree-walk. cache is * invalidated by calling notifyPropertyChanged from the property * (hardcoded) * * @return */ public List<IASTNode> getChildren() { if (childrenCache == null) { List<IASTNode> children = new ArrayList<IASTNode>(); for (Property property : properties) for (IASTNode child : property.getChildren()) children.add(child); Collections.sort(children, new StartPositionSorter()); childrenCache = children; } return childrenCache; } public Property getProperty(String name) { for (Property property : properties) if (property.name.equals(name)) return property; return null; } public ISourceFile getRoot() { IASTNode parent = this; while (parent.getParent() != null) { parent = parent.getParent(); } return (ISourceFile) parent; } /** * ATTENTION: Never call from outside this package! Never call from a * Property! */ public void setParent(IASTNode parentNode, Property parentProperty) { this.parentProperty = parentProperty; if (this.parentProperty != null) this.parentProperty.setParent(parentNode); } public void setParentProperty(Property parentProperty) { this.parentProperty = parentProperty; } public IASTNode getParent() { if (parentProperty == null) return null; return parentProperty.getNode(); } public Property getLocationInParent() { return parentProperty; } private String idCache = null; public String getId() { if (idCache != null) return idCache; String id = ""; if (this.getParent() != null) id = this.getParent().getId() + "/" + parentProperty.getId(this); idCache = id; return id; } public void setId(String id) { idCache = id; } public List<Property> getProperties() { return Collections.unmodifiableList(properties); } public int getStartPosition() { return offset; } public int getLength() { return length; } private int calcLength() { if (lastToken.getOffset() == firstToken.getOffset()) return firstToken.getLength(); return lastToken.getOffset() + lastToken.getLength() - getStartPosition(); } public boolean isOptional() { if (parentProperty == null) return false; return parentProperty.canRemoveSubtree(this); } protected Property[] cloneProperties() { Property[] result = new Property[properties.size()]; int i = 0; for (Property p : properties) result[i++] = p.deepCopy(); return result; } public abstract IASTNode deepCopy(); public void remove() { if (!isOptional()) return; parentProperty.removeSubtree(this); } /** * Ersetzt diesen Knoten durch den gegebenen Knoten. * * ACHTUNG: Die �nderungen von Offsets, die durch ein Austauschen eines * Knotens passieren k�nnen, werden NICHT durchgef�hrt, so dass der AST * unbrauchbar werden k�nnte. Zur Zeit wird diese Methode nur auf einer * DeepCopy des AST ausgef�hrt, die dann gerendered wird. * * @param newNode */ public void replaceSubtreeWith(IASTNode newNode) { newNode.setParentProperty(this.parentProperty); newNode.getLocationInParent().replaceChild(this, newNode); } // public boolean hasReferenceTypes() { // return getReferenceTypes().length > 0; // } // // /** // * returns all supported reference types for this choice. // * // * @return array of reference types, possibly empty // */ // public IReferenceType[] getReferenceTypes() { // return new IReferenceType[0]; // } /** * prints the AST subtree to a string * * @return string representation of ASTNode */ public abstract String render(); public String getDisplayName() { return this.getClass().getSimpleName(); } @Override public boolean equals(Object obj) { if (obj instanceof IASTNode) return getStartPosition() == ((IASTNode) obj).getStartPosition() && getLength() == ((IASTNode) obj).getLength(); return super.equals(obj); } @Override public int hashCode() { return getStartPosition(); } public boolean isWrapper() { return getWrappee() != null; } /** * default implementation looks up whether PropertyWrapper.isWrapper is * used. may be overwritten by other parser generators if needed. * * the JDT bridge uses a different mechanism */ public IASTNode getWrappee() { Property lip = getLocationInParent(); if (!(lip instanceof PropertyWrapper)) return null; return ((PropertyWrapper<IASTNode, IASTNode>) lip).getWrappee(); } public int getStartLine() { return startLine; } public int getEndLine() { return endLine; } }