diff options
| author | Pierre-Charles David | 2016-08-16 13:44:57 +0000 |
|---|---|---|
| committer | Pierre-Charles David | 2016-08-22 15:00:12 +0000 |
| commit | 10f13b75448bb9c81d401fff90013e9299eed335 (patch) | |
| tree | 4de2ea620eff05d98201f59acc0ad069ed60e0e3 | |
| parent | 04cc6c431dcf1943ecd6ae8382c5cd591dc2fe19 (diff) | |
| download | org.eclipse.sirius-10f13b75448bb9c81d401fff90013e9299eed335.tar.gz org.eclipse.sirius-10f13b75448bb9c81d401fff90013e9299eed335.tar.xz org.eclipse.sirius-10f13b75448bb9c81d401fff90013e9299eed335.zip | |
[496014] Provide an IInterpretedExpressionQuery for properties expressions
Bug: 496014
Change-Id: I5a24ea9462f5dce96963377fb7d9ebd3f84b49ab
Signed-off-by: Pierre-Charles David <pierre-charles.david@obeo.fr>
6 files changed, 818 insertions, 0 deletions
diff --git a/plugins/org.eclipse.sirius.ui.properties/META-INF/MANIFEST.MF b/plugins/org.eclipse.sirius.ui.properties/META-INF/MANIFEST.MF index 91db8cbd22..c9b0a8dc70 100644 --- a/plugins/org.eclipse.sirius.ui.properties/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.sirius.ui.properties/META-INF/MANIFEST.MF @@ -37,4 +37,5 @@ Bundle-ActivationPolicy: lazy Bundle-Localization: plugin Export-Package: org.eclipse.sirius.ui.properties.api;version="4.1.0", org.eclipse.sirius.ui.properties.internal;version="4.1.0";x-internal:=true, + org.eclipse.sirius.ui.properties.internal.expressions;version="4.1.0";x-internal:=true, org.eclipse.sirius.ui.properties.internal.tabprovider;version="4.1.0";x-internal:=true diff --git a/plugins/org.eclipse.sirius.ui.properties/plugin.xml b/plugins/org.eclipse.sirius.ui.properties/plugin.xml index 358a225a93..29ac3b644c 100644 --- a/plugins/org.eclipse.sirius.ui.properties/plugin.xml +++ b/plugins/org.eclipse.sirius.ui.properties/plugin.xml @@ -64,4 +64,10 @@ </siriusPropertySheetPageProvider> --> </extension> + <extension + point="org.eclipse.sirius.interpretedExpressionQueryProvider"> + <interpretedExpressionProvider + class="org.eclipse.sirius.ui.properties.internal.expressions.PropertiesExpressionQueryProvider"> + </interpretedExpressionProvider> + </extension> </plugin> diff --git a/plugins/org.eclipse.sirius.ui.properties/src/org/eclipse/sirius/ui/properties/internal/expressions/DomainClassSwitch.java b/plugins/org.eclipse.sirius.ui.properties/src/org/eclipse/sirius/ui/properties/internal/expressions/DomainClassSwitch.java new file mode 100644 index 0000000000..12f101056b --- /dev/null +++ b/plugins/org.eclipse.sirius.ui.properties/src/org/eclipse/sirius/ui/properties/internal/expressions/DomainClassSwitch.java @@ -0,0 +1,207 @@ +/******************************************************************************* + * Copyright (c) 2016 Obeo. + * 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 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.ui.properties.internal.expressions; + +import java.util.Collection; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.sirius.common.tools.api.interpreter.TypeName; +import org.eclipse.sirius.ext.base.Option; +import org.eclipse.sirius.ext.base.Options; +import org.eclipse.sirius.properties.CustomExpression; +import org.eclipse.sirius.properties.CustomOperation; +import org.eclipse.sirius.properties.DynamicMappingFor; +import org.eclipse.sirius.properties.DynamicMappingIf; +import org.eclipse.sirius.properties.GroupDescription; +import org.eclipse.sirius.properties.GroupStyle; +import org.eclipse.sirius.properties.OperationDescription; +import org.eclipse.sirius.properties.PageDescription; +import org.eclipse.sirius.properties.PropertiesPackage; +import org.eclipse.sirius.properties.WidgetAction; +import org.eclipse.sirius.properties.WidgetConditionalStyle; +import org.eclipse.sirius.properties.WidgetDescription; +import org.eclipse.sirius.properties.WidgetStyle; +import org.eclipse.sirius.properties.util.PropertiesSwitch; + +import com.google.common.collect.Sets; + +/** + * Computes the domainClass (i.e. expected type of the receiver) for any + * interpreted expression defined on properties views elements. Note that this + * does not handle element inside a properties view description which come from + * other Sirius package (e.g. model operations). + * + * @author pcdavid + */ +public class DomainClassSwitch extends PropertiesSwitch<Option<Collection<String>>> { + /** + * Constant used in switches on feature id to consider the case when the + * feature must not be considered. + */ + private static final int DO_NOT_CONSIDER_FEATURE = -1; + + /** + * The feature containing the Interpreted expression. + */ + protected EStructuralFeature feature; + + /** + * Indicates if the feature must be considered. + */ + protected boolean considerFeature; + + /** + * Default constructor. + * + * @param feature + * the feature containing the Interpreted expression + */ + public DomainClassSwitch(EStructuralFeature feature) { + this.feature = feature; + } + + @Override + public Option<Collection<String>> doSwitch(EObject theEObject) { + Option<Collection<String>> doSwitch = super.doSwitch(theEObject); + if (doSwitch != null) { + return doSwitch; + } + Collection<String> defaultResult = Sets.newLinkedHashSet(); + return Options.newSome(defaultResult); + } + + /** + * Changes the behavior of this switch: if true, then the feature will be + * considered to calculate target types; if false, then the feature will be + * ignored. + * + * @param considerFeature + * true if the feature should be considered, false otherwise + */ + public void setConsiderFeature(boolean considerFeature) { + this.considerFeature = considerFeature; + } + + @Override + public Option<Collection<String>> casePageDescription(PageDescription page) { + Option<Collection<String>> result = null; + switch (getFeatureId(page.eClass())) { + case PropertiesPackage.PAGE_DESCRIPTION__SEMANTIC_CANDIDATE_EXPRESSION: + /* + * A page can be activated from almost any kind of input: anything + * selectable from any of the representations defined in the same + * VSM can trigger teh evaluation of the page's + * semanticCandidateExpression. Technically we could compute a union + * of all the relevant semantic types, but in practice there is + * little chance that it would be more useful than just EObject. + */ + Collection<String> target = Sets.newLinkedHashSet(); + target.add(TypeName.EOBJECT_TYPENAME.getCompleteName()); + result = Options.newSome(target); + break; + case PropertiesPackage.PAGE_DESCRIPTION__LABEL_EXPRESSION: + case PropertiesPackage.PAGE_DESCRIPTION__PRECONDITION_EXPRESSION: + case DO_NOT_CONSIDER_FEATURE: + result = Options.newSome(VSMNavigation.getPageDomainClass(page)); + break; + default: + break; + } + return result; + } + + @Override + public Option<Collection<String>> caseGroupDescription(GroupDescription group) { + Option<Collection<String>> result = null; + switch (getFeatureId(group.eClass())) { + case PropertiesPackage.GROUP_DESCRIPTION__SEMANTIC_CANDIDATE_EXPRESSION: + /* + * A Group's semanticCandidateExpression is evaluated from the + * target of its referencing page, which is an instance of the + * page's domainClass. + */ + Collection<String> target = Sets.newLinkedHashSet(); + for (PageDescription page : VSMNavigation.findReferencingPages(group)) { + target.addAll(VSMNavigation.getPageDomainClass(page)); + } + result = Options.newSome(target); + break; + case PropertiesPackage.GROUP_DESCRIPTION__LABEL_EXPRESSION: + case PropertiesPackage.GROUP_DESCRIPTION__PRECONDITION_EXPRESSION: + case DO_NOT_CONSIDER_FEATURE: + result = Options.newSome(VSMNavigation.getGroupDomainClass(group)); + break; + default: + break; + } + return result; + } + + @Override + public Option<Collection<String>> caseWidgetDescription(WidgetDescription object) { + return VSMNavigation.getDomainClassFromContainingGroup(object); + } + + @Override + public Option<Collection<String>> caseWidgetStyle(WidgetStyle object) { + return VSMNavigation.getDomainClassFromContainingGroup(object); + } + + @Override + public Option<Collection<String>> caseWidgetConditionalStyle(WidgetConditionalStyle object) { + return VSMNavigation.getDomainClassFromContainingGroup(object); + } + + @Override + public Option<Collection<String>> caseWidgetAction(WidgetAction object) { + return VSMNavigation.getDomainClassFromContainingGroup(object); + } + + @Override + public Option<Collection<String>> caseGroupStyle(GroupStyle object) { + return VSMNavigation.getDomainClassFromContainingGroup(object); + } + + @Override + public Option<Collection<String>> caseCustomExpression(CustomExpression object) { + return VSMNavigation.getDomainClassFromContainingGroup(object); + } + + @Override + public Option<Collection<String>> caseCustomOperation(CustomOperation object) { + return VSMNavigation.getDomainClassFromContainingGroup(object); + } + + @Override + public Option<Collection<String>> caseOperationDescription(OperationDescription object) { + return VSMNavigation.getDomainClassFromContainingGroup(object); + } + + @Override + public Option<Collection<String>> caseDynamicMappingFor(DynamicMappingFor object) { + return VSMNavigation.getDomainClassFromContainingGroup(object); + } + + @Override + public Option<Collection<String>> caseDynamicMappingIf(DynamicMappingIf object) { + return VSMNavigation.getDomainClassFromContainingGroup(object); + } + + private int getFeatureId(EClass eClass) { + if (considerFeature && feature != null) { + return eClass.getFeatureID(feature); + } else { + return DomainClassSwitch.DO_NOT_CONSIDER_FEATURE; + } + } +} diff --git a/plugins/org.eclipse.sirius.ui.properties/src/org/eclipse/sirius/ui/properties/internal/expressions/PropertiesExpressionQueryProvider.java b/plugins/org.eclipse.sirius.ui.properties/src/org/eclipse/sirius/ui/properties/internal/expressions/PropertiesExpressionQueryProvider.java new file mode 100644 index 0000000000..74e5143bee --- /dev/null +++ b/plugins/org.eclipse.sirius.ui.properties/src/org/eclipse/sirius/ui/properties/internal/expressions/PropertiesExpressionQueryProvider.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2016 Obeo. + * 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 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.ui.properties.internal.expressions; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.sirius.business.api.dialect.description.IInterpretedExpressionQuery; +import org.eclipse.sirius.business.api.dialect.description.IInterpretedExpressionQueryProvider; +import org.eclipse.sirius.ext.base.Option; +import org.eclipse.sirius.ext.base.Options; + +/** + * An {@link IInterpretedExpressionQueryProvider} for properties view + * description expressions. + * + * @author pcdavid + */ +public class PropertiesExpressionQueryProvider implements IInterpretedExpressionQueryProvider { + @Override + public Option<IInterpretedExpressionQuery> getExpressionQueryFor(EObject context, EStructuralFeature expressionAttribute) { + if (VSMNavigation.isInsideViewExtensionDescription(context)) { + IInterpretedExpressionQuery value = new PropertiesInterpretedExpressionQuery(context, expressionAttribute); + return Options.newSome(value); + } else { + return Options.newNone(); + } + } +} diff --git a/plugins/org.eclipse.sirius.ui.properties/src/org/eclipse/sirius/ui/properties/internal/expressions/PropertiesInterpretedExpressionQuery.java b/plugins/org.eclipse.sirius.ui.properties/src/org/eclipse/sirius/ui/properties/internal/expressions/PropertiesInterpretedExpressionQuery.java new file mode 100644 index 0000000000..e20f75e349 --- /dev/null +++ b/plugins/org.eclipse.sirius.ui.properties/src/org/eclipse/sirius/ui/properties/internal/expressions/PropertiesInterpretedExpressionQuery.java @@ -0,0 +1,360 @@ +/******************************************************************************* + * Copyright (c) 2016 Obeo. + * 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 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.ui.properties.internal.expressions; + +import java.util.Collection; +import java.util.Map; + +import org.eclipse.eef.common.api.utils.Util; +import org.eclipse.eef.core.api.EEFExpressionUtils; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EPackage.Registry; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.sirius.business.api.dialect.description.AbstractInterpretedExpressionQuery; +import org.eclipse.sirius.business.api.dialect.description.IInterpretedExpressionQuery; +import org.eclipse.sirius.business.api.dialect.description.IInterpretedExpressionTargetSwitch; +import org.eclipse.sirius.business.api.dialect.description.MultiLanguagesValidator; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.business.internal.dialect.description.ToolInterpretedExpressionTargetSwitch; +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreterContext; +import org.eclipse.sirius.common.tools.api.interpreter.TypeName; +import org.eclipse.sirius.common.tools.api.interpreter.ValidationResult; +import org.eclipse.sirius.common.tools.api.interpreter.VariableType; +import org.eclipse.sirius.ext.base.Option; +import org.eclipse.sirius.ext.base.Options; +import org.eclipse.sirius.properties.DynamicMappingFor; +import org.eclipse.sirius.properties.GroupDescription; +import org.eclipse.sirius.properties.PageDescription; +import org.eclipse.sirius.properties.PropertiesPackage; +import org.eclipse.sirius.properties.ViewExtensionDescription; +import org.eclipse.sirius.tools.api.interpreter.context.SiriusInterpreterContextFactory; +import org.eclipse.sirius.ui.properties.internal.SiriusInputDescriptor; +import org.eclipse.sirius.ui.properties.internal.SiriusToolServices; +import org.eclipse.sirius.viewpoint.ViewpointPackage; +import org.eclipse.sirius.viewpoint.description.DescriptionPackage; +import org.eclipse.sirius.viewpoint.description.Extension; +import org.eclipse.sirius.viewpoint.description.RepresentationDescription; +import org.eclipse.sirius.viewpoint.description.tool.InitialOperation; +import org.eclipse.sirius.viewpoint.description.tool.ToolPackage; +import org.eclipse.sirius.viewpoint.description.validation.ValidationPackage; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +/** + * An {@code IInterpretedExpressionQuery} for expressions occuring inside + * properties view descriptions. + * + * @author pcdavid + */ +public final class PropertiesInterpretedExpressionQuery extends AbstractInterpretedExpressionQuery implements IInterpretedExpressionQuery { + private Collection<EPackage> packagesToImport; + + /** + * Constructor. + * + * @param target + * the VSM element on which the expression appears. + * @param expressionAttribute + * the attribute of the VSM element which defines the expression + * (assumed to be an InterpredExpression). + */ + public PropertiesInterpretedExpressionQuery(EObject target, EStructuralFeature expressionAttribute) { + super(target, expressionAttribute); + } + + @Override + protected void initializeTargetSwitch() { + this.targetSwitch = new PropertiesExpressionsGlobalTargetSwitch(feature); + } + + @Override + public Collection<EPackage> getPackagesToImport() { + /* + * We can't rely on the default implementation here, as it assumes we + * are inside a RepresentationDescription, which is not the case for + * properties definitions. + */ + if (packagesToImport == null) { + packagesToImport = PropertiesInterpretedExpressionQuery.getEPackagesInScope(target); + } + return packagesToImport; + } + + @Override + public Collection<String> getDependencies() { + /* + * We can't rely on the default implementation here, as it assumes we + * are inside a Viewpoint, which is not the case for properties + * definitions. + */ + if (dependencies == null) { + Collection<String> result = Lists.newArrayList(VSMNavigation.getJavaExtensionsInVSM(target)); + // Make sure the implicitly registered SiriusToolServices class is + // also visible. + result.add(SiriusToolServices.class.getName()); + dependencies = result; + } + return dependencies; + } + + private static Collection<EPackage> getEPackagesInScope(EObject target) { + Collection<EPackage> result = Sets.newLinkedHashSet(); + + boolean needsGlobalPackages = false; + for (RepresentationDescription desc : VSMNavigation.getRepresentationDescriptionsInVSM(target)) { + EList<EPackage> configured = desc.getMetamodel(); + result.addAll(configured); + if (configured.isEmpty()) { + /* + * If at least one of the possible source representations has no + * explicitly configured metamodel, we must include the globally + * registered packages. + */ + needsGlobalPackages = true; + } + } + + if (needsGlobalPackages) { + result.addAll(PropertiesInterpretedExpressionQuery.getAllRegisteredEPackages(EPackage.Registry.INSTANCE)); + } + + // Also add metamodels explicitly added to the ViewExtensionDescription, + // if any. + Option<EObject> viewDescriptionOpt = new EObjectQuery(target).getFirstAncestorOfType(PropertiesPackage.Literals.VIEW_EXTENSION_DESCRIPTION); + if (viewDescriptionOpt.some()) { + ViewExtensionDescription ved = (ViewExtensionDescription) viewDescriptionOpt.get(); + result.addAll(ved.getMetamodels()); + } + + // In all cases, we make sure the core Sirius metamodels are present... + result.add(EcorePackage.eINSTANCE); + result.add(ViewpointPackage.eINSTANCE); + result.add(DescriptionPackage.eINSTANCE); + result.add(ToolPackage.eINSTANCE); + result.add(ValidationPackage.eINSTANCE); + // ... and the properties view one too. + result.add(PropertiesPackage.eINSTANCE); + + return result; + } + + private static Collection<EPackage> getAllRegisteredEPackages(Registry source) { + Collection<EPackage> result = Sets.newLinkedHashSet(); + for (String nsURI : Sets.newLinkedHashSet(source.keySet())) { + try { + result.add(source.getEPackage(nsURI)); + // CHECKSTYLE:OFF + } catch (Throwable e) { + /* + * anything might happen here depending on the other Eclipse + * tools, and we've seen many time tools breaking all the + * others. + */ + // CHECKSTYLE:ON + } + } + return result; + } + + @Override + public Map<String, VariableType> getAvailableVariables() { + if (availableVariables == null) { + availableVariables = Maps.newLinkedHashMap(); + } + boolean isPropertiesElement = target.eClass().getEPackage() == PropertiesPackage.eINSTANCE; + if (isPropertiesElement) { + // "input" is always available. + availableVariables.put(EEFExpressionUtils.INPUT, VariableType.fromString(SiriusInputDescriptor.class.getName())); + + if (feature == PropertiesPackage.Literals.SELECT_DESCRIPTION__CANDIDATE_DISPLAY_EXPRESSION) { + VariableType candidatesExpressionType = getResultType(target, PropertiesPackage.Literals.SELECT_DESCRIPTION__CANDIDATES_EXPRESSION); + availableVariables.put(EEFExpressionUtils.EEFSelect.CANDIDATE, candidatesExpressionType); + } else if (feature == PropertiesPackage.Literals.RADIO_DESCRIPTION__CANDIDATE_DISPLAY_EXPRESSION) { + VariableType candidatesExpressionType = getResultType(target, PropertiesPackage.Literals.RADIO_DESCRIPTION__CANDIDATES_EXPRESSION); + availableVariables.put(EEFExpressionUtils.EEFSelect.CANDIDATE, candidatesExpressionType); + } else if (feature == PropertiesPackage.Literals.REFERENCE_DESCRIPTION__DISPLAY_EXPRESSION) { + VariableType valueExpressionType = getResultType(target, PropertiesPackage.Literals.REFERENCE_DESCRIPTION__VALUE_EXPRESSION); + availableVariables.put(EEFExpressionUtils.EEFSelect.CANDIDATE, valueExpressionType); + } else if (feature == PropertiesPackage.Literals.DYNAMIC_MAPPING_IF__PREDICATE_EXPRESSION) { + if (target.eContainer() instanceof DynamicMappingFor) { + DynamicMappingFor forDefinition = (DynamicMappingFor) target.eContainer(); + String iteratorName = forDefinition.getIterator(); + if (!Util.isBlank(iteratorName)) { + VariableType iteratorType = getResultType(forDefinition, PropertiesPackage.Literals.DYNAMIC_MAPPING_FOR__DOMAIN_CLASS_EXPRESSION); + availableVariables.put(iteratorName, iteratorType); + } + } + } + return availableVariables; + } else { + return super.getAvailableVariables(); + } + } + + @Override + protected Option<EObject> getToolContext() { + Option<EObject> result = super.getToolContext(); + if (!result.some()) { + if (target instanceof PageDescription || target instanceof GroupDescription) { + result = Options.newSome(target); + } else { + result = new EObjectQuery(target).getFirstAncestorOfType(ToolPackage.Literals.INITIAL_OPERATION); + } + } + return result; + } + + @Override + protected void addVariablesFromToolContext(EObject toolContext) { + super.addVariablesFromToolContext(toolContext); + availableVariables.put(EEFExpressionUtils.INPUT, VariableType.fromString(SiriusInputDescriptor.class.getName())); + if (toolContext instanceof InitialOperation) { + EReference callbackFeature = toolContext.eContainmentFeature(); + VariableType stringType = VariableType.fromString("java.lang.String"); //$NON-NLS-1$ + VariableType booleanType = VariableType.fromString("java.lang.Boolean"); //$NON-NLS-1$ + VariableType unkownType = VariableType.fromString(TypeName.EOBJECT_TYPENAME.getCompleteName()); + if (callbackFeature == PropertiesPackage.Literals.TEXT_DESCRIPTION__INITIAL_OPERATION) { + availableVariables.put(EEFExpressionUtils.EEFText.NEW_VALUE, stringType); + } else if (callbackFeature == PropertiesPackage.Literals.CHECKBOX_DESCRIPTION__INITIAL_OPERATION) { + availableVariables.put(EEFExpressionUtils.EEFCheckbox.NEW_VALUE, booleanType); + } else if (callbackFeature == PropertiesPackage.Literals.HYPERLINK_DESCRIPTION__INITIAL_OPERATION) { + Option<Collection<String>> domainClass = VSMNavigation.getDomainClassFromContainingGroup(toolContext); + if (domainClass.some()) { + availableVariables.put(EEFExpressionUtils.EEFHyperlink.SELECTION, VariableType.fromStrings(domainClass.get())); + } else { + availableVariables.put(EEFExpressionUtils.EEFHyperlink.SELECTION, unkownType); + } + } else if (callbackFeature == PropertiesPackage.Literals.RADIO_DESCRIPTION__INITIAL_OPERATION) { + availableVariables.put(EEFExpressionUtils.EEFText.NEW_VALUE, stringType); + } else if (callbackFeature == PropertiesPackage.Literals.REFERENCE_DESCRIPTION__ON_CLICK_OPERATION) { + Option<Collection<String>> domainClass = VSMNavigation.getDomainClassFromContainingGroup(toolContext); + if (domainClass.some()) { + availableVariables.put(EEFExpressionUtils.EEFReference.SELECTION, VariableType.fromStrings(domainClass.get())); + } else { + availableVariables.put(EEFExpressionUtils.EEFHyperlink.SELECTION, unkownType); + } + availableVariables.put(EEFExpressionUtils.EEFReference.ON_CLICK_EVENT_KIND, stringType); + } else if (callbackFeature == PropertiesPackage.Literals.WIDGET_ACTION__INITIAL_OPERATION) { + Option<Collection<String>> domainClass = VSMNavigation.getDomainClassFromContainingGroup(toolContext); + if (domainClass.some()) { + availableVariables.put(EEFExpressionUtils.EEFReference.SELECTION, VariableType.fromStrings(domainClass.get())); + } else { + availableVariables.put(EEFExpressionUtils.EEFHyperlink.SELECTION, unkownType); + } + } else if (callbackFeature == PropertiesPackage.Literals.SELECT_DESCRIPTION__INITIAL_OPERATION) { + availableVariables.put(EEFExpressionUtils.EEFText.NEW_VALUE, stringType); + } + } + } + + private VariableType getResultType(EObject owner, EAttribute attr) { + IInterpreterContext context = SiriusInterpreterContextFactory.createInterpreterContext(owner, attr); + ValidationResult res = MultiLanguagesValidator.getInstance().validateExpression(context, (String) owner.eGet(attr)); + return res.getReturnTypes(); + } + + /** + * The switch used to compute domainClasses for expressions used in + * properties definitions. + * + * @author pcdavid + */ + private static class PropertiesExpressionsGlobalTargetSwitch implements IInterpretedExpressionTargetSwitch { + /** + * The switch for properties-specific expressions. + */ + private final DomainClassSwitch propertiesSwitch; + + /** + * The switch we delegate to for Model Operations. + */ + private final ToolInterpretedExpressionTargetSwitch delegateSwitch; + + /** + * By default ToolInterpretedExpressionTargetSwitch assumes operations + * will appear inside representations or mappings, so we override + * getFirstContextChangingContainer() to locate the parent + * GroupDescription instead. + */ + private static class CustomToolInterpretedExpressionTargetSwitch extends ToolInterpretedExpressionTargetSwitch { + public CustomToolInterpretedExpressionTargetSwitch(EStructuralFeature feature, IInterpretedExpressionTargetSwitch defaultSwitch) { + super(feature, defaultSwitch); + } + + @Override + protected EObject getFirstContextChangingContainer(EObject element) { + EObject defaultResult = super.getFirstContextChangingContainer(element); + if (defaultResult instanceof Extension) { + /* + * The generic algorithm in the super-class does not know + * anything about the properties metamodel but will stop at + * the top-level ViewExtensionDescription as it is an + * Extension. + */ + return VSMNavigation.findClosestGroupDescription(element); + } else { + return defaultResult; + } + } + } + + public PropertiesExpressionsGlobalTargetSwitch(EStructuralFeature feature) { + this.propertiesSwitch = new DomainClassSwitch(feature); + this.delegateSwitch = new CustomToolInterpretedExpressionTargetSwitch(feature, this); + } + + @Override + public Option<Collection<String>> doSwitch(EObject target, boolean considerFeature) { + Collection<String> targetTypes = Sets.newLinkedHashSet(); + Option<Collection<String>> expressionTarget = Options.newSome(targetTypes); + if (target != null) { + if (expressionTarget.some() && expressionTarget.get().isEmpty()) { + propertiesSwitch.setConsiderFeature(considerFeature); + expressionTarget = propertiesSwitch.doSwitch(target); + } + if (expressionTarget.some() && expressionTarget.get().isEmpty()) { + delegateSwitch.setConsiderFeature(considerFeature); + expressionTarget = delegateSwitch.doSwitch(target); + } + } + return expressionTarget; + } + + @Override + public EObject getFirstRelevantContainer(EObject obj) { + if (obj != null) { + EObject container = obj.eContainer(); + while (container != null && !isRelevant(container)) { + container = container.eContainer(); + } + return container; + } else { + return null; + } + } + + /** + * In this context, relevant containers are top-level pages and groups + * only. The root ViewExtensionDescriptions do not define anything + * relevant for domain class computation. + */ + private boolean isRelevant(EObject container) { + return container instanceof PageDescription || container instanceof GroupDescription; + } + } +} diff --git a/plugins/org.eclipse.sirius.ui.properties/src/org/eclipse/sirius/ui/properties/internal/expressions/VSMNavigation.java b/plugins/org.eclipse.sirius.ui.properties/src/org/eclipse/sirius/ui/properties/internal/expressions/VSMNavigation.java new file mode 100644 index 0000000000..2513a251d2 --- /dev/null +++ b/plugins/org.eclipse.sirius.ui.properties/src/org/eclipse/sirius/ui/properties/internal/expressions/VSMNavigation.java @@ -0,0 +1,208 @@ +/******************************************************************************* + * Copyright (c) 2016 Obeo. + * 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 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.ui.properties.internal.expressions; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +import org.eclipse.eef.common.api.utils.Util; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.common.tools.api.interpreter.TypeName; +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.ext.base.Option; +import org.eclipse.sirius.ext.base.Options; +import org.eclipse.sirius.properties.GroupDescription; +import org.eclipse.sirius.properties.PageDescription; +import org.eclipse.sirius.properties.PropertiesPackage; +import org.eclipse.sirius.properties.ViewExtensionDescription; +import org.eclipse.sirius.viewpoint.description.DescriptionPackage; +import org.eclipse.sirius.viewpoint.description.Group; +import org.eclipse.sirius.viewpoint.description.JavaExtension; +import org.eclipse.sirius.viewpoint.description.RepresentationDescription; +import org.eclipse.sirius.viewpoint.description.Viewpoint; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +/** + * Utility methods to navigate inside a VSM, especially wrt properties + * descriptions. + * + * @author pcdavid + */ +public final class VSMNavigation { + private VSMNavigation() { + // Preven instanciation. + } + + /** + * Tests whether a model element is part of a Sirius properties view + * description. + * + * @param vsmElement + * the element to test. + * @return <code>true</code> if the element is part of a Sirius properties + * view description. + */ + public static boolean isInsideViewExtensionDescription(EObject vsmElement) { + return new EObjectQuery(vsmElement).getFirstAncestorOfType(PropertiesPackage.Literals.VIEW_EXTENSION_DESCRIPTION).some(); + } + + /** + * Returns the domain class of a given {@link PageDescription}, defaulting + * to a generic catch-all type if no value was set explicitly. + * + * @param page + * a {@link PageDescription}. + * @return the names of potential domain classes for that page. + */ + public static Collection<String> getPageDomainClass(PageDescription page) { + if (page != null && !Util.isBlank(page.getDomainClass())) { + return Collections.singleton(page.getDomainClass()); + } else { + return Collections.singleton(TypeName.EOBJECT_TYPENAME.getCompleteName()); + } + } + + /** + * Returns the domain class of a given {@link GroupDescription}, defaulting + * to the union of all the possible domain classes from pages which + * reference that group if no value was set explicitly. + * + * @param group + * a {@link GroupDescription}. + * @return the names of potential domain classes for that group. + */ + public static Collection<String> getGroupDomainClass(GroupDescription group) { + if (group != null && !Util.isBlank(group.getDomainClass())) { + return Collections.singleton(group.getDomainClass()); + } else { + Collection<String> result = Sets.newLinkedHashSet(); + for (PageDescription page : VSMNavigation.findReferencingPages(group)) { + result.addAll(getPageDomainClass(page)); + } + return result; + } + } + + /** + * Returns the domain class of a VSM element from inside a + * {@link GroupDescription} (for example a widget). + * + * @param vsmElement + * the VSM element. + * @return the domain class of the VSM element, as determined by the + * enclosing {@link GroupDescription}. + */ + public static Option<Collection<String>> getDomainClassFromContainingGroup(EObject vsmElement) { + Option<Collection<String>> result = Options.newNone(); + GroupDescription group = VSMNavigation.findClosestGroupDescription(vsmElement); + if (group != null) { + result = Options.newSome(getGroupDomainClass(group)); + } + return result; + } + + /** + * Get all the representation description defined in the same VSM as a given + * element. + * + * @param vsmElement + * a VSM element. + * @return all the representation description defined in the same VSM. + */ + public static Collection<RepresentationDescription> getRepresentationDescriptionsInVSM(EObject vsmElement) { + Collection<RepresentationDescription> result = Lists.newArrayList(); + Option<EObject> answer = getVSMRoot(vsmElement); + if (answer.some()) { + Group group = (Group) answer.get(); + for (Viewpoint viewpoint : group.getOwnedViewpoints()) { + result.addAll(viewpoint.getOwnedRepresentations()); + } + } + return result; + } + + /** + * Finds all the Java extensions registered in the VSM of the specified + * element. + * + * @param vsmElement + * an element from a VSM model. + * @return the qualified names of all the Java extensions registered in the + * same VSM. + */ + public static Collection<String> getJavaExtensionsInVSM(EObject vsmElement) { + Collection<String> result = Lists.newArrayList(); + Option<EObject> answer = getVSMRoot(vsmElement); + if (answer.some()) { + Group group = (Group) answer.get(); + for (Viewpoint vp : group.getOwnedViewpoints()) { + for (JavaExtension dep : vp.getOwnedJavaExtensions()) { + if (!StringUtil.isEmpty(dep.getQualifiedClassName())) { + result.add(dep.getQualifiedClassName()); + } + } + } + } + return result; + } + + private static Option<EObject> getVSMRoot(EObject vsmElement) { + return new EObjectQuery(vsmElement).getFirstAncestorOfType(DescriptionPackage.Literals.GROUP); + } + + /** + * Find which pages reference a given group in a VSM. Groups are not + * contained insides pages, but referenced from pages defined inside the + * same {@link ViewExtensionDescription}. + * + * @param group + * a group. + * @return all the pages inside the same ViewExtensionDescription as the + * group that reference it. + */ + public static Set<PageDescription> findReferencingPages(GroupDescription group) { + EObject container = group.eContainer(); + if (container instanceof ViewExtensionDescription) { + ViewExtensionDescription ved = (ViewExtensionDescription) container; + Set<PageDescription> result = Sets.newLinkedHashSet(); + for (PageDescription page : ved.getPages()) { + if (page.getGroups().contains(group)) { + result.add(page); + } + } + return result; + } else { + return Collections.emptySet(); + } + } + + /** + * Returns the {@link GroupDescription} enclosing a given VSM element, if + * any. + * + * @param vsmElement + * a VSM element. + * @return the {@link GroupDescription} enclosing the element, or + * <code>null</code> if none could be found. + */ + public static GroupDescription findClosestGroupDescription(EObject vsmElement) { + if (vsmElement instanceof GroupDescription) { + return (GroupDescription) vsmElement; + } else { + Option<EObject> answer = new EObjectQuery(vsmElement).getFirstAncestorOfType(PropertiesPackage.Literals.GROUP_DESCRIPTION); + return answer.some() ? (GroupDescription) answer.get() : null; + } + } +} |
