/******************************************************************************* * Copyright (c) 2012 BMW Car IT 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.jnario.feature.ui.highlighting; import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.findNodesForFeature; import static org.jnario.feature.ui.highlighting.FeatureHighlightingConfiguration.CODE_ID; import static org.jnario.feature.ui.highlighting.FeatureHighlightingConfiguration.SCENARIO_ID; import static org.jnario.util.Strings.getFirstWord; import java.util.List; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.xtend.core.xtend.XtendClass; import org.eclipse.xtend.core.xtend.XtendField; import org.eclipse.xtend.core.xtend.XtendMember; import org.eclipse.xtend.core.xtend.XtendPackage; import org.eclipse.xtext.nodemodel.ICompositeNode; import org.eclipse.xtext.nodemodel.INode; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.ui.editor.syntaxcoloring.IHighlightedPositionAcceptor; import org.eclipse.xtext.xbase.XAbstractFeatureCall; import org.eclipse.xtext.xbase.XNumberLiteral; import org.eclipse.xtext.xbase.annotations.xAnnotations.XAnnotation; import org.jnario.feature.feature.Feature; import org.jnario.feature.feature.FeaturePackage; import org.jnario.feature.feature.Scenario; import org.jnario.feature.feature.Step; import org.jnario.feature.feature.StepReference; import org.jnario.feature.feature.util.FeatureSwitch; import org.jnario.feature.jvmmodel.StepArgumentsProvider; import org.jnario.ui.highlighting.JnarioHighlightingCalculator; import org.jnario.util.Strings; import com.google.inject.Inject; /** * @author Birgit Engelmann - Initial contribution and API * @author Sebastian Benz */ public class FeatureSemanticHighlightingCalculator extends JnarioHighlightingCalculator { private class Implementation extends FeatureSwitch<Boolean> { private final IHighlightedPositionAcceptor acceptor; public Implementation(IHighlightedPositionAcceptor acceptor) { this.acceptor = acceptor; } @Override public Boolean caseScenario(Scenario scenario) { for (XtendMember member : scenario.getMembers()) { if(member.eClass() == XtendPackage.Literals.XTEND_FIELD){ XtendField field = (XtendField) member; highlightXtendField(field, acceptor); // XExpression initializer = field.getInitialValue(); // highlightRichStrings(initializer, acceptor); } for (XAnnotation annotation : member.getAnnotations()) { highlightDeprecatedXtendAnnotationTarget(acceptor, member, annotation); } } return highlightXtendClassName(scenario, SCENARIO_ID); } private Boolean highlightXtendClassName(XtendClass object, String id) { List<INode> nodes = findNodesForFeature(object, XtendPackage.Literals.XTEND_TYPE_DECLARATION__NAME); for (INode node : nodes) { highlightNode(node, id, acceptor); } return Boolean.TRUE; } @Override public Boolean caseFeature(Feature feature) { List<INode> nodes = findNodesForFeature(feature, XtendPackage.Literals.XTEND_TYPE_DECLARATION__NAME); for (INode node : nodes) { highlightNode(node, FeatureHighlightingConfiguration.FEATURE_ID, acceptor); } // there is an xtext problem if the splitted token is the only rule present if(feature.getBackground() == null && feature.getScenarios().isEmpty()){ nodes = findNodesForFeature(feature, FeaturePackage.Literals.FEATURE__DESCRIPTION); for (INode node : nodes) { highlightNode(node, FeatureHighlightingConfiguration.STEP_TEXT_ID, acceptor); } } return Boolean.TRUE; } @Override public Boolean caseStep(Step step) { String description; if(step != null && step.getName() != null){ description = getFirstWord(stepReferenceName(step, XtendPackage.Literals.XTEND_FUNCTION__NAME)); highlightStep(description, step, XtendPackage.Literals.XTEND_FUNCTION__NAME); }else if(step instanceof StepReference){ StepReference ref = (StepReference) step; highlightFirstWordOfReference(ref, ref.getReference()); } highlightIdentifiers(step); List<INode> nodes; if (step instanceof StepReference) { nodes = NodeModelUtils.findNodesForFeature(step, FeaturePackage.Literals.STEP_REFERENCE__REFERENCE); }else{ nodes = NodeModelUtils.findNodesForFeature(step, XtendPackage.Literals.XTEND_FUNCTION__NAME); } INode lastChild = nodes.get(nodes.size()-1); String text = lastChild.getText(); String trailingWhitespace = Strings.trailingWhitespace(text); acceptor.addPosition(lastChild.getOffset() + text.length() - trailingWhitespace.length(), trailingWhitespace.length(), CODE_ID); return Boolean.TRUE; } private void highlightStep(String string, EObject object, EAttribute attribute) { acceptor.addPosition(offset(object, attribute), string.length(), FeatureHighlightingConfiguration.STEP_ID); } private void highlightReference(String string, EObject object, EReference reference, String highlightConfig){ acceptor.addPosition(offset(object, reference), string.length(), highlightConfig); } private void highlightFirstWordOfReference(Step reference, Step referencedStep){ String description; if(referencedStep.getName() != null){ description = getFirstWord(stepReferenceName(reference, FeaturePackage.Literals.STEP_REFERENCE__REFERENCE)); highlightReference(description, reference, FeaturePackage.Literals.STEP_REFERENCE__REFERENCE, FeatureHighlightingConfiguration.STEP_REFERENCE_ID); } else{ // unresolved reference -> take first word until whitespace description = getFirstWord(stepReferenceName(reference, FeaturePackage.Literals.STEP_REFERENCE__REFERENCE)); highlightReference(description, reference, FeaturePackage.Literals.STEP_REFERENCE__REFERENCE, FeatureHighlightingConfiguration.STEP_UNRESOLVED_REFERENCE_ID); } } private String stepReferenceName(Step step, EStructuralFeature feature){ List<INode> nodes = NodeModelUtils.findNodesForFeature(step, feature); // works only if keyword exists only once in Step return nodes.iterator().next().getText(); } private int offset(EObject content, EAttribute attribute) { List<INode> nodes = NodeModelUtils.findNodesForFeature(content, attribute); // works only if keyword exists only once in Step return nodes.iterator().next().getOffset(); } private int offset(EObject content, EReference reference) { List<INode> nodes = NodeModelUtils.findNodesForFeature(content, reference); // works only if keyword exists only once in Step return nodes.iterator().next().getOffset(); } private void highlightIdentifiers(Step step){ String name; final int offset; if(step instanceof StepReference){ name = stepReferenceName(step, FeaturePackage.Literals.STEP_REFERENCE__REFERENCE); offset = offset(step, FeaturePackage.Literals.STEP_REFERENCE__REFERENCE); } else{ name = step.getName(); offset = offset(step, XtendPackage.Literals.XTEND_FUNCTION__NAME); } if(name != null){ argumentsProvider.findStepArguments(step, new StepArgumentsProvider.ArgumentAcceptor() { public void accept(String arg, int localOffset, int length) { acceptor.addPosition(offset + localOffset, length, FeatureHighlightingConfiguration.IDENTIFIERS_ID); } }); } } } @Inject private StepArgumentsProvider argumentsProvider; protected EObject root(XtextResource resource) { return resource.getParseResult().getRootASTElement(); } protected boolean noNodeModel(XtextResource resource) { return resource == null || resource.getParseResult() == null || root(resource) == null; } protected void searchAndHighlightElements(XtextResource resource, IHighlightedPositionAcceptor acceptor) { Implementation highlighter = new Implementation(acceptor); TreeIterator<EObject> iterator = resource.getAllContents(); while (iterator.hasNext()) { EObject object = iterator.next(); ICompositeNode node = NodeModelUtils.getNode(object); if(node != null){ highlighter.doSwitch(object); if (object instanceof XAbstractFeatureCall) { computeFeatureCallHighlighting((XAbstractFeatureCall) object, acceptor); } // if (object instanceof ExampleTable) { // highlightExampleHeader((ExampleTable) object, acceptor); // } // Handle XAnnotation in a special way because we want the @ highlighted too if (object instanceof XNumberLiteral) { highlightNumberLiterals((XNumberLiteral) object, acceptor); } if (object instanceof XAnnotation) { highlightAnnotation((XAnnotation) object, acceptor); } else { computeReferencedJvmTypeHighlighting(acceptor, object); } } } } // private void highlightExampleHeader(ExampleTable table, IHighlightedPositionAcceptor acceptor){ // if(table == null){ // return; // } // for (int i = 0; i < table.getColumns().size(); i++) { // XtendField element = table.getColumns().get(i); // INode node = NodeModelUtils.getNode(element); // highlightNode(node, FeatureHighlightingConfiguration.IDENTIFIERS_ID, acceptor); // if(table.getColumns().size() == i + 1){ // highlightTableHeadingEnd(node.getNextSibling(), acceptor); // } // } // } // // private void highlightTableHeadingEnd(INode node, IHighlightedPositionAcceptor acceptor) { // while(node instanceof LeafNode){ // if(!((LeafNode)node).isHidden()){ // highlightNode(node, FeatureHighlightingConfiguration.IDENTIFIERS_ID, acceptor); // return; // } // node = node.getNextSibling(); // } // } }