/******************************************************************************* * Copyright (c) 2010, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package melnorme.lang.tooling.ast; import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull; import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue; import melnorme.lang.tooling.ast.NodeData.CompleteNodeVisitor; import melnorme.lang.tooling.ast.NodeData.CreatedStatusNodeData; import melnorme.lang.tooling.ast.util.ASTChildrenCollector; import melnorme.lang.tooling.ast.util.ASTDirectChildrenVisitor; import melnorme.lang.tooling.ast.util.NodeElementUtil; import melnorme.lang.tooling.ast.util.NodeVector; import melnorme.lang.tooling.ast_actual.ASTNode; import melnorme.lang.tooling.common.ParserError; import melnorme.utilbox.collections.ArrayView; public abstract class CommonASTNode extends SourceElement implements IASTNode { public static final ASTNode[] NO_ELEMENTS = new ASTNode[0]; public CommonASTNode() { } @Override public final ASTNode asNode() { return (ASTNode) this; } /* ------------------------ Parent and children visitor ------------------------ */ @Override public ILanguageElement getOwnerElement() { return parent; } public final IModuleNode getModuleNode() { return NodeElementUtil.getMatchingParent(this, IModuleNode.class); } @Override public boolean isBuiltinElement() { return false; } /* =============== Children =============== */ /* ----------------- Visitor ----------------- */ /** Accept a visitor into this node. */ @Override public final void accept(IASTVisitor visitor) { assertNotNull(visitor); // begin with the generic pre-visit if(visitor.preVisit(asNode())) { visitChildren(visitor); } // end with the generic post-visit visitor.postVisit(asNode()); } public abstract void visitChildren(IASTVisitor visitor); public void visitDirectChildren(ASTDirectChildrenVisitor directChildrenVisitor) { accept(directChildrenVisitor); // This might be optimized in the future } @Override public boolean hasChildren() { CheckForChildrenVisitor checkForChildrenVisitor = new CheckForChildrenVisitor(); visitDirectChildren(checkForChildrenVisitor); return checkForChildrenVisitor.hasChildren; } public static final class CheckForChildrenVisitor extends ASTDirectChildrenVisitor { boolean hasChildren = false; @Override protected void geneticChildrenVisit(ASTNode child) { hasChildren = true; } } @Override public ASTNode[] getChildren() { return ASTChildrenCollector.getChildrenArray(asNode()); } // Utility methods /** Accepts the visitor on child. If child is null, nothing happens. */ public static void acceptVisitor(IASTVisitor visitor, IASTNode node) { if (node != null) { node.accept(visitor); } } /** Accepts the visitor on the children. If children is null, nothing happens. */ public static void acceptVisitor(IASTVisitor visitor, Iterable<? extends IASTNode> nodes) { if (nodes == null) return; for(IASTNode node : nodes) { acceptVisitor(visitor, node); } } /* ----------------- childen parenting utils ----------------- */ public static <T> ArrayView<T> nonNull(ArrayView<T> arrayView) { return arrayView != null ? arrayView : ArrayView.EMPTY_ARRAYVIEW.<T>upcastTypeParameter(); } /** Set the parent of the given node to the receiver. @return node */ protected <T extends ILanguageElement> T parentize(T node) { if (node != null) { node.setParent(asNode()); } return node; } /** Set the parent of the given collection to the receiver. @return collection */ protected final <C extends Iterable<? extends ILanguageElement>> C parentize(C collection) { parentizeCollection(collection, false, asNode()); return collection; } public static void parentizeCollection(Iterable<? extends ILanguageElement> coll, boolean allowNulls, CommonLanguageElement parent) { if (coll == null) { return; } for (ILanguageElement node : coll) { if(node != null) { node.setParent(parent); } else { assertTrue(allowNulls); } } } /* ----------------- cloning ----------------- */ @Override public final CommonASTNode cloneTree() { return finalizeClone(doCloneTree()); } protected CommonASTNode finalizeClone(final CommonASTNode clonedNode) { setParsedFromOther(clonedNode, this); assertNotNull(clonedNode); assertTrue(clonedNode != this); assertTrue(clonedNode.getLexicalParent() == null); assertTrue(clonedNode.getClass() == this.getClass()); assertTrue(clonedNode.isParsedStatus()); assertTrue(clonedNode.isSemanticReady() == false); return clonedNode; } protected static <T extends CommonASTNode> T setParsedFromOther(T node, T otherNode) { assertTrue(node.isSemanticReady() == false); // This assertion might not be necessary, we could clone without range info. assertTrue(otherNode.hasSourceRangeInfo()); node.setSourceRange(otherNode.getStartPos(), otherNode.getLength()); node.setParsedStatus(); return node; } protected abstract CommonASTNode doCloneTree(); @SuppressWarnings("unchecked") protected static <T extends IASTNode> T clone(T node) { if(node == null) return null; return (T) node.cloneTree(); } protected static <T extends IASTNode> NodeVector<T> clone(NodeVector<T> nodeListView) { if(nodeListView == null) return null; return nodeListView.cloneTree(); } /* ------------------------ Node data ------------------------ */ protected CreatedStatusNodeData getDataAtCreatedPhase() { assertTrue(getData() == NodeData.CREATED_STATUS); //return (ParsedNodeData) this.data; return NodeData.CREATED_STATUS; } public CommonASTNode setParsedStatus() { getDataAtCreatedPhase().setParsed(asNode()); return this; } public void setParsedStatusWithErrors(ParserError... errors) { getDataAtCreatedPhase().setParsedWithErrors(asNode(), errors); } public final boolean isParsedStatus() { return getData().isParsedStatus(); } /* =============== Analysis and semantics =============== */ @Override protected final void doSetElementSemanticReady() { accept(CompleteNodeVisitor.instance); } protected final void setSemanticReady_afterChildren() { assertTrue(isParsedStatus()); // Assert: children are already semanticReady setSemanticReady_afterChildren_do(); getData().setSemanticReady(this); } protected void setSemanticReady_afterChildren_do() { // Default implementation: do nothing } /* =============== STRING FUNCTIONS =============== */ @Override public final String toString() { StringBuilder sb = new StringBuilder(); sb.append(toStringClassName()); sb.append(isParsedStatus() ? "#" : ":" + getData()); sb.append(toStringAsCode()); sb.append("\n"); return sb.toString(); } }