iterator = node.getAsTreeIterable().iterator();
- while (iterator.hasNext()) {
- INode child = iterator.next();
- EObject grammarElement = child.getGrammarElement();
- if (grammarElement != null) {
- if (grammarElement instanceof Action) {
- Action action = (Action) grammarElement;
- if (child.getSemanticElement() == semanticElement) {
- child = iterator.next();
- if (featureName.equals(action.getFeature())) {
- result.add(child);
- }
- } else {
- // navigate the action's left side (first child) until we find an assignment (a rule call)
- // the assignment will tell us about the feature to which we assigned
- // the semantic object that has been created by the action
- INode firstChild = ((ICompositeNode) child).getFirstChild();
- while (firstChild.getGrammarElement() instanceof Action) {
- firstChild = ((ICompositeNode) firstChild).getFirstChild();
- }
- EObject firstChildGrammarElement = firstChild.getGrammarElement();
- Assignment assignment = GrammarUtil.containingAssignment(firstChildGrammarElement);
- if (assignment != null && featureName.equals(assignment.getFeature())) {
- result.add(child);
- }
- }
- iterator.prune();
- } else if (child != node) {
- Assignment assignment = GrammarUtil.containingAssignment(grammarElement);
- if (assignment != null) {
- if (featureName.equals(assignment.getFeature())) {
- result.add(child);
- }
- iterator.prune();
- }
- }
- }
- }
- return result;
- }
-
/**
* Returns the node that covers all assigned values of the given object. It handles the semantics of {@link Action
* actions} and {@link RuleCall unassigned rule calls}. The returned node will include unassigned surrounding leafs,
@@ -241,14 +152,7 @@ private static List findNodesForFeature(EObject semanticElement, INode no
public static ICompositeNode findActualNodeFor(/* @Nullable */ EObject semanticObject) {
ICompositeNode node = getNode(semanticObject);
if (node != null) {
- while(GrammarUtil.containingAssignment(node.getGrammarElement()) == null) {
- ICompositeNode parent = node.getParent();
- if (parent != null && !parent.hasDirectSemanticElement() && !GrammarUtil.isEObjectFragmentRuleCall(parent.getGrammarElement())) {
- node = parent;
- } else {
- break;
- }
- }
+ return node.utils().findActualNodeEnclosing(node);
}
return node;
}
@@ -264,75 +168,7 @@ public static ICompositeNode findActualNodeFor(/* @Nullable */ EObject semanticO
public static EObject findActualSemanticObjectFor(/* @Nullable */ INode node) {
if (node == null)
return null;
- if (node.hasDirectSemanticElement())
- return node.getSemanticElement();
- EObject grammarElement = node.getGrammarElement();
- ICompositeNode parent = node.getParent();
- if (grammarElement == null)
- return findActualSemanticObjectFor(parent);
- Assignment assignment = GrammarUtil.containingAssignment(grammarElement);
- if (assignment != null) {
- if (GrammarUtil.isEObjectFragmentRule(GrammarUtil.containingRule(assignment))) {
- EObject result = findActualSemanticObjectInChildren(node, grammarElement);
- if (result != null)
- return result;
- }
- if (parent.hasDirectSemanticElement())
- return findActualSemanticObjectFor(parent);
- INode sibling = parent.getFirstChild();
- while(!sibling.equals(node)) {
- EObject siblingGrammarElement = sibling.getGrammarElement();
- if (siblingGrammarElement != null && GrammarUtil.containingAssignment(siblingGrammarElement) == null) {
- if (GrammarUtil.isEObjectRuleCall(siblingGrammarElement)) {
- return findActualSemanticObjectFor(sibling);
- }
- if (siblingGrammarElement.eClass() == XtextPackage.Literals.ACTION) {
- return findActualSemanticObjectFor(sibling);
- }
- }
- sibling = sibling.getNextSibling();
- }
- } else if (!GrammarUtil.isEObjectFragmentRuleCall(grammarElement)) {
- EObject result = findActualSemanticObjectInChildren(node, grammarElement);
- if (result != null)
- return result;
- }
- return findActualSemanticObjectFor(parent);
- }
-
- /* @Nullable */
- private static EObject findActualSemanticObjectInChildren(/* @NonNull */ INode node, /* @Nullable */ EObject grammarElement) {
- if (node.hasDirectSemanticElement())
- return node.getSemanticElement();
- AbstractRule rule = null;
- if (grammarElement instanceof RuleCall) {
- rule = ((RuleCall) grammarElement).getRule();
- } else if (grammarElement instanceof AbstractRule) {
- rule = (AbstractRule) grammarElement;
- }
- if (rule instanceof ParserRule && !GrammarUtil.isDatatypeRule(rule)) {
- if (node instanceof ICompositeNode) {
- for (INode child : ((ICompositeNode) node).getChildren()) {
- if (child instanceof ICompositeNode) {
- EObject childGrammarElement = child.getGrammarElement();
- if (childGrammarElement instanceof Action) {
- EObject result = findActualSemanticObjectInChildren(child, childGrammarElement);
- if (result != null)
- return result;
- } else if (childGrammarElement instanceof RuleCall) {
- RuleCall childRuleCall = (RuleCall) childGrammarElement;
- if (childRuleCall.getRule() instanceof ParserRule
- && !GrammarUtil.isDatatypeRule(childRuleCall.getRule())) {
- EObject result = findActualSemanticObjectInChildren(child, childRuleCall);
- if (result != null)
- return result;
- }
- }
- }
- }
- }
- }
- return null;
+ return node.utils().findActualSemanticObjectFor(node);
}
/**
@@ -341,60 +177,10 @@ private static EObject findActualSemanticObjectInChildren(/* @NonNull */ INode n
* @return a debug string for the given node.
*/
public static String compactDump(INode node, boolean showHidden) {
- StringBuilder result = new StringBuilder();
- try {
- compactDump(node, showHidden, "", result);
- } catch (IOException e) {
- return e.getMessage();
- }
- return result.toString();
- }
-
- private static void compactDump(INode node, boolean showHidden, String prefix, Appendable result)
- throws IOException {
- if (!showHidden && node instanceof ILeafNode && ((ILeafNode) node).isHidden())
- return;
- if (prefix.length() != 0) {
- result.append("\n");
- result.append(prefix);
- }
- if (node instanceof ICompositeNode) {
- if (node.getGrammarElement() != null)
- result.append(new GrammarElementTitleSwitch().showAssignments().doSwitch(node.getGrammarElement()));
- else
- result.append("(unknown)");
- String newPrefix = prefix + " ";
- result.append(" {");
- BidiIterator children = ((ICompositeNode) node).getChildren().iterator();
- while (children.hasNext()) {
- INode child = children.next();
- compactDump(child, showHidden, newPrefix, result);
- }
- result.append("\n");
- result.append(prefix);
- result.append("}");
- SyntaxErrorMessage error = node.getSyntaxErrorMessage();
- if (error != null)
- result.append(" SyntaxError: [" + error.getIssueCode() + "] " + error.getMessage());
- } else if (node instanceof ILeafNode) {
- if (((ILeafNode) node).isHidden())
- result.append("hidden ");
- if (node.getGrammarElement() != null)
- result.append(new GrammarElementTitleSwitch().showAssignments().doSwitch(node.getGrammarElement()));
- else
- result.append("(unknown)");
- result.append(" => '");
- result.append(node.getText());
- result.append("'");
- SyntaxErrorMessage error = node.getSyntaxErrorMessage();
- if (error != null)
- result.append(" SyntaxError: [" + error.getIssueCode() + "] " + error.getMessage());
- } else if (node == null) {
- result.append("(null)");
- } else {
- result.append("unknown type ");
- result.append(node.getClass().getName());
+ if (node == null) {
+ return "(null)";
}
+ return node.utils().compactDump(node, showHidden);
}
/**
@@ -411,41 +197,323 @@ private static void compactDump(INode node, boolean showHidden, String prefix, A
*
*/
public static String getTokenText(INode node) {
- if (node instanceof ILeafNode)
- return ((ILeafNode) node).getText();
- else {
- StringBuilder builder = new StringBuilder(Math.max(node.getTotalLength(), 1));
- boolean hiddenSeen = false;
- for (ILeafNode leaf : node.getLeafNodes()) {
- if (!leaf.isHidden()) {
- if (hiddenSeen && builder.length() > 0)
- builder.append(' ');
- builder.append(leaf.getText());
- hiddenSeen = false;
+ return node.utils().getTokenText(node);
+ }
+
+ public static ParserRule getEntryParserRule(INode node) {
+ return node.utils().getEntryParserRule(node);
+ }
+
+ /**
+ * Internal abstraction of the {@link NodeModelUtils} that allows to customize the functionality of the
+ * {@link NodeModelUtils} even though it's statically available.
+ *
+ * @see INode#utils()
+ * @see INodeReference
+ * @see NodeModelBuilder
+ *
+ * @since 2.34
+ */
+ public interface Implementation {
+
+ /**
+ * The singleton default instance that implements {@link NodeModelUtils.Implementation}.
+ * @since 2.34
+ */
+ enum Default implements Implementation {
+ INSTANCE;
+ }
+
+ default /* @Nullable */ ILeafNode findLeafNodeAtOffset(INode node, int leafNodeOffset) {
+ INode localNode = node;
+ while (!(localNode instanceof AbstractNode)) {
+ localNode = localNode.getParent();
+ }
+ int offset = localNode.getTotalOffset();
+ int length = localNode.getTotalLength();
+ BidiTreeIterator iterator = ((AbstractNode) localNode).basicIterator();
+ if (leafNodeOffset > (offset + length) / 2) {
+ while (iterator.hasPrevious()) {
+ AbstractNode previous = iterator.previous();
+ int previousOffset = previous.getTotalOffset();
+ int previousLength = previous.getTotalLength();
+ if (!intersects(previousOffset, previousLength, leafNodeOffset)) {
+ if (previousOffset + previousLength <= leafNodeOffset) {
+ return null;
+ }
+ iterator.prune();
+ } else {
+ if (previous instanceof ILeafNode)
+ return (ILeafNode) previous;
+ }
+ }
+ } else {
+ while (iterator.hasNext()) {
+ AbstractNode next = iterator.next();
+ int nextOffset = next.getTotalOffset();
+ int nextLength = next.getTotalLength();
+ if (!intersects(nextOffset, nextLength, leafNodeOffset)) {
+ if (nextOffset > leafNodeOffset) {
+ return null;
+ }
+ iterator.prune();
+ } else {
+ if (next instanceof ILeafNode)
+ return (ILeafNode) next;
+ }
+ }
+ }
+ return null;
+ }
+
+ default LineAndColumn getLineAndColumn(INode anyNode, int documentOffset) {
+ // special treatment for inconsistent nodes such as SyntheticLinkingLeafNode
+ if (anyNode.getParent() == null && !(anyNode instanceof RootNode)) {
+ return LineAndColumn.from(1, 1);
+ }
+ return internalGetLineAndColumn(anyNode, documentOffset);
+ }
+
+ private boolean intersects(int offset, int length, int lookupOffset) {
+ if (offset <= lookupOffset && offset + length > lookupOffset)
+ return true;
+ return false;
+ }
+
+ default List findNodesForFeature(EObject semanticElement, INode node, EStructuralFeature structuralFeature) {
+ if (structuralFeature == null) {
+ return Collections.emptyList();
+ }
+
+ List result = Lists.newArrayList();
+ String featureName = structuralFeature.getName();
+ BidiTreeIterator iterator = node.getAsTreeIterable().iterator();
+ while (iterator.hasNext()) {
+ INode child = iterator.next();
+ EObject grammarElement = child.getGrammarElement();
+ if (grammarElement != null) {
+ if (grammarElement instanceof Action) {
+ Action action = (Action) grammarElement;
+ if (child.getSemanticElement() == semanticElement) {
+ child = iterator.next();
+ if (featureName.equals(action.getFeature())) {
+ result.add(child);
+ }
+ } else {
+ // navigate the action's left side (first child) until we find an assignment (a rule call)
+ // the assignment will tell us about the feature to which we assigned
+ // the semantic object that has been created by the action
+ INode firstChild = ((ICompositeNode) child).getFirstChild();
+ while (firstChild.getGrammarElement() instanceof Action) {
+ firstChild = ((ICompositeNode) firstChild).getFirstChild();
+ }
+ EObject firstChildGrammarElement = firstChild.getGrammarElement();
+ Assignment assignment = GrammarUtil.containingAssignment(firstChildGrammarElement);
+ if (assignment != null && featureName.equals(assignment.getFeature())) {
+ result.add(child);
+ }
+ }
+ iterator.prune();
+ } else if (child != node) {
+ Assignment assignment = GrammarUtil.containingAssignment(grammarElement);
+ if (assignment != null) {
+ if (featureName.equals(assignment.getFeature())) {
+ result.add(child);
+ }
+ iterator.prune();
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ default /* @Nullable */ ICompositeNode findActualNodeEnclosing(ICompositeNode node) {
+ while (GrammarUtil.containingAssignment(node.getGrammarElement()) == null) {
+ ICompositeNode parent = node.getParent();
+ if (parent != null && !parent.hasDirectSemanticElement()
+ && !GrammarUtil.isEObjectFragmentRuleCall(parent.getGrammarElement())) {
+ node = parent;
} else {
- hiddenSeen = true;
+ break;
}
}
- return builder.toString();
+ return node;
}
- }
-
- public static ParserRule getEntryParserRule(INode node) {
- ICompositeNode root = node.getRootNode();
- EObject ge1 = root.getGrammarElement();
- if (ge1 instanceof ParserRule) {
- return (ParserRule) ge1;
- } else if (ge1 instanceof Action) {
- INode firstChild = root.getFirstChild();
- while (firstChild.getGrammarElement() instanceof Action && firstChild instanceof CompositeNode) {
- firstChild = ((CompositeNode)firstChild).getFirstChild();
+
+ default /* @Nullable */ EObject findActualSemanticObjectFor(INode node) {
+ if (node == null) {
+ return null;
}
- EObject ge2 = firstChild.getGrammarElement();
- if (ge2 instanceof ParserRule) {
- return (ParserRule) ge2;
+ if (node.hasDirectSemanticElement()) {
+ return node.getSemanticElement();
}
+ EObject grammarElement = node.getGrammarElement();
+ ICompositeNode parent = node.getParent();
+ if (grammarElement == null)
+ return findActualSemanticObjectFor(parent);
+ Assignment assignment = GrammarUtil.containingAssignment(grammarElement);
+ if (assignment != null) {
+ if (GrammarUtil.isEObjectFragmentRule(GrammarUtil.containingRule(assignment))) {
+ EObject result = findActualSemanticObjectInChildren(node, grammarElement);
+ if (result != null)
+ return result;
+ }
+ if (parent.hasDirectSemanticElement())
+ return findActualSemanticObjectFor(parent);
+ INode sibling = parent.getFirstChild();
+ while (!sibling.equals(node)) {
+ EObject siblingGrammarElement = sibling.getGrammarElement();
+ if (siblingGrammarElement != null && GrammarUtil.containingAssignment(siblingGrammarElement) == null) {
+ if (GrammarUtil.isEObjectRuleCall(siblingGrammarElement)) {
+ return findActualSemanticObjectFor(sibling);
+ }
+ if (siblingGrammarElement.eClass() == XtextPackage.Literals.ACTION) {
+ return findActualSemanticObjectFor(sibling);
+ }
+ }
+ sibling = sibling.getNextSibling();
+ }
+ } else if (!GrammarUtil.isEObjectFragmentRuleCall(grammarElement)) {
+ EObject result = findActualSemanticObjectInChildren(node, grammarElement);
+ if (result != null)
+ return result;
+ }
+ return findActualSemanticObjectFor(parent);
+ }
+
+ private /* @Nullable */ EObject findActualSemanticObjectInChildren(INode node, /* @Nullable */ EObject grammarElement) {
+ if (node.hasDirectSemanticElement())
+ return node.getSemanticElement();
+ AbstractRule rule = null;
+ if (grammarElement instanceof RuleCall) {
+ rule = ((RuleCall) grammarElement).getRule();
+ } else if (grammarElement instanceof AbstractRule) {
+ rule = (AbstractRule) grammarElement;
+ }
+ if (rule instanceof ParserRule && !GrammarUtil.isDatatypeRule(rule)) {
+ if (node instanceof ICompositeNode) {
+ for (INode child : ((ICompositeNode) node).getChildren()) {
+ if (child instanceof ICompositeNode) {
+ EObject childGrammarElement = child.getGrammarElement();
+ if (childGrammarElement instanceof Action) {
+ EObject result = findActualSemanticObjectInChildren(child, childGrammarElement);
+ if (result != null)
+ return result;
+ } else if (childGrammarElement instanceof RuleCall) {
+ RuleCall childRuleCall = (RuleCall) childGrammarElement;
+ if (childRuleCall.getRule() instanceof ParserRule
+ && !GrammarUtil.isDatatypeRule(childRuleCall.getRule())) {
+ EObject result = findActualSemanticObjectInChildren(child, childRuleCall);
+ if (result != null)
+ return result;
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Creates a string representation of the given node. Useful for debugging.
+ *
+ * @return a debug string for the given node.
+ */
+ default String compactDump(INode node, boolean showHidden) {
+ StringBuilder result = new StringBuilder();
+ try {
+ compactDump(node, showHidden, "", result);
+ } catch (IOException e) {
+ return e.getMessage();
+ }
+ return result.toString();
+ }
+
+ private void compactDump(INode node, boolean showHidden, String prefix, Appendable result) throws IOException {
+ if (!showHidden && node instanceof ILeafNode && ((ILeafNode) node).isHidden())
+ return;
+ if (prefix.length() != 0) {
+ result.append("\n");
+ result.append(prefix);
+ }
+ if (node instanceof ICompositeNode) {
+ if (node.getGrammarElement() != null)
+ result.append(new GrammarElementTitleSwitch().showAssignments().doSwitch(node.getGrammarElement()));
+ else
+ result.append("(unknown)");
+ String newPrefix = prefix + " ";
+ result.append(" {");
+ BidiIterator children = ((ICompositeNode) node).getChildren().iterator();
+ while (children.hasNext()) {
+ INode child = children.next();
+ compactDump(child, showHidden, newPrefix, result);
+ }
+ result.append("\n");
+ result.append(prefix);
+ result.append("}");
+ SyntaxErrorMessage error = node.getSyntaxErrorMessage();
+ if (error != null)
+ result.append(" SyntaxError: [" + error.getIssueCode() + "] " + error.getMessage());
+ } else if (node instanceof ILeafNode) {
+ if (((ILeafNode) node).isHidden())
+ result.append("hidden ");
+ if (node.getGrammarElement() != null)
+ result.append(new GrammarElementTitleSwitch().showAssignments().doSwitch(node.getGrammarElement()));
+ else
+ result.append("(unknown)");
+ result.append(" => '");
+ result.append(node.getText());
+ result.append("'");
+ SyntaxErrorMessage error = node.getSyntaxErrorMessage();
+ if (error != null)
+ result.append(" SyntaxError: [" + error.getIssueCode() + "] " + error.getMessage());
+ } else if (node == null) {
+ result.append("(null)");
+ } else {
+ result.append("unknown type ");
+ result.append(node.getClass().getName());
+ }
+ }
+
+ default String getTokenText(INode node) {
+ if (node instanceof ILeafNode)
+ return ((ILeafNode) node).getText();
+ else {
+ StringBuilder builder = new StringBuilder(Math.max(node.getTotalLength(), 1));
+ boolean hiddenSeen = false;
+ for (ILeafNode leaf : node.getLeafNodes()) {
+ if (!leaf.isHidden()) {
+ if (hiddenSeen && builder.length() > 0)
+ builder.append(' ');
+ builder.append(leaf.getText());
+ hiddenSeen = false;
+ } else {
+ hiddenSeen = true;
+ }
+ }
+ return builder.toString();
+ }
+ }
+
+ default ParserRule getEntryParserRule(INode node) {
+ ICompositeNode root = node.getRootNode();
+ EObject ge1 = root.getGrammarElement();
+ if (ge1 instanceof ParserRule) {
+ return (ParserRule) ge1;
+ } else if (ge1 instanceof Action) {
+ INode firstChild = root.getFirstChild();
+ while (firstChild.getGrammarElement() instanceof Action && firstChild instanceof CompositeNode) {
+ firstChild = ((CompositeNode) firstChild).getFirstChild();
+ }
+ EObject ge2 = firstChild.getGrammarElement();
+ if (ge2 instanceof ParserRule) {
+ return (ParserRule) ge2;
+ }
+ }
+ throw new IllegalStateException("No Root Parser Rule found; The Node Model is broken.");
}
- throw new IllegalStateException("No Root Parser Rule found; The Node Model is broken.");
- }
+ }
}