package edu.kit.iti.formal.pse.worthwhile.util;

import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;

import edu.kit.iti.formal.pse.worthwhile.model.ast.ASTNode;

/**
 * A collection of utility functions to deal with Worthwhile ASTs.
 * 
 * @author Joachim
 * 
 */
public final class NodeHelper {

	/**
	 * Private constructor to prevent instantiation.
	 */
	private NodeHelper() {

	}

	/**
	 * Returns the 1-based line number of a node in the source file.
	 * 
	 * @param node
	 *                The node to locate in the source file.
	 * @return The 1-based line number of the node in the source file.
	 */
	public static int getLine(final ASTNode node) {
		final ICompositeNode actualNode = NodeModelUtils.findActualNodeFor(node);
		if (actualNode != null) {
			return actualNode.getStartLine();
		} else {
			return -1;
		}
	}

	/**
	 * Returns the offset of a node in the entire document.
	 * 
	 * @param node
	 *                The node to locate in the source file.
	 * @return The offset of the node in the document
	 */
	public static int getOffset(final ASTNode node) {
		final ICompositeNode actualNode = NodeModelUtils.findActualNodeFor(node);
		if (actualNode != null) {
			return actualNode.getOffset();
		} else {
			return -1;
		}
	}

	/**
	 * Returns the length of a node in the source file.
	 * 
	 * @param node
	 *                The node whose length to get.
	 * @return The length of the node in the source file.
	 */
	public static int getLength(final ASTNode node) {
		final ICompositeNode actualNode = NodeModelUtils.findActualNodeFor(node);
		if (actualNode != null) {
			return actualNode.getLength();
		} else {
			return -1;
		}
	}
	
	/**
	 * Returns the text of an AST node.
	 * 
	 * @param node
	 *                The AST node to get the text of.
	 * @return The text contained in the AST node.
	 */
	public static String getText(final ASTNode node) {
		final ICompositeNode actualNode = NodeModelUtils.findActualNodeFor(node);
		if (actualNode != null) {
			return actualNode.getText();
		} else {
			return "";
		}
	}

	/**
	 * Returns whether the node {@code node} is a direct ancestor in the AST of the node {@code child}.
	 * 
	 * @param node
	 *                The node for which to check whether it is an ancestor of {@code child}
	 * @param child
	 *                The child node for which to check for ancestors.
	 * @return Whether the node {@code node} is a direct ancestor in the AST of the node {@code child}.
	 */
	public static boolean isAncestor(final ASTNode node, final ASTNode child) {
		ASTNode current = child;

		while (current != null && !(node.equals(current))) {
			current = (ASTNode) current.eContainer();
		}

		return node.equals(current);
	}

}

@@ -71,28 +71,208 @@
 ext.
-nodemodel.IComposite
+GrammarUtil;%0Aimport org.eclipse.xtext.impl.RuleCallImpl;%0Aimport org.eclipse.xtext.nodemodel.ICompositeNode;%0Aimport org.eclipse.xtext.nodemodel.INode;%0Aimport org.eclipse.xtext.nodemodel.impl.HiddenLeaf
 Node
@@ -1830,37 +1830,696 @@
 %0A%09%09%09
-return actualNode.getLength()
+int len = actualNode.getLength();%0A%09%09%09int subtractLen = 0;%0A%0A%09%09%09// Subtract length of hidden nodes that appear at the end of a node. They seem not to be%0A%09%09%09// subtracted from the length although INode.getLength() claims%0A%09%09%09// %22Returns the length of this node excluding hidden tokens.%22%0A%09%09%09for (INode child : actualNode.getChildren()) %7B%0A%09%09%09%09if (child instanceof HiddenLeafNode%0A%09%09%09%09                %7C%7C (GrammarUtil.isTerminalRuleCall(child.getGrammarElement()) && ((RuleCallImpl) child%0A%09%09%09%09                                .getGrammarElement()).getRule().getName().equals(%22NL%22))) %7B%0A%09%09%09%09%09subtractLen += child.getLength();%0A%09%09%09%09%7D else %7B%0A%09%09%09%09%09subtractLen = 0;%0A%09%09%09%09%7D%0A%09%09%09%7D%0A%0A%09%09%09return len - subtractLen
 ;%0A%09%09
@@ -2548,17 +2548,16 @@
 %0A%09%09%7D%0A%09%7D%0A
-%09
 %0A%09/**%0A%09 

Fix #84: Proper handling of comments and newlines in node helper