From 251d68ecdbc6bffb9defcaca024e6f4613b61fb1 Mon Sep 17 00:00:00 2001 From: pfriese Date: Fri, 4 Jul 2008 13:56:06 +0000 Subject: [PATCH] Progress on bug 239622: [Content Assist] Implement generic content assist for Xtext. https://bugs.eclipse.org/bugs/show_bug.cgi?id=239622 --- .../src/org/eclipse/xtext/parsetree/ParseTreeUtil.java | 1 + 1 file changed, 1 insertion(+) create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/ParseTreeUtil.java diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/ParseTreeUtil.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/ParseTreeUtil.java new file mode 100644 index 000000000..46a7024bf --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/ParseTreeUtil.java @@ -0,0 +1 @@ +/******************************************************************************* * Copyright (c) 2008 Michael Clay 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 * *******************************************************************************/ package org.eclipse.xtext.parsetree; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.Assert; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.AbstractElement; import org.eclipse.xtext.Alternatives; import org.eclipse.xtext.Assignment; import org.eclipse.xtext.Grammar; import org.eclipse.xtext.Group; import org.eclipse.xtext.ParserRule; import org.eclipse.xtext.RuleCall; import org.eclipse.xtext.parsetree.AbstractNode; import org.eclipse.xtext.parsetree.CompositeNode; import org.eclipse.xtext.parsetree.LeafNode; /** * * Represents a class wich provides various static helper functions used to * support the work with object models containing * {@link org.eclipse.xtext.AbstractElement} and * {@link org.eclipse.xtext.parsetree.AbstractNode} composite structure models. * * * @author Michael Clay - Initial contribution and API * * @see org.eclipse.xtext.AbstractElement * @see org.eclipse.xtext.parsetree.AbstractNode */ public final class ParseTreeUtil { /** * * @param abstractNode * the node to inspect * @param grammarElementClass * to match * @return a set of abstractNode associated with a grammarelement class * matching the given class */ public static Set getNodesByGrammarElement(AbstractNode abstractNode, Class grammarElementClass) { assertParameterNotNull(abstractNode, "abstractNode"); assertParameterNotNull(grammarElementClass, "grammarElementClass"); Set abstractNodeSet = new LinkedHashSet(); if (abstractNode.getGrammarElement() != null && grammarElementClass.isAssignableFrom(abstractNode.getGrammarElement().getClass())) { abstractNodeSet.add(abstractNode); } for (AbstractNode leafNode : abstractNode.getLeafNodes()) { abstractNodeSet.addAll(getNodesByGrammarElement(leafNode, grammarElementClass)); } return abstractNodeSet; } /** * Dump the composite structure (parsetree) of the given node. * * @param abstractNode * the node to dump */ public static final void dumpNode(AbstractNode abstractNode) { assertParameterNotNull(abstractNode, "abstractNode"); System.out.println("dump parsetree with root node '" + EcoreUtil.getIdentification(abstractNode) + "'"); doDumpNode(abstractNode, "\t"); } /** * @param contextNode * the node representing the 'scope' of the current lookup * @param offsetPosition * the text position within the the current sentence * @param tab * @return the last node element contained within the given contextNode at * the provided position */ public static final AbstractNode getCurrentNodeByOffset(AbstractNode contextNode, int offsetPosition) { assertParameterNotNull(contextNode, "contextNode"); AbstractNode abstractNode = null; if (contextNode.getOffset() <= offsetPosition) { if (contextNode.getGrammarElement() instanceof AbstractElement || contextNode.getGrammarElement() instanceof ParserRule) { abstractNode = contextNode; } for (AbstractNode childNode : contextNode.getLeafNodes()) { AbstractNode lastElementByOffset = getCurrentNodeByOffset(childNode, offsetPosition); if (lastElementByOffset != null) { abstractNode = lastElementByOffset; } } } return abstractNode; } /** * * This method returns the parent grammar of the given eObject by recursive * 'upwards' invocations, passing the eContainer property as parameter until * some Grammar level object is reached. * * @param eObject * an object contained or referenced within some 'root' grammar * @return the {@link org.eclipse.xtext.Grammar} of the given object. */ public static final Grammar getGrammar(EObject eObject) { assertParameterNotNull(eObject, "eObject"); if (eObject instanceof Grammar) { return (Grammar) eObject; } else { return getGrammar(eObject.eContainer()); } } /** * * Returns a list of all assignment to the given rule. * * @param parserRule * the rule of the assignments (ruleCall) to match * @return a list containing all {@see org.eclipse.xtext.Assignment} to the * given rule. */ public static final List getParserRuleAssignments(ParserRule parserRule) { assertParameterNotNull(parserRule, "parserRule"); List list = new ArrayList(); Grammar grammar = (Grammar) parserRule.eContainer(); // filter and search for (ParserRule rule : grammar.getParserRules()) { // excluded? if (!parserRule.equals(rule)) { Assignment ruleAssignment = getParserRuleAssignment(rule.getAlternatives(), parserRule); if (ruleAssignment != null) { list.add(ruleAssignment); } } } return list; } /** * asserts if the given parameter object isnt null * * @param parameter * reference to assert * @param parameterName * the name of the parameter */ public static final void assertParameterNotNull(Object parameter, String parameterName) { Assert.isLegal(parameter != null, "parameter '" + parameterName + "' must not be null."); } /** * * @param abstractNode * the node of the asociated grammar element * * @return the grammar element of the given node or null if its neither a * abstractElement or a parserRule */ public static final AbstractElement getGrammarElementFromNode(AbstractNode abstractNode) { assertParameterNotNull(abstractNode, "abstractNode"); AbstractElement abstractElement = null; if (abstractNode.getGrammarElement() instanceof AbstractElement) { abstractElement = (AbstractElement) abstractNode.getGrammarElement(); } else if (abstractNode.getGrammarElement() instanceof ParserRule) { abstractElement = ((ParserRule) abstractNode.getGrammarElement()).getAlternatives(); } return abstractElement; } /** * * @param contextElement * element searched for assignments to the given rule * @param parserRule * the rule of the assignments to search for * @return an assignment object containing a rulecall to the given * parserRule or null if not found. */ private static final Assignment getParserRuleAssignment(AbstractElement contextElement, ParserRule parserRule) { assertParameterNotNull(contextElement, "contextElement"); assertParameterNotNull(parserRule, "parserRule"); Assignment assignment = null; if (contextElement instanceof Group) { Group group = (Group) contextElement; for (AbstractElement groupElement : group.getAbstractTokens()) { assignment = getParserRuleAssignment(groupElement, parserRule); if (null != assignment) { break; } } } else if (contextElement instanceof Alternatives) { Alternatives alternatives = (Alternatives) contextElement; for (AbstractElement groupElement : alternatives.getGroups()) { assignment = getParserRuleAssignment(groupElement, parserRule); if (null != assignment) { break; } } } else if (contextElement instanceof Assignment) { Assignment assignmentToMatch = (Assignment) contextElement; if (assignmentToMatch.getTerminal() instanceof RuleCall && ((RuleCall) assignmentToMatch.getTerminal()).getName().equalsIgnoreCase(parserRule.getName())) { assignment = assignmentToMatch; } } return assignment; } /** * @param abstractNode * @param indentString */ private static final void doDumpNode(AbstractNode abstractNode, String indentString) { if (abstractNode instanceof CompositeNode) { // CompositeNode compositeNode = (CompositeNode) abstractNode; System.out.println(indentString + "line '" + abstractNode.getLine() + "' offset '" + abstractNode.getOffset() + "' length '" + abstractNode.getLength() + "' grammar-hierarchy (" + dumpParentHierarchy(abstractNode) + ")"); } else if (abstractNode instanceof LeafNode) { LeafNode leafNode = (LeafNode) abstractNode; // ommit hidden channel if (!leafNode.isHidden()) { System.out.println(indentString + "'" + "line '" + leafNode.getLine() + "' offset '" + leafNode.getOffset() + " length '" + leafNode.getLength() + "' " + (leafNode.getFeature() != null ? leafNode.getFeature() + " = " : "") + " text '" + leafNode.getText() + "' grammar-hierarchy (" + dumpParentHierarchy(leafNode) + ")"); } } for (AbstractNode childNode : abstractNode.getLeafNodes()) { doDumpNode(childNode, indentString + indentString); } } private static final String dumpParentHierarchy(AbstractNode abstractNode) { StringBuilder stringBuilder = new StringBuilder(); while (null != abstractNode) { stringBuilder.append(abstractNode.getGrammarElement().getClass().getSimpleName()); if (abstractNode.getGrammarElement() instanceof ParserRule) { stringBuilder.append("[" + ((ParserRule) abstractNode.getGrammarElement()).getName() + "]"); } else if (abstractNode.getGrammarElement() instanceof RuleCall) { stringBuilder.append("[" + ((RuleCall) abstractNode.getGrammarElement()).getName() + "]"); } abstractNode = abstractNode.getParent(); if (null != abstractNode) { stringBuilder.append("->"); } } return stringBuilder.toString(); } } \ No newline at end of file