/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2017 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.sarl.lang.ui.labeling;
import java.util.Collections;
import java.util.concurrent.locks.ReentrantLock;
import javax.inject.Singleton;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.jdt.ui.JavaElementImageDescriptor;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.swt.graphics.Image;
import org.eclipse.xtend.core.jvmmodel.IXtendJvmAssociations;
import org.eclipse.xtend.ide.labeling.XtendLabelProvider;
import org.eclipse.xtext.common.types.JvmAnnotationType;
import org.eclipse.xtext.common.types.JvmDeclaredType;
import org.eclipse.xtext.common.types.JvmEnumerationType;
import org.eclipse.xtext.common.types.JvmExecutable;
import org.eclipse.xtext.common.types.JvmGenericType;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmOperation;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.common.types.JvmVisibility;
import org.eclipse.xtext.common.types.TypesPackage;
import org.eclipse.xtext.common.types.access.IJvmTypeProvider;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.util.Exceptions;
import org.eclipse.xtext.util.PolymorphicDispatcher;
import org.eclipse.xtext.util.PolymorphicDispatcher.ErrorHandler;
import org.eclipse.xtext.xbase.XVariableDeclaration;
import org.eclipse.xtext.xbase.scoping.featurecalls.OperatorMapping;
import org.eclipse.xtext.xbase.typesystem.IResolvedTypes;
import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference;
import org.eclipse.xtext.xbase.typesystem.util.CommonTypeComputationServices;
import org.eclipse.xtext.xbase.ui.labeling.XbaseImageAdornments;
import org.eclipse.xtext.xbase.validation.UIStrings;
import io.sarl.lang.sarl.SarlAction;
import io.sarl.lang.sarl.SarlAgent;
import io.sarl.lang.sarl.SarlBehavior;
import io.sarl.lang.sarl.SarlBehaviorUnit;
import io.sarl.lang.sarl.SarlCapacity;
import io.sarl.lang.sarl.SarlCapacityUses;
import io.sarl.lang.sarl.SarlConstructor;
import io.sarl.lang.sarl.SarlEvent;
import io.sarl.lang.sarl.SarlField;
import io.sarl.lang.sarl.SarlPackage;
import io.sarl.lang.sarl.SarlRequiredCapacity;
import io.sarl.lang.sarl.SarlScript;
import io.sarl.lang.sarl.SarlSkill;
import io.sarl.lang.services.SARLGrammarKeywordAccess;
import io.sarl.lang.typesystem.InheritanceHelper;
import io.sarl.lang.ui.images.IQualifiedNameImageProvider;
import io.sarl.lang.ui.images.SARLImages;
/**
* Provides labels for a EObjects.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @see "https://www.eclipse.org/Xtext/documentation/304_ide_concepts.html#label-provider"
*/
@SuppressWarnings("checkstyle:classfanoutcomplexity")
@Singleton
public class SARLLabelProvider extends XtendLabelProvider implements IQualifiedNameImageProvider {
/** Max length of the text for the behavior units.
*/
public static final int BEHAVIOR_UNIT_TEXT_LENGTH = 7;
@Inject
private UIStrings uiStrings;
@Inject
private OperatorMapping operatorMapping;
@Inject
private IXtendJvmAssociations jvmModelAssociations;
@Inject
private SARLImages images;
@Inject
private XbaseImageAdornments adornments;
private final PolymorphicDispatcher<ImageDescriptor> imageDescriptorDispatcher;
private final ReentrantLock imageDescriptorLock = new ReentrantLock();
@Inject
private CommonTypeComputationServices services;
@Inject
private SARLGrammarKeywordAccess keywords;
@Inject
private InheritanceHelper inheritanceHelper;
/**
* @param delegate - the original provider.
*/
@Inject
public SARLLabelProvider(AdapterFactoryLabelProvider delegate) {
super(delegate);
this.imageDescriptorDispatcher = new PolymorphicDispatcher<>(
"imageDescriptor", //$NON-NLS-1$
1, 1,
Collections.singletonList(this),
new ErrorHandler<ImageDescriptor>() {
@Override
public ImageDescriptor handle(Object[] params, Throwable exception) {
return handleImageDescriptorError(params, exception);
}
});
}
/** Get the image descriptor for the given element.
*
* @param element - the element.
* @return the image descriptor.
*/
protected ImageDescriptor doGetImageDescriptor(Object element) {
return this.imageDescriptorDispatcher.invoke(element);
}
/** Invoked when an image descriptor cannot be found.
*
* @param params - the parameters given to the method polymorphic dispatcher.
* @param exception - the cause of the error.
* @return the image descriptor for the error.
*/
protected ImageDescriptor handleImageDescriptorError(Object[] params, Throwable exception) {
if (exception instanceof NullPointerException) {
final Object defaultImage = getDefaultImage();
if (defaultImage instanceof ImageDescriptor) {
return (ImageDescriptor) defaultImage;
}
if (defaultImage instanceof Image) {
return ImageDescriptor.createFromImage((Image) defaultImage);
}
return super.imageDescriptor(params[0]);
}
return Exceptions.throwUncheckedException(exception);
}
/** Create a string representation of a signature without the return type.
*
* @param simpleName - the action name.
* @param element - the executable element.
* @return the signature.
*/
protected StyledString signatureWithoutReturnType(StyledString simpleName, JvmExecutable element) {
return simpleName.append(this.uiStrings.parameters(element));
}
/** Create a string representation of the given element.
*
* @param reference - the element.
* @return the string representation.
*/
protected StyledString getHumanReadableName(JvmTypeReference reference) {
if (reference == null) {
return new StyledString("Object"); //$NON-NLS-1$
}
String name = this.uiStrings.referenceToString(reference, "Object"); //$NON-NLS-1$
//
// FIXME: https://bugs.eclipse.org/bugs/show_bug.cgi?id=443131
final JvmType type = reference.getType();
if (type != null && type.eIsProxy() && reference.eResource() != null) {
// This case occurs when the reference is unknown:
// the found "name" is the fully qualified name of the type.
// So we should extract the simple name
int index = name.length() - 1;
final char dot = '.';
final char doll = '$';
final char dies = '#';
char ch;
while (index >= 0) {
ch = name.charAt(index);
if (ch == dot || ch == doll || ch == dies) {
name = name.substring(index + 1);
// break the loop
index = -1;
} else {
index--;
}
}
}
// END OF FIX
//
return convertToStyledString(name);
}
// Descriptors
@Override
protected ImageDescriptor imageDescriptor(Object element) {
if (this.imageDescriptorLock.isLocked()) {
return super.imageDescriptor(element);
}
this.imageDescriptorLock.lock();
try {
return doGetImageDescriptor(element);
} finally {
this.imageDescriptorLock.unlock();
}
}
/** Replies the image for the given element.
*
* <p>This function is a Xtext dispatch function for {@link #imageDescriptor(Object)}.
*
* @param element - the element.
* @return the image descriptor.
* @see #imageDescriptor(Object)
*/
protected ImageDescriptor imageDescriptor(Package element) {
// Mostly used by the outline
return this.images.forPackage();
}
/** Replies the image for the given element.
*
* <p>This function is a Xtext dispatch function for {@link #imageDescriptor(Object)}.
*
* @param element - the element.
* @return the image descriptor.
* @see #imageDescriptor(Object)
*/
protected ImageDescriptor imageDescriptor(SarlScript element) {
return this.images.forFile();
}
/** Replies the image for the given element.
*
* <p>This function is a Xtext dispatch function for {@link #imageDescriptor(Object)}.
*
* @param element - the element.
* @return the image descriptor.
* @see #imageDescriptor(Object)
*/
protected ImageDescriptor imageDescriptor(SarlAgent element) {
final JvmDeclaredType jvmElement = this.jvmModelAssociations.getInferredType(element);
return this.images.forAgent(
element.getVisibility(),
this.adornments.get(jvmElement));
}
/** Replies the image for the given element.
*
* <p>This function is a Xtext dispatch function for {@link #imageDescriptor(Object)}.
*
* @param element - the element.
* @return the image descriptor.
* @see #imageDescriptor(Object)
*/
protected ImageDescriptor imageDescriptor(SarlEvent element) {
final JvmDeclaredType jvmElement = this.jvmModelAssociations.getInferredType(element);
return this.images.forEvent(
element.getVisibility(),
this.adornments.get(jvmElement));
}
/** Replies the image for the given element.
*
* <p>This function is a Xtext dispatch function for {@link #imageDescriptor(Object)}.
*
* @param element - the element.
* @return the image descriptor.
* @see #imageDescriptor(Object)
*/
protected ImageDescriptor imageDescriptor(SarlCapacity element) {
final JvmDeclaredType jvmElement = this.jvmModelAssociations.getInferredType(element);
return this.images.forCapacity(
element.getVisibility(),
this.adornments.get(jvmElement));
}
/** Replies the image for the given element.
*
* <p>This function is a Xtext dispatch function for {@link #imageDescriptor(Object)}.
*
* @param element - the element.
* @return the image descriptor.
* @see #imageDescriptor(Object)
*/
protected ImageDescriptor imageDescriptor(SarlSkill element) {
final JvmDeclaredType jvmElement = this.jvmModelAssociations.getInferredType(element);
return this.images.forSkill(
element.getVisibility(),
this.adornments.get(jvmElement));
}
/** Replies the image for the given element.
*
* <p>This function is a Xtext dispatch function for {@link #imageDescriptor(Object)}.
*
* @param element - the element.
* @return the image descriptor.
* @see #imageDescriptor(Object)
*/
protected ImageDescriptor imageDescriptor(SarlBehavior element) {
final JvmDeclaredType jvmElement = this.jvmModelAssociations.getInferredType(element);
return this.images.forBehavior(
element.getVisibility(),
this.adornments.get(jvmElement));
}
/** Replies the image for the given element.
*
* <p>This function is a Xtext dispatch function for {@link #imageDescriptor(Object)}.
*
* @param element - the element.
* @return the image descriptor.
* @see #imageDescriptor(Object)
*/
protected ImageDescriptor imageDescriptor(SarlField element) {
return this.images.forField(
element.getVisibility(),
this.adornments.get(this.jvmModelAssociations.getJvmField(element)));
}
/** Replies the image for the given element.
*
* <p>This function is a Xtext dispatch function for {@link #imageDescriptor(Object)}.
*
* @param element - the element.
* @return the image descriptor.
* @see #imageDescriptor(Object)
*/
protected ImageDescriptor imageDescriptor(SarlConstructor element) {
return this.images.forConstructor(
element.getVisibility(),
this.adornments.get(this.jvmModelAssociations.getInferredConstructor(element)));
}
/** Replies the image for the given element.
*
* <p>This function is a Xtext dispatch function for {@link #imageDescriptor(Object)}.
*
* @param element - the element.
* @return the image descriptor.
* @see #imageDescriptor(Object)
*/
protected ImageDescriptor imageDescriptor(SarlAction element) {
final JvmOperation jvmElement = this.jvmModelAssociations.getDirectlyInferredOperation(element);
return this.images.forOperation(
element.getVisibility(),
this.adornments.get(jvmElement));
}
/** Replies the image for the given element.
*
* <p>This function is a Xtext dispatch function for {@link #imageDescriptor(Object)}.
*
* @param element - the element.
* @return the image descriptor.
* @see #imageDescriptor(Object)
*/
protected ImageDescriptor imageDescriptor(SarlCapacityUses element) {
return this.images.forCapacityUses();
}
/** Replies the image for the given element.
*
* <p>This function is a Xtext dispatch function for {@link #imageDescriptor(Object)}.
*
* @param element - the element.
* @return the image descriptor.
* @see #imageDescriptor(Object)
*/
protected ImageDescriptor imageDescriptor(SarlRequiredCapacity element) {
return this.images.forCapacityRequirements();
}
/** Replies the image for the given element.
*
* <p>This function is a Xtext dispatch function for {@link #imageDescriptor(Object)}.
*
* @param element - the element.
* @return the image descriptor.
* @see #imageDescriptor(Object)
*/
protected ImageDescriptor imageDescriptor(SarlBehaviorUnit element) {
return this.images.forBehaviorUnit();
}
// Texts
/** Replies the text for the given element.
*
* @param element - the element.
* @return the text.
*/
protected StyledString text(JvmTypeReference element) {
return getHumanReadableName(element);
}
/** Replies the text for the given element.
*
* @param element - the element.
* @return the text.
*/
protected StyledString text(SarlAgent element) {
return convertToStyledString(element.getName());
}
/** Replies the text for the given element.
*
* @param element - the element.
* @return the text.
*/
protected StyledString text(SarlEvent element) {
return convertToStyledString(element.getName());
}
/** Replies the text for the given element.
*
* @param element - the element.
* @return the text.
*/
protected StyledString text(SarlCapacity element) {
return convertToStyledString(element.getName());
}
/** Replies the text for the given element.
*
* @param element - the element.
* @return the text.
*/
protected StyledString text(SarlSkill element) {
return convertToStyledString(element.getName());
}
/** Replies the text for the given element.
*
* @param element - the element.
* @return the text.
*/
protected StyledString text(SarlBehavior element) {
return convertToStyledString(element.getName());
}
/** Replies the text for the given element.
*
* @param element - the element.
* @return the text.
*/
protected StyledString text(SarlAction element) {
final JvmIdentifiableElement jvmElement = this.jvmModelAssociations.getDirectlyInferredOperation(element);
final String simpleName = element.getName();
if (simpleName != null) {
final QualifiedName qnName = QualifiedName.create(simpleName);
final QualifiedName operator = this.operatorMapping.getOperator(qnName);
if (operator != null) {
final StyledString result = signature(operator.getFirstSegment(), jvmElement);
result.append(" (" + simpleName + ")", StyledString.COUNTER_STYLER); //$NON-NLS-1$//$NON-NLS-2$
return result;
}
}
return signature(element.getName(), jvmElement);
}
/** Replies the text for the given element.
*
* @param element - the element.
* @return the text.
*/
@SuppressWarnings("static-method")
protected StyledString text(SarlCapacityUses element) {
return new StyledString(Messages.SARLLabelProvider_0, StyledString.QUALIFIER_STYLER);
}
/** Replies the text for the given element.
*
* @param element - the element.
* @return the text.
*/
@SuppressWarnings("static-method")
protected StyledString text(SarlRequiredCapacity element) {
return new StyledString(Messages.SARLLabelProvider_1, StyledString.QUALIFIER_STYLER);
}
/** Replies the text for the given element.
*
* @param element - the element.
* @return the text.
*/
protected StyledString text(SarlBehaviorUnit element) {
final StyledString text = new StyledString("on ", StyledString.DECORATIONS_STYLER); //$NON-NLS-1$
text.append(getHumanReadableName(element.getName()));
if (element.getGuard() != null) {
String txt = null;
final ICompositeNode node = NodeModelUtils.getNode(element.getGuard());
if (node != null) {
txt = node.getText().trim();
}
if (Strings.isNullOrEmpty(txt)) {
txt = "[" + Messages.SARLLabelProvider_2 + "]"; //$NON-NLS-1$//$NON-NLS-2$
} else {
assert txt != null;
final String dots = "..."; //$NON-NLS-1$
if (txt.length() > BEHAVIOR_UNIT_TEXT_LENGTH + dots.length()) {
txt = "[" + txt.substring(0, BEHAVIOR_UNIT_TEXT_LENGTH) + dots + "]"; //$NON-NLS-1$//$NON-NLS-2$
} else {
txt = "[" + txt + "]"; //$NON-NLS-1$//$NON-NLS-2$
}
}
text.append(" "); //$NON-NLS-1$
text.append(txt, StyledString.DECORATIONS_STYLER);
}
return text;
}
@Override
protected String text(XVariableDeclaration variableDeclaration) {
final IResolvedTypes resolvedTypes = getTypeResolver().resolveTypes(variableDeclaration);
final LightweightTypeReference type = resolvedTypes.getActualType((JvmIdentifiableElement) variableDeclaration);
if (type != null) {
return variableDeclaration.getName() + " " + this.keywords.getColonKeyword() //$NON-NLS-1$
+ " " + type.getHumanReadableName(); //$NON-NLS-1$
}
return variableDeclaration.getName();
}
@Override
public Image getImageForQualifiedName(String qualifiedName, Notifier context, IJvmTypeProvider jvmTypeProvider) {
return convertToImage(getImageDescriptorForQualifiedName(qualifiedName, context, jvmTypeProvider));
}
@SuppressWarnings("checkstyle:npathcomplexity")
@Override
public ImageDescriptor getImageDescriptorForQualifiedName(String qualifiedName, Notifier context,
IJvmTypeProvider typeProvider) {
JvmType type = null;
if (typeProvider != null) {
type = typeProvider.findTypeByName(qualifiedName);
}
if (type == null && context != null) {
type = this.services.getTypeReferences().findDeclaredType(qualifiedName, context);
}
int adornments = this.adornments.get(type);
JvmVisibility visibility = JvmVisibility.DEFAULT;
if (type != null) {
if (type.eClass() == TypesPackage.Literals.JVM_GENERIC_TYPE) {
final JvmGenericType gtype = (JvmGenericType) type;
visibility = gtype.getVisibility();
final int ecoreCode = this.inheritanceHelper.getSarlElementEcoreType(gtype);
switch (ecoreCode) {
case SarlPackage.SARL_AGENT:
return this.images.forAgent(visibility, this.adornments.get(gtype));
case SarlPackage.SARL_BEHAVIOR:
return this.images.forBehavior(visibility, this.adornments.get(gtype));
case SarlPackage.SARL_CAPACITY:
// Remove the "abstract" ornment because capacities are always abstract.
adornments = (adornments & JavaElementImageDescriptor.ABSTRACT) ^ adornments;
return this.images.forCapacity(visibility, adornments);
case SarlPackage.SARL_EVENT:
return this.images.forEvent(visibility, this.adornments.get(gtype));
case SarlPackage.SARL_SKILL:
return this.images.forSkill(visibility, this.adornments.get(gtype));
default:
if (gtype.isInterface()) {
return this.images.forInterface(visibility, this.adornments.get(gtype));
}
}
} else if (type.eClass() == TypesPackage.Literals.JVM_ENUMERATION_TYPE) {
final JvmEnumerationType etype = (JvmEnumerationType) type;
visibility = etype.getVisibility();
return this.images.forEnum(visibility, adornments);
} else if (type.eClass() == TypesPackage.Literals.JVM_ANNOTATION_TYPE) {
final JvmAnnotationType atype = (JvmAnnotationType) type;
visibility = atype.getVisibility();
return this.images.forAnnotation(visibility, adornments);
} else {
visibility = JvmVisibility.DEFAULT;
}
}
// Default icon is the class icon.
return this.images.forClass(visibility, adornments);
}
}