/*******************************************************************************
* Copyright (c) 2013 Rene Schneider, GEBIT Solutions GmbH 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
*******************************************************************************/
/*
* generated by Xtext
*/
package de.gebit.integrity.ui.outline;
import javax.inject.Inject;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.swt.graphics.Image;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.ui.IImageHelper;
import org.eclipse.xtext.ui.editor.model.IXtextDocument;
import org.eclipse.xtext.ui.editor.outline.IOutlineNode;
import org.eclipse.xtext.ui.editor.outline.impl.DefaultOutlineTreeProvider;
import org.eclipse.xtext.ui.editor.outline.impl.DocumentRootNode;
import org.eclipse.xtext.ui.editor.outline.impl.EObjectNode;
import org.eclipse.xtext.ui.label.StylerFactory;
import org.eclipse.xtext.util.TextRegion;
import de.gebit.integrity.dsl.ConstantDefinition;
import de.gebit.integrity.dsl.Import;
import de.gebit.integrity.dsl.Model;
import de.gebit.integrity.dsl.NestedObject;
import de.gebit.integrity.dsl.PackageDefinition;
import de.gebit.integrity.dsl.SuiteDefinition;
import de.gebit.integrity.dsl.SuiteStatementWithResult;
import de.gebit.integrity.dsl.ValueOrEnumValueOrOperationCollection;
import de.gebit.integrity.dsl.VariableDefinition;
import de.gebit.integrity.dsl.VariableEntity;
import de.gebit.integrity.exceptions.ThisShouldNeverHappenException;
/**
* Customization of the default outline structure.
*
* @author Rene Schneider - initial API and implementation
* @author tilois - actual first working implementation of a usable outline beyond the Xtext-generated default
*/
// SUPPRESS CHECKSTYLE LONG MethodName
public class DSLOutlineTreeProvider extends DefaultOutlineTreeProvider {
/*
* Note that methods starting with an underscore '_' are called by reflection, based on their signature.
*
* E.g. an instance of 'Import' (which is a subclass of 'EObject') would normally go through the method:
* _text(EObject) but if there is an method: _text(Import) this would be called instead.
*
* See class PolymorphicDispatcher for details.
*/
/**
* Differentiates between the different occurences of suites. They may be either occur as plain suite definitions or
* as a declaration of an dependency or an conclusion.
*
* @author tilois
*/
protected enum SuiteType {
/** Normal suite declaration. */
NORMAL,
/** Declaration of a suite as a dependency. */
DEPENDENCY,
/** Declaration of a suite as a conclusion. */
CONCLUSION;
}
/** Factory to create styles. */
@Inject
private StylerFactory styleFactory;
/** Formatting configuration for the outline. */
@Inject
private OutlineFormatting format;
/** Helps loading images from the PlugIn Path. */
@Inject
private IImageHelper imageLoader;
/** Dynamic Dispatch of {@link #createChildren(IOutlineNode, EObject)}. */
public void _createChildren(IOutlineNode aParent, SuiteDefinition aSuiteDefinition) {
for (SuiteDefinition tempSuiteInitializer : aSuiteDefinition.getDependencies()) {
createSuiteNode(aParent, tempSuiteInitializer, SuiteType.DEPENDENCY);
}
for (SuiteDefinition tempSuiteFinalizer : aSuiteDefinition.getFinalizers()) {
createSuiteNode(aParent, tempSuiteFinalizer, SuiteType.CONCLUSION);
}
super._createChildren(aParent, aSuiteDefinition);
}
/** Dynamic Dispatch of {@link #createNode(IOutlineNode, EObject)}. */
protected void _createNode(IOutlineNode aParentNode, SuiteStatementWithResult aModelElement) {
// Don't let every command clutter the outline structure
}
/** Dynamic Dispatch of {@link #createNode(IOutlineNode, EObject)}. */
protected void _createNode(IOutlineNode aParentNode, SuiteDefinition aSuiteDefinition) {
createSuiteNode(aParentNode, aSuiteDefinition, SuiteType.NORMAL);
}
/** Dynamic Dispatch of {@link #createChildren(IOutlineNode, EObject)}. */
protected void _createChildren(DocumentRootNode aParentNode, Model aModelElement) {
for (EObject tempChild : aModelElement.eContents()) {
createNodeDispatcher.invoke(aParentNode, tempChild);
}
}
/** Dynamic Dispatch of {@link #createNode(IOutlineNode, EObject)}. */
protected void _createNode(IntegrityDocumentRoot parentNode, Import anImport) {
super._createNode(parentNode.getImportContainer(), anImport);
}
// Ensure that we get our own root node for outline container handling - otherwise the dynamic dispatch would fail
@Override
public IntegrityDocumentRoot createRoot(IXtextDocument document) {
IntegrityDocumentRoot documentNode = new IntegrityDocumentRoot(labelProvider.getImage(document),
labelProvider.getText(document), document, this);
documentNode.setTextRegion(new TextRegion(0, document.getLength()));
return documentNode;
}
/**
* Creates a container object for imports.
*
* @param aRoot
* Root for which to create this container.
* @return Container objects.
*/
public IOutlineNode createOutlineContainer(DocumentRootNode aRoot) {
// Do not call aRoot.getChildren() here, as this would call the tree provider (us) to
// recreate its children and a new outline container, which would then call aRoot.getChildren() again..
StyledString containerText = styleFactory.createFromXtextStyle("Imports", format.importTextStyle());
return new OutlineImportContainer(aRoot, image("import"), containerText, false);
}
/**
* Creates a Suite Node, which is a special case because there are multiple types of suite nodes and we need to know
* this type before, as the node itself is unaware of it's type.
*
* @param aParentNode
* Parent of this node
* @param aSuiteDefinition
* Suitedefinition this node is linked to
* @param aType
* Type of the suite
* @return An {@link EObjectNode} representing this suite declaration, but is unaware of it's suite type.
*/
protected EObjectNode createSuiteNode(IOutlineNode aParentNode, SuiteDefinition aSuiteDefinition, SuiteType aType) {
return createEObjectNode(aParentNode, aSuiteDefinition, suiteImage(aType), text(aSuiteDefinition, aType),
aType != SuiteType.NORMAL);
}
/** Dynamic Dispatch of {@link #_text(Object)}. */
protected Object _text(Import anImport) {
final String importName = anImport.getImportedNamespace() != null ? anImport.getImportedNamespace() : "";
return styleFactory.createFromXtextStyle(importName, format.importTextStyle());
}
/** Dynamic Dispatch of {@link #_text(Object)}. */
protected Object _text(VariableDefinition aVariableDefinition) {
final StyledString result = new StyledString();
final String variableName = aVariableDefinition.getName().getName();
if (variableName != null) {
result.append(styleFactory.createFromXtextStyle(variableName, format.variableDefinitionTextStyle()));
}
String tempValue = getValueOf(aVariableDefinition.getInitialValue());
if (tempValue != null) {
result.append(" := ");
result.append(styleFactory.createFromXtextStyle(tempValue, format.constantDefinitionTextStyle()));
}
return appendExplanationTo(result, "Variable");
}
/** Dynamic Dispatch of {@link #_text(Object)}. */
protected Object _text(ConstantDefinition aConstantDefinition) {
final StyledString result = new StyledString();
final String constantName = aConstantDefinition.getName().getName();
if (constantName != null) {
result.append(styleFactory.createFromXtextStyle(constantName, format.constantDefinitionTextStyle()));
}
String tempValue = getValueOf(aConstantDefinition.getValue());
if (tempValue != null) {
result.append(" := ");
result.append(styleFactory.createFromXtextStyle(tempValue, format.constantDefinitionTextStyle()));
}
return appendExplanationTo(result, "Constant");
}
/**
* Gets the textual representation of an arbitrary value from the source. Unless it's an {@link NestedObject}, where
* a placeholder is returned, as NestedObjects are expected to hold a multiline value, which can't be displayed in
* the outline properly.
*
* @param aValue
* Value to display
* @return Textual representation of this value.
*/
protected String getValueOf(ValueOrEnumValueOrOperationCollection aValue) {
if (aValue instanceof NestedObject) {
return "{...}";
}
ICompositeNode tempParserNode = NodeModelUtils.getNode(aValue);
return tempParserNode != null ? tempParserNode.getText() : null;
}
/** Dynamic Dispatch of {@link #_text(Object)}. */
protected Object _text(VariableEntity aParameter) {
if (!(aParameter.eContainer() instanceof SuiteDefinition)) {
return null; // Only handling suite parameters
}
final String parameterRawText = aParameter.getName() != null ? aParameter.getName() : "";
final StyledString parameterText = styleFactory.createFromXtextStyle(parameterRawText,
format.suiteParameterTextStyle());
return appendExplanationTo(parameterText, "Parameter");
}
/** Dynamic Dispatch of {@link #_text(Object)}. */
protected StyledString _text(SuiteDefinition aSuiteDefinition) {
final String suiteName = aSuiteDefinition.getName() != null ? aSuiteDefinition.getName() : "";
return styleFactory.createFromXtextStyle(suiteName, format.suiteTextStyle());
}
/**
* Returns a styled textual representation of the given suite definition.
*
* @param aSuiteDefinition
* Suite definition to get a textual representation.
* @param aType
* Type of the suite definition
* @return Textual representation of this suite definition.
*/
protected StyledString text(SuiteDefinition aSuiteDefinition, SuiteType aType) {
StyledString tempSuiteText = _text(aSuiteDefinition);
switch (aType) {
case NORMAL:
return appendExplanationTo(tempSuiteText, "Suite");
case DEPENDENCY:
return appendExplanationTo(tempSuiteText, "Dependency");
case CONCLUSION:
return appendExplanationTo(tempSuiteText, "Conclusion");
default:
throw new ThisShouldNeverHappenException("Unknown suite type " + aType + " encountered!");
}
}
/** Dynamic Dispatch of {@link #_image(Object)}. */
protected Image _image(Import anImport) {
return image("import");
}
/** Dynamic Dispatch of {@link #_image(Object)}. */
protected Image _image(SuiteDefinition aSuite) {
return image("suite");
}
/** Dynamic Dispatch of {@link #_image(Object)}. */
protected Image _image(VariableDefinition aVariable) {
return image("variable");
}
/** Dynamic Dispatch of {@link #_image(Object)}. */
protected Image _image(ConstantDefinition aSuite) {
return image("constant");
}
/** Dynamic Dispatch of {@link #_image(Object)}. */
protected Image _image(PackageDefinition anPackage) {
return image("package");
}
/** Dynamic Dispatch of {@link #_image(Object)}. */
protected Object _image(VariableEntity aParameter) {
if (!(aParameter.eContainer() instanceof SuiteDefinition)) {
return null; // Only handling suite parameters
}
return image("parameter");
}
/**
* Gets an image for the given suite definition.
*
* @param aType
* Type of the suite definition
* @return Image represention this suite declaration.
*/
protected Image suiteImage(SuiteType aType) {
switch (aType) {
case NORMAL:
return image("suite");
case DEPENDENCY:
return image("dependency");
case CONCLUSION:
return image("conclusion");
default:
throw new ThisShouldNeverHappenException("Unknown suite type " + aType + " encountered!");
}
}
/**
* Assumes that all images are loaded from the subfolder "outline" and prepends it to the given name if not already
* present. Also assumes that all images are in PNG format and will add that suffix, if not already present.
*
* @param aName
* Name of the image.
* @return Image.
*/
protected Image image(String aName) {
if (aName == null) {
return null;
}
String tempImageName = aName.startsWith("outline/") ? aName : "outline/" + aName;
tempImageName = tempImageName.endsWith(".png") ? tempImageName : tempImageName + ".png";
return imageLoader.getImage(tempImageName);
}
/** Dynamic Dispatch of {@link #_isLeaf(Object)}. */
protected boolean _isLeaf(VariableDefinition aVariableDefintion) {
return true; // Don't show the VariableEntity in the outline
}
/** Dynamic Dispatch of {@link #_isLeaf(Object)}. */
protected boolean _isLeaf(ConstantDefinition aConstantDefinition) {
return true; // Don't show the VariableEntity in the outline
}
/**
* Appends an explanation text to the given styled text.
*
* @param aNormalText
* Normal text where the explanation should be appended to.
* @param anExplanation
* Explanation text.
* @return Styled text with an explanation text.
*/
protected StyledString appendExplanationTo(StyledString aNormalText, String anExplanation) {
StyledString tempExplanation = styleFactory.createFromXtextStyle("(" + anExplanation + ")",
format.explanationTextStyle());
return aNormalText.append(" ").append(tempExplanation);
}
}