Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/facet/org.eclipse.papyrus.emf.facet.efacet.core')
-rw-r--r--plugins/facet/org.eclipse.papyrus.emf.facet.efacet.core/src/org/eclipse/papyrus/emf/facet/efacet/core/internal/FacetCache.java82
-rw-r--r--plugins/facet/org.eclipse.papyrus.emf.facet.efacet.core/src/org/eclipse/papyrus/emf/facet/efacet/core/internal/FacetManagerContext.java1192
2 files changed, 722 insertions, 552 deletions
diff --git a/plugins/facet/org.eclipse.papyrus.emf.facet.efacet.core/src/org/eclipse/papyrus/emf/facet/efacet/core/internal/FacetCache.java b/plugins/facet/org.eclipse.papyrus.emf.facet.efacet.core/src/org/eclipse/papyrus/emf/facet/efacet/core/internal/FacetCache.java
new file mode 100644
index 00000000000..dae45d3d158
--- /dev/null
+++ b/plugins/facet/org.eclipse.papyrus.emf.facet.efacet.core/src/org/eclipse/papyrus/emf/facet/efacet/core/internal/FacetCache.java
@@ -0,0 +1,82 @@
+/*****************************************************************************
+ * Copyright (c) 2014 CEA LIST 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
+ *
+ * Contributors:
+ * CEA LIST - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.emf.facet.efacet.core.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.common.notify.impl.AdapterImpl;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.ETypedElement;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+
+/**
+ * A cache of resolved facet overrides for an {@link EObject}. It expunges itself automatically when
+ * it detects that it is out-of-date with respect to the {@link FacetManagerContext}.
+ */
+class FacetCache extends AdapterImpl {
+
+ private final FacetManagerContext context;
+
+ private long currentGeneration = -1;
+
+ private final Map<ETypedElement, ETypedElement> overrides = new HashMap<ETypedElement, ETypedElement>();
+
+ FacetCache(EObject owner, FacetManagerContext context) {
+ this.context = context;
+ owner.eAdapters().add(this);
+ }
+
+ static FacetCache getInstance(EObject element, FacetManagerContext context) {
+ FacetCache result = (FacetCache) EcoreUtil.getExistingAdapter(element, context);
+
+ if (result == null) {
+ result = new FacetCache(element, context);
+ }
+
+ return result;
+ }
+
+ @Override
+ public boolean isAdapterForType(Object type) {
+ return type == context;
+ }
+
+ @Override
+ public void notifyChanged(Notification msg) {
+ if (!msg.isTouch()) {
+ // It could be that some facet/customization predicate has changed for me. Purge the cache
+ currentGeneration = -1;
+ }
+ }
+
+ public <T extends ETypedElement> T resolve(T feature) {
+ if (context.facetGeneration > currentGeneration) {
+ // Purge and recompute as needed
+ overrides.clear();
+ currentGeneration = context.facetGeneration;
+ }
+
+ // This should be safe because we should only add mappings of matching types, although this cannot
+ // actually be enforced in any way
+ @SuppressWarnings("unchecked")
+ T result = (T) overrides.get(feature);
+ return result;
+ }
+
+ <T extends ETypedElement> void add(T feature, T override) {
+ overrides.put(feature, override);
+ }
+}
diff --git a/plugins/facet/org.eclipse.papyrus.emf.facet.efacet.core/src/org/eclipse/papyrus/emf/facet/efacet/core/internal/FacetManagerContext.java b/plugins/facet/org.eclipse.papyrus.emf.facet.efacet.core/src/org/eclipse/papyrus/emf/facet/efacet/core/internal/FacetManagerContext.java
index 6cf384d9dd0..21e57baef52 100644
--- a/plugins/facet/org.eclipse.papyrus.emf.facet.efacet.core/src/org/eclipse/papyrus/emf/facet/efacet/core/internal/FacetManagerContext.java
+++ b/plugins/facet/org.eclipse.papyrus.emf.facet.efacet.core/src/org/eclipse/papyrus/emf/facet/efacet/core/internal/FacetManagerContext.java
@@ -1,552 +1,640 @@
-/*******************************************************************************
- * Copyright (c) 2011 Mia-Software
- * 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:
- * Olivier Remaud (Soft-Maint) - Bug 361794 - [Restructuring] EMF Facet customization meta-model
- * Gregoire Dupe (Mia-Software) - Bug 364325 - [Restructuring] The user must be able to navigate into a model using the Facet.
- * Gregoire Dupe (Mia-Software) - Bug 361794 - [Restructuring] EMF Facet customization meta-model
- * Gregoire Dupe (Mia-Software) - Bug 373078 - API Cleaning
- * Gregoire Dupe (Mia-Software) - Bug 375087 - [Table] ITableWidget.addColumn(List<ETypedElement>, List<FacetSet>)
- * Gregoire Dupe (Mia-Software) - Bug 372626 - Aggregates
- * Gregoire Dupe (Mia-Software) - Bug 377178 - [EFacet] infinite recursion in override resolution
- * Gregoire Dupe (Mia-software) - Bug 383418 - [Table] FacetManagerContext.getOverrideCandidateFeatures(...) is empty
- * Gregoire Dupe (Mia-software) - Bug 420093 - [EFacet] The facetManger list doesn't deal with uniqueness
- * Thomas Cicognani (Soft-Maint) - Bug 420193 - Listener on FacetManager
- * Fabien Treguer (Soft-Maint) - Bug 423285 - [Table] FacetSets not stored in a resource cause model manager crashes
- *******************************************************************************/
-
-package org.eclipse.papyrus.emf.facet.efacet.core.internal;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Set;
-
-import org.eclipse.emf.common.util.EList;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.EOperation;
-import org.eclipse.emf.ecore.ETypedElement;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecore.resource.ResourceSet;
-import org.eclipse.emf.ecore.util.EcoreUtil;
-import org.eclipse.papyrus.emf.facet.efacet.core.FacetUtils;
-import org.eclipse.papyrus.emf.facet.efacet.core.IFacetManagerListener;
-import org.eclipse.papyrus.emf.facet.efacet.core.exception.FacetManagerException;
-import org.eclipse.papyrus.emf.facet.efacet.core.internal.FacetManager.ConformanceState;
-import org.eclipse.papyrus.emf.facet.efacet.core.internal.exception.FacetConformanceEvaluationException;
-import org.eclipse.papyrus.emf.facet.efacet.core.internal.exception.UnmatchingExpectedTypeException;
-import org.eclipse.papyrus.emf.facet.efacet.core.internal.exported.IResolverManager;
-import org.eclipse.papyrus.emf.facet.efacet.metamodel.v0_2_0.efacet.DerivedTypedElement;
-import org.eclipse.papyrus.emf.facet.efacet.metamodel.v0_2_0.efacet.Facet;
-import org.eclipse.papyrus.emf.facet.efacet.metamodel.v0_2_0.efacet.FacetSet;
-import org.eclipse.papyrus.emf.facet.util.core.Logger;
-import org.eclipse.papyrus.emf.facet.util.core.internal.exported.ListUtils;
-import org.eclipse.papyrus.emf.facet.util.emf.core.ModelUtils;
-
-/**
- * @author oremaud
- *
- * FacetManager Context
- *
- * Defines which FacetSets will be taken into account, and in which
- * order ('front' FacetSets have higher precedence)
- *
- * Responsible of overrides resolution for Facets and Customs
- */
-class FacetManagerContext implements List<FacetSet> {
-
- private static final String SILENT_OPTION = "org.eclipse.papyrus.emf.facet.efacet.core.internal.FacetManagerContext.getOverrideCandidateFeatures.silent"; //$NON-NLS-1$
- private static final boolean SILENT = Boolean
- .getBoolean(FacetManagerContext.SILENT_OPTION);
-
- // We cannot use the interface (i.e, List) instead because we need to use
- // the methods addLast and addFirst
- private LinkedList<FacetSet> managedFacetSets = new LinkedList<FacetSet>(); // NOPMD by gdupe on 15/03/12 10:36
- private final transient FacetManager manager;
- /**
- * This field is used to avoid to have to many error messages in the log.
- */
- private final Set<ETypedElement> failingFeatures = new HashSet<ETypedElement>();
-
- private final Set<IFacetManagerListener> listeners = new HashSet<IFacetManagerListener>();
-
- public FacetManagerContext(final FacetManager manager) {
- this.manager = manager;
- }
-
- /**
- *
- * @param baseFeature
- * @param eObject
- * @return
- * @throws FacetConformanceEvaluationException
- * @throws UnmatchingExpectedTypeException
- * @throws FacetManagerException
- */
- public <T extends ETypedElement> T resolveOverrides(final T baseFeature,
- final EObject eObject) throws FacetManagerException {
- T result = baseFeature;
- if (baseFeature instanceof DerivedTypedElement) {
- final DerivedTypedElement derivedResult = resolveOverrides(
- (DerivedTypedElement) baseFeature, eObject);
- if (derivedResult != null
- && !(derivedResult instanceof ETypedElement)) {
- throw new UnmatchingExpectedTypeException(
- "Type mismatch in override resolution '" + baseFeature.getName() + "'"); //$NON-NLS-1$ //$NON-NLS-2$
- }
- result = (T) derivedResult;
- }
- // If nothing is found, return the original basefeature
- if (result == null) {
- result = baseFeature;
- }
- return result;
- }
-
- /**
- *
- * @param baseFeature
- * @param eObject
- * @return
- * @throws FacetConformanceEvaluationException
- * @throws FacetManagerException
- */
- public <T extends DerivedTypedElement> T resolveOverrides(
- final T baseFeature, final EObject eObject)
- throws FacetManagerException {
- try {
- // -- Find master override
- // If the baseFeature overrides another feature, then we follow the
- // override chain to find the top feature
- // The main idea behind this is that the baseFeature does not really
- // represents a specific feature but a
- // feature signature (as in java method invocation)
- final T signatureFeature = FacetUtils
- .getTopOverrideFeature(baseFeature);
-
- // -- Find all candidates
- // Get all Facet referenced by the FacetManager to get all features
- // matching this signature
- // Note : candidates are searched ine the order provided by the current
- // FacetManager,
- // so they are already ordered by the wanted priority
- // XXX : Debug check that DerivedTypedElement only contained by Facet
- final List<T> orderedCandidates = getOverrideCandidateFeatures(eObject,
- signatureFeature);
-
- // -- Find the most specific feature
- return findMostSpecificFeature(orderedCandidates);
- } catch (Exception e) {
- throw new FacetManagerException(e);
- }
- }
-
- public List<FacetSet> getManagedFacetSets() {
- return Collections.unmodifiableList(this.managedFacetSets);
- }
-
- public void setManagedFacetSets(final List<FacetSet> facetSets) {
- this.managedFacetSets = new LinkedList<FacetSet>(facetSets);
- notifyListeners();
- }
-
- public void addBackManagedFacetSet(final FacetSet facetSet) {
- // adding an already managed FacetSet again moves it to the right position
- this.managedFacetSets.remove(facetSet);
- this.managedFacetSets.addLast(facetSet);
- notifyListeners();
- }
-
- public void addFrontManagedFacetSet(final FacetSet facetSet) {
- if (this.managedFacetSets == null) {
- this.managedFacetSets = new LinkedList<FacetSet>();
- }
- // adding an already managed FacetSet again moves it to the right position
- this.managedFacetSets.remove(facetSet);
- this.managedFacetSets.addFirst(facetSet);
- notifyListeners();
- }
-
- /**
- * Find the most specific feature in the candidates list. The most specific
- * is the feature that : - is the most specific (in terms of Facet
- * inheritance) : the lowest in the inheritance tree (per branch) - is
- * encountered first. The order is given by the FacetManager context.
- *
- * @param candidates
- * The list of candidates features. Can be empty.
- * @return
- */
- private static <T extends DerivedTypedElement> T findMostSpecificFeature(
- final List<T> orderedCandidates) {
- T result = null;
- if (!orderedCandidates.isEmpty()) {
- final Iterator<T> candidatesIt = orderedCandidates.iterator();
- result = candidatesIt.next();
- // We have the highest priority feature, we just have to check
- // that there is no more specific one in the override chain (from
- // top to bottom)
- while (candidatesIt.hasNext()) {
- final T candidate = candidatesIt.next();
- if (isOverridenBy(candidate, result)) {
- result = candidate;
- }
- }
- }
- return result;
- }
-
- /**
- * Find matching candidates.
- *
- * @param eObject
- * @param baseFeature
- * @return A list of candidates, in the right order for conflict resolution
- * @throws FacetManagerException
- */
- private <T extends DerivedTypedElement> List<T> getOverrideCandidateFeatures(
- final EObject eObject, final T baseFeature)
- throws FacetManagerException {
- final ResourceSet baserFeatureRS = baseFeature.eResource().getResourceSet();
- final List<T> result = new LinkedList<T>();
- // Iterate over all Facets from all facetSets to find conforming
- // features
- final List<FacetSet> managedFSets = getManagedFacetSets();
- final List<FacetSet> allFacetSets = new ArrayList<FacetSet>(managedFSets);
- // add aggregated FacetSets
- for (FacetSet facetSet : managedFSets) {
- // FIXME Should handle recursive containment.
- for (FacetSet subFacetSet : facetSet.getFacetSets()) {
- final FacetSet resolvedFacetSet = IResolverManager.DEFAULT
- .resolve(subFacetSet, FacetSet.class);
- allFacetSets.add(resolvedFacetSet);
- }
- }
- for (FacetSet facetSet : allFacetSets) {
- final Resource resource = facetSet.eResource();
- if (resource == null) {
- final String message = String.format("The facetSet %s (%s) is not stored in a resource.", //$NON-NLS-1$
- facetSet.getName(), facetSet.getNsURI());
- Logger.logWarning(message, Activator.getDefault());
- } else {
- final ResourceSet facetSetRS = resource.getResourceSet();
- if (!facetSetRS.equals(baserFeatureRS)) {
- Logger.logWarning("The facet manager is dealing with more than one resource set.", //$NON-NLS-1$
- Activator.getDefault());
- }
- }
- for (Facet facet : FacetUtils.getFacets(facetSet)) {
- final T matchingFeature = getMatchingFeature(eObject, facet,
- baseFeature);
- if (matchingFeature != null) {
- result.add(matchingFeature);
- }
- }
- }
- if (result.isEmpty()) {
- if (!this.failingFeatures.contains(baseFeature)
- && !FacetManagerContext.SILENT) {
- Logger.logWarning(
- "The result of " //$NON-NLS-1$
- + this.getClass().getSimpleName()
- + ".getOverrideCandidateFeatures(...) is empty! baseFeature=" //$NON-NLS-1$
- + EcoreUtil.getURI(baseFeature)
- + " (This message will be sent only once)", //$NON-NLS-1$
- Activator.getDefault());
- // This avoid to have to many error messages in the log.
- this.failingFeatures.add(baseFeature);
- }
- result.add(baseFeature);
- }
- return result;
- }
-
- /**
- * Test whether a feature is overridden by another (directly or not)
- *
- * @param targetParent
- * @param child
- * @return true if child is directly or indirectly overridden by parent,
- * false otherwise
- */
- private static boolean isOverridenBy(final DerivedTypedElement child,
- final DerivedTypedElement targetParent) {
-
- boolean result = false;
- if (child.equals(targetParent)) {
- result = true;
- } else {
- DerivedTypedElement currentParent = child.getOverride();
- while (!result && currentParent != null) {
- if (currentParent.equals(targetParent)) {
- result = true;
- } else {
- currentParent = currentParent.getOverride();
- }
- }
- }
- return result;
- }
-
- /**
- * Find DerivedTypedElement features that matches the 'signature'
- *
- * @param eObject
- * EObject used to test conformance
- * @param facet
- * @param signatureFeature
- * reference feature that serves as 'signature'
- * @throws FacetManagerException
- */
- private <T extends DerivedTypedElement> T getMatchingFeature(
- final EObject eObject, final Facet facet, final T signatureFeature)
- throws FacetManagerException {
- T result = null;
- EList<? extends ETypedElement> eTypedElements;
- if (signatureFeature instanceof EOperation) {
- eTypedElements = facet.getFacetOperations();
- } else {
- eTypedElements = facet.getFacetElements();
- }
- // For each eTypedElement check is it override the signature feature and
- // if the eTypedElement is owned by a facet to which the eObject
- // conforms.
- for (ETypedElement feature : eTypedElements) {
- if (isMatchingFeature2(signatureFeature, feature)) {
- // The conformance check is done after the check on the override
- // to avoid infinite recursion.
- final ConformanceState conformanceState = this.manager
- .getConformanceState(eObject, facet);
- if (conformanceState == ConformanceState.Conformant) {
- if (!signatureFeature.getClass().isInstance(feature)) {
- throw new FacetManagerException(
- ModelUtils.getQualifiedName(feature)
- + " overrides " //$NON-NLS-1$
- + ModelUtils
- .getQualifiedName(signatureFeature)
- + " but both are not of the same kind."); //$NON-NLS-1$
- }
- @SuppressWarnings("unchecked")
- // @SuppressWarnings("unchecked") check by
- // "if (!signatureFeature.getClass().isInstance(feature))"
- final T tmpFeature = (T) feature;
- result = tmpFeature;
- break;
- // Two features from the same Facet cannot
- // override a feature, so only one can be find
- // in this Facet => stop search here.
- }
- }
- }
- return result;
- }
-
- private static <T extends DerivedTypedElement> boolean isMatchingFeature2(
- final T signatureFeature, final ETypedElement feature)
- throws FacetManagerException {
- boolean result = false;
- // We're focusing on DerivedTypedElements
- // ECore native features could not be overridden by Facets
- // (EMF facet is supposed to be non intrusive)
-
- if (signatureFeature.getClass().isInstance(feature)) {
- final T element = (T) feature;
- // Just check that the top override is the same as the base feature
- // i.e. that the current feature matches the 'signature'
- final DerivedTypedElement topFeature = FacetUtils
- .getTopOverrideFeature(element);
- if (topFeature == signatureFeature) {
- // found a match
- result = true;
- } else {
- final Resource topResource = topFeature.eResource();
- final Resource signatureResource = signatureFeature.eResource();
- if (topResource == null || signatureResource == null || topFeature.eResource().getResourceSet() != signatureFeature.eResource().getResourceSet()) {
- Logger.logWarning("topOverrideFeature.eResource().getResourceSet() != signatureFeature.eResource().getResourceSet()", //$NON-NLS-1$
- Activator.getDefault());
- }
- }
- }
- return result;
- }
-
- public void removeFacetSet(final FacetSet facetSet) {
- final boolean removed = this.managedFacetSets.remove(facetSet);
- if (removed) {
- notifyListeners();
- }
- }
-
- public void clear() {
- final boolean empty = this.managedFacetSets.isEmpty();
- if (!empty) {
- this.managedFacetSets.clear();
- notifyListeners();
- }
- }
-
- public int size() {
- return this.managedFacetSets.size();
- }
-
- public boolean isEmpty() {
- return this.managedFacetSets.isEmpty();
- }
-
- public boolean contains(final Object object) {
- return this.managedFacetSets.contains(object);
- }
-
- public Iterator<FacetSet> iterator() {
- return this.managedFacetSets.iterator();
- }
-
- public Object[] toArray() {
- return this.managedFacetSets.toArray();
- }
-
- public <T> T[] toArray(final T[] array) {
- return this.managedFacetSets.toArray(array);
- }
-
- public boolean add(final FacetSet object) {
- boolean result = false;
- this.managedFacetSets.remove(object);
- if (object != null) {
- result = this.managedFacetSets.add(object);
- }
- if (result) {
- notifyListeners();
- }
- return result;
- }
-
- public boolean remove(final Object object) {
- final boolean isRemoved = this.managedFacetSets.remove(object);
- if (isRemoved) {
- notifyListeners();
- }
- return isRemoved;
- }
-
- public boolean containsAll(final Collection<?> collection) {
- return this.managedFacetSets.containsAll(collection);
- }
-
- public boolean addAll(final Collection<? extends FacetSet> collection) {
- boolean result = false;
- for (FacetSet facetSet : collection) {
- this.managedFacetSets.remove(facetSet);
- if (facetSet != null) {
- final boolean addResult = this.managedFacetSets.add(facetSet);
- result = result || addResult;
- }
- }
- if (result) {
- notifyListeners();
- }
- return result;
- }
-
- public boolean addAll(final int index,
- final Collection<? extends FacetSet> collection) {
- final List<FacetSet> filtered = new ArrayList<FacetSet>();
- for (FacetSet facetSet : collection) {
- if (!filtered.contains(facetSet)) {
- filtered.add(facetSet);
- }
- }
- this.managedFacetSets.removeAll(filtered);
- final boolean isAdded = this.managedFacetSets.addAll(index,
- ListUtils.cleanList(filtered));
- if (isAdded) {
- notifyListeners();
- }
- return isAdded;
- }
-
- public boolean removeAll(final Collection<?> collection) {
- final boolean isRemoved = this.managedFacetSets.removeAll(collection);
- if (isRemoved) {
- notifyListeners();
- }
- return isRemoved;
- }
-
- public boolean retainAll(final Collection<?> collection) {
- final boolean isRetained = this.managedFacetSets.retainAll(collection);
- if (isRetained) {
- notifyListeners();
- }
- return isRetained;
- }
-
- public FacetSet get(final int index) {
- return this.managedFacetSets.get(index);
- }
-
- public FacetSet set(final int index, final FacetSet element) {
- final FacetSet oldElement = this.managedFacetSets.set(index, element);
- if (!oldElement.equals(element)) {
- notifyListeners();
- }
- return oldElement;
- }
-
- public void add(final int index, final FacetSet element) {
- this.managedFacetSets.remove(element);
- if (element != null) {
- this.managedFacetSets.add(index, element);
- notifyListeners();
- }
- }
-
- public FacetSet remove(final int index) {
- final FacetSet oldElement = this.managedFacetSets.remove(index);
- notifyListeners();
- return oldElement;
- }
-
- public int indexOf(final Object object) {
- return this.managedFacetSets.indexOf(object);
- }
-
- public int lastIndexOf(final Object object) {
- return this.managedFacetSets.lastIndexOf(object);
- }
-
- public ListIterator<FacetSet> listIterator() {
- return this.managedFacetSets.listIterator();
- }
-
- public ListIterator<FacetSet> listIterator(final int index) {
- return this.managedFacetSets.listIterator(index);
- }
-
- public List<FacetSet> subList(final int fromIndex, final int toIndex) {
- return this.subList(fromIndex, toIndex);
- }
-
- public void addListener(final IFacetManagerListener listener) {
- this.listeners.add(listener);
- }
-
- public void removeListener(final IFacetManagerListener listener) {
- this.listeners.remove(listener);
- }
-
- private void notifyListeners() {
- for (IFacetManagerListener listener : this.listeners) {
- listener.facetManagerChanged();
- }
- }
-}
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 Mia-Software, CEA, 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
+ *
+ * Contributors:
+ * Olivier Remaud (Soft-Maint) - Bug 361794 - [Restructuring] EMF Facet customization meta-model
+ * Gregoire Dupe (Mia-Software) - Bug 364325 - [Restructuring] The user must be able to navigate into a model using the Facet.
+ * Gregoire Dupe (Mia-Software) - Bug 361794 - [Restructuring] EMF Facet customization meta-model
+ * Gregoire Dupe (Mia-Software) - Bug 373078 - API Cleaning
+ * Gregoire Dupe (Mia-Software) - Bug 375087 - [Table] ITableWidget.addColumn(List<ETypedElement>, List<FacetSet>)
+ * Gregoire Dupe (Mia-Software) - Bug 372626 - Aggregates
+ * Gregoire Dupe (Mia-Software) - Bug 377178 - [EFacet] infinite recursion in override resolution
+ * Gregoire Dupe (Mia-software) - Bug 383418 - [Table] FacetManagerContext.getOverrideCandidateFeatures(...) is empty
+ * Gregoire Dupe (Mia-software) - Bug 420093 - [EFacet] The facetManger list doesn't deal with uniqueness
+ * Thomas Cicognani (Soft-Maint) - Bug 420193 - Listener on FacetManager
+ * Fabien Treguer (Soft-Maint) - Bug 423285 - [Table] FacetSets not stored in a resource cause model manager crashes
+ * Christian W. Damus (CEA) - Bug 441857 - [Performances - Model Explorer] Severe performance problems for larger models
+ *******************************************************************************/
+
+package org.eclipse.papyrus.emf.facet.efacet.core.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+
+import org.eclipse.emf.common.notify.Adapter;
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.common.util.BasicEList;
+import org.eclipse.emf.common.util.ECollections;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EOperation;
+import org.eclipse.emf.ecore.ETypedElement;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.util.EContentAdapter;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.papyrus.emf.facet.efacet.core.FacetUtils;
+import org.eclipse.papyrus.emf.facet.efacet.core.IFacetManagerListener;
+import org.eclipse.papyrus.emf.facet.efacet.core.exception.FacetManagerException;
+import org.eclipse.papyrus.emf.facet.efacet.core.internal.FacetManager.ConformanceState;
+import org.eclipse.papyrus.emf.facet.efacet.core.internal.exception.FacetConformanceEvaluationException;
+import org.eclipse.papyrus.emf.facet.efacet.core.internal.exception.UnmatchingExpectedTypeException;
+import org.eclipse.papyrus.emf.facet.efacet.core.internal.exported.IResolverManager;
+import org.eclipse.papyrus.emf.facet.efacet.metamodel.v0_2_0.efacet.DerivedTypedElement;
+import org.eclipse.papyrus.emf.facet.efacet.metamodel.v0_2_0.efacet.Facet;
+import org.eclipse.papyrus.emf.facet.efacet.metamodel.v0_2_0.efacet.FacetSet;
+import org.eclipse.papyrus.emf.facet.util.core.Logger;
+import org.eclipse.papyrus.emf.facet.util.core.internal.exported.ListUtils;
+import org.eclipse.papyrus.emf.facet.util.emf.core.ModelUtils;
+
+/**
+ * @author oremaud
+ *
+ * FacetManager Context
+ *
+ * Defines which FacetSets will be taken into account, and in which
+ * order ('front' FacetSets have higher precedence)
+ *
+ * Responsible of overrides resolution for Facets and Customs
+ */
+class FacetManagerContext implements List<FacetSet> {
+
+ private static final String SILENT_OPTION = "org.eclipse.papyrus.emf.facet.efacet.core.internal.FacetManagerContext.getOverrideCandidateFeatures.silent"; //$NON-NLS-1$
+ private static final boolean SILENT = Boolean
+ .getBoolean(FacetManagerContext.SILENT_OPTION);
+
+ // We cannot use the interface (i.e, List) instead because we need to use
+ // the methods addLast and addFirst
+ private final EList<FacetSet> managedFacetSets = new BasicEList<FacetSet>() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void didRemove(int index, FacetSet oldObject) {
+ unconfigure(oldObject);
+ }
+
+ @Override
+ protected void didAdd(int index, FacetSet newObject) {
+ configure(newObject);
+ }
+
+ @Override
+ protected void didSet(int index, FacetSet newObject, FacetSet oldObject) {
+ unconfigure(oldObject);
+ configure(newObject);
+ }
+
+ @Override
+ protected void didChange() {
+ facetsUpdated();
+ }
+ };
+ private final transient FacetManager manager;
+
+ private transient Adapter facetAdapter; // Facets aren't serializable so no need for transient, but be consistent with the manager
+ transient long facetGeneration; // Likewise
+ private transient boolean updateEnabled = true;
+
+ /**
+ * This field is used to avoid to have to many error messages in the log.
+ */
+ private final Set<ETypedElement> failingFeatures = new HashSet<ETypedElement>();
+
+ private final Set<IFacetManagerListener> listeners = new HashSet<IFacetManagerListener>();
+
+ public FacetManagerContext(final FacetManager manager) {
+ this.manager = manager;
+ }
+
+ /**
+ *
+ * @param baseFeature
+ * @param eObject
+ * @return
+ * @throws FacetConformanceEvaluationException
+ * @throws UnmatchingExpectedTypeException
+ * @throws FacetManagerException
+ */
+ public <T extends ETypedElement> T resolveOverrides(final T baseFeature,
+ final EObject eObject) throws FacetManagerException {
+ T result = baseFeature;
+ if (baseFeature instanceof DerivedTypedElement) {
+ final DerivedTypedElement derivedResult = resolveOverrides(
+ (DerivedTypedElement) baseFeature, eObject);
+ if (derivedResult != null
+ && !(derivedResult instanceof ETypedElement)) {
+ throw new UnmatchingExpectedTypeException(
+ "Type mismatch in override resolution '" + baseFeature.getName() + "'"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ result = (T) derivedResult;
+ }
+ // If nothing is found, return the original basefeature
+ if (result == null) {
+ result = baseFeature;
+ }
+ return result;
+ }
+
+ /**
+ *
+ * @param baseFeature
+ * @param eObject
+ * @return
+ * @throws FacetConformanceEvaluationException
+ * @throws FacetManagerException
+ */
+ public <T extends DerivedTypedElement> T resolveOverrides(
+ final T baseFeature, final EObject eObject)
+ throws FacetManagerException {
+
+ final FacetCache cache = FacetCache.getInstance(eObject, this);
+ T result = cache.resolve(baseFeature);
+
+ if (result == null) {
+ try {
+ // -- Find master override
+ // If the baseFeature overrides another feature, then we follow the
+ // override chain to find the top feature
+ // The main idea behind this is that the baseFeature does not really
+ // represents a specific feature but a
+ // feature signature (as in java method invocation)
+ final T signatureFeature = FacetUtils
+ .getTopOverrideFeature(baseFeature);
+
+ // -- Find all candidates
+ // Get all Facet referenced by the FacetManager to get all features
+ // matching this signature
+ // Note : candidates are searched ine the order provided by the current
+ // FacetManager,
+ // so they are already ordered by the wanted priority
+ // XXX : Debug check that DerivedTypedElement only contained by Facet
+ final List<T> orderedCandidates = getOverrideCandidateFeatures(eObject,
+ signatureFeature);
+
+ // -- Find the most specific feature
+ result = findMostSpecificFeature(orderedCandidates);
+
+ cache.add(baseFeature, result);
+ } catch (Exception e) {
+ throw new FacetManagerException(e);
+ }
+ }
+
+ return result;
+ }
+
+ public List<FacetSet> getManagedFacetSets() {
+ return Collections.unmodifiableList(this.managedFacetSets);
+ }
+
+ public void setManagedFacetSets(final List<FacetSet> facetSets) {
+ // Don't increment the generation and notify listeners multiple times in this composite operation
+ final boolean enableUpdate = updateEnabled;
+ updateEnabled = false;
+ try {
+ ECollections.setEList(this.managedFacetSets, facetSets);
+ } finally {
+ updateEnabled = enableUpdate;
+ }
+
+ facetsUpdated();
+ }
+
+ public void addBackManagedFacetSet(final FacetSet facetSet) {
+ add(facetSet);
+ }
+
+ public void addFrontManagedFacetSet(final FacetSet facetSet) {
+ add(0, facetSet);
+ }
+
+ /**
+ * Find the most specific feature in the candidates list. The most specific
+ * is the feature that : - is the most specific (in terms of Facet
+ * inheritance) : the lowest in the inheritance tree (per branch) - is
+ * encountered first. The order is given by the FacetManager context.
+ *
+ * @param candidates
+ * The list of candidates features. Can be empty.
+ * @return
+ */
+ private static <T extends DerivedTypedElement> T findMostSpecificFeature(
+ final List<T> orderedCandidates) {
+ T result = null;
+ if (!orderedCandidates.isEmpty()) {
+ final Iterator<T> candidatesIt = orderedCandidates.iterator();
+ result = candidatesIt.next();
+ // We have the highest priority feature, we just have to check
+ // that there is no more specific one in the override chain (from
+ // top to bottom)
+ while (candidatesIt.hasNext()) {
+ final T candidate = candidatesIt.next();
+ if (isOverridenBy(candidate, result)) {
+ result = candidate;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Find matching candidates.
+ *
+ * @param eObject
+ * @param baseFeature
+ * @return A list of candidates, in the right order for conflict resolution
+ * @throws FacetManagerException
+ */
+ private <T extends DerivedTypedElement> List<T> getOverrideCandidateFeatures(
+ final EObject eObject, final T baseFeature)
+ throws FacetManagerException {
+ final ResourceSet baserFeatureRS = baseFeature.eResource().getResourceSet();
+ final List<T> result = new LinkedList<T>();
+ // Iterate over all Facets from all facetSets to find conforming
+ // features
+ final List<FacetSet> managedFSets = getManagedFacetSets();
+ final List<FacetSet> allFacetSets = new ArrayList<FacetSet>(managedFSets);
+ // add aggregated FacetSets
+ for (FacetSet facetSet : managedFSets) {
+ // FIXME Should handle recursive containment.
+ for (FacetSet subFacetSet : facetSet.getFacetSets()) {
+ final FacetSet resolvedFacetSet = IResolverManager.DEFAULT
+ .resolve(subFacetSet, FacetSet.class);
+ allFacetSets.add(resolvedFacetSet);
+ }
+ }
+ for (FacetSet facetSet : allFacetSets) {
+ final Resource resource = facetSet.eResource();
+ if (resource == null) {
+ final String message = String.format("The facetSet %s (%s) is not stored in a resource.", //$NON-NLS-1$
+ facetSet.getName(), facetSet.getNsURI());
+ Logger.logWarning(message, Activator.getDefault());
+ } else {
+ final ResourceSet facetSetRS = resource.getResourceSet();
+ if (!facetSetRS.equals(baserFeatureRS)) {
+ Logger.logWarning("The facet manager is dealing with more than one resource set.", //$NON-NLS-1$
+ Activator.getDefault());
+ }
+ }
+ for (Facet facet : FacetUtils.getFacets(facetSet)) {
+ final T matchingFeature = getMatchingFeature(eObject, facet,
+ baseFeature);
+ if (matchingFeature != null) {
+ result.add(matchingFeature);
+ }
+ }
+ }
+ if (result.isEmpty()) {
+ if (!this.failingFeatures.contains(baseFeature)
+ && !FacetManagerContext.SILENT) {
+ Logger.logWarning(
+ "The result of " //$NON-NLS-1$
+ + this.getClass().getSimpleName()
+ + ".getOverrideCandidateFeatures(...) is empty! baseFeature=" //$NON-NLS-1$
+ + EcoreUtil.getURI(baseFeature)
+ + " (This message will be sent only once)", //$NON-NLS-1$
+ Activator.getDefault());
+ // This avoid to have to many error messages in the log.
+ this.failingFeatures.add(baseFeature);
+ }
+ result.add(baseFeature);
+ }
+ return result;
+ }
+
+ /**
+ * Test whether a feature is overridden by another (directly or not)
+ *
+ * @param targetParent
+ * @param child
+ * @return true if child is directly or indirectly overridden by parent,
+ * false otherwise
+ */
+ private static boolean isOverridenBy(final DerivedTypedElement child,
+ final DerivedTypedElement targetParent) {
+
+ boolean result = false;
+ if (child.equals(targetParent)) {
+ result = true;
+ } else {
+ DerivedTypedElement currentParent = child.getOverride();
+ while (!result && currentParent != null) {
+ if (currentParent.equals(targetParent)) {
+ result = true;
+ } else {
+ currentParent = currentParent.getOverride();
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Find DerivedTypedElement features that matches the 'signature'
+ *
+ * @param eObject
+ * EObject used to test conformance
+ * @param facet
+ * @param signatureFeature
+ * reference feature that serves as 'signature'
+ * @throws FacetManagerException
+ */
+ private <T extends DerivedTypedElement> T getMatchingFeature(
+ final EObject eObject, final Facet facet, final T signatureFeature)
+ throws FacetManagerException {
+ T result = null;
+ EList<? extends ETypedElement> eTypedElements;
+ if (signatureFeature instanceof EOperation) {
+ eTypedElements = facet.getFacetOperations();
+ } else {
+ eTypedElements = facet.getFacetElements();
+ }
+ // For each eTypedElement check is it override the signature feature and
+ // if the eTypedElement is owned by a facet to which the eObject
+ // conforms.
+ for (ETypedElement feature : eTypedElements) {
+ if (isMatchingFeature2(signatureFeature, feature)) {
+ // The conformance check is done after the check on the override
+ // to avoid infinite recursion.
+ final ConformanceState conformanceState = this.manager
+ .getConformanceState(eObject, facet);
+ if (conformanceState == ConformanceState.Conformant) {
+ if (!signatureFeature.getClass().isInstance(feature)) {
+ throw new FacetManagerException(
+ ModelUtils.getQualifiedName(feature)
+ + " overrides " //$NON-NLS-1$
+ + ModelUtils
+ .getQualifiedName(signatureFeature)
+ + " but both are not of the same kind."); //$NON-NLS-1$
+ }
+ @SuppressWarnings("unchecked")
+ // @SuppressWarnings("unchecked") check by
+ // "if (!signatureFeature.getClass().isInstance(feature))"
+ final T tmpFeature = (T) feature;
+ result = tmpFeature;
+ break;
+ // Two features from the same Facet cannot
+ // override a feature, so only one can be find
+ // in this Facet => stop search here.
+ }
+ }
+ }
+ return result;
+ }
+
+ private static <T extends DerivedTypedElement> boolean isMatchingFeature2(
+ final T signatureFeature, final ETypedElement feature)
+ throws FacetManagerException {
+ boolean result = false;
+ // We're focusing on DerivedTypedElements
+ // ECore native features could not be overridden by Facets
+ // (EMF facet is supposed to be non intrusive)
+
+ if (signatureFeature.getClass().isInstance(feature)) {
+ final T element = (T) feature;
+ // Just check that the top override is the same as the base feature
+ // i.e. that the current feature matches the 'signature'
+ final DerivedTypedElement topFeature = FacetUtils
+ .getTopOverrideFeature(element);
+ if (topFeature == signatureFeature) {
+ // found a match
+ result = true;
+ } else {
+ final Resource topResource = topFeature.eResource();
+ final Resource signatureResource = signatureFeature.eResource();
+ if (topResource == null || signatureResource == null || topFeature.eResource().getResourceSet() != signatureFeature.eResource().getResourceSet()) {
+ Logger.logWarning("topOverrideFeature.eResource().getResourceSet() != signatureFeature.eResource().getResourceSet()", //$NON-NLS-1$
+ Activator.getDefault());
+ }
+ }
+ }
+ return result;
+ }
+
+ public void removeFacetSet(final FacetSet facetSet) {
+ this.managedFacetSets.remove(facetSet);
+ }
+
+ public void clear() {
+ this.managedFacetSets.clear();
+ }
+
+ public int size() {
+ return this.managedFacetSets.size();
+ }
+
+ public boolean isEmpty() {
+ return this.managedFacetSets.isEmpty();
+ }
+
+ public boolean contains(final Object object) {
+ return this.managedFacetSets.contains(object);
+ }
+
+ public Iterator<FacetSet> iterator() {
+ return this.managedFacetSets.iterator();
+ }
+
+ public Object[] toArray() {
+ return this.managedFacetSets.toArray();
+ }
+
+ public <T> T[] toArray(final T[] array) {
+ return this.managedFacetSets.toArray(array);
+ }
+
+ public boolean add(final FacetSet object) {
+ boolean result = false;
+
+ if (object != null) {
+ // adding an already managed FacetSet again moves it to the last position
+ int existing = managedFacetSets.indexOf(object);
+ int last = size() - 1;
+ if (existing >= 0) {
+ if (existing != last) {
+ managedFacetSets.move(last, existing);
+ result = true;
+ }
+ } else {
+ result = managedFacetSets.add(object);
+ }
+ }
+
+ return result;
+ }
+
+ public boolean remove(final Object object) {
+ return this.managedFacetSets.remove(object);
+ }
+
+ public boolean containsAll(final Collection<?> collection) {
+ return this.managedFacetSets.containsAll(collection);
+ }
+
+ public boolean addAll(final Collection<? extends FacetSet> collection) {
+ // Don't increment the generation and notify listeners multiple times in this composite operation
+ final boolean result;
+ final boolean enableUpdate = updateEnabled;
+ updateEnabled = false;
+ try {
+ final boolean removed = this.managedFacetSets.removeAll(collection);
+ final boolean added = this.managedFacetSets.addAll(collection);
+ result = removed || added;
+ } finally {
+ updateEnabled = enableUpdate;
+ }
+
+ if (result) {
+ facetsUpdated();
+ }
+
+ return result;
+ }
+
+ public boolean addAll(final int index, final Collection<? extends FacetSet> collection) {
+ final List<FacetSet> filtered = new ArrayList<FacetSet>();
+ for (FacetSet facetSet : collection) {
+ if (!filtered.contains(facetSet)) {
+ filtered.add(facetSet);
+ }
+ }
+
+ // Don't increment the generation and notify listeners multiple times in this composite operation
+ final boolean result;
+ final boolean enableUpdate = updateEnabled;
+ updateEnabled = false;
+ try {
+ final boolean removed = this.managedFacetSets.removeAll(filtered);
+ final boolean added = this.managedFacetSets.addAll(index, ListUtils.cleanList(filtered));
+ result = removed || added;
+ } finally {
+ updateEnabled = enableUpdate;
+ }
+
+ if (result) {
+ facetsUpdated();
+ }
+
+ return result;
+ }
+
+ public boolean removeAll(final Collection<?> collection) {
+ return this.managedFacetSets.removeAll(collection);
+ }
+
+ public boolean retainAll(final Collection<?> collection) {
+ return this.managedFacetSets.retainAll(collection);
+ }
+
+ public FacetSet get(final int index) {
+ return this.managedFacetSets.get(index);
+ }
+
+ public FacetSet set(final int index, final FacetSet element) {
+ return this.managedFacetSets.set(index, element);
+ }
+
+ public void add(final int index, final FacetSet element) {
+ if (element != null) {
+ int existing = managedFacetSets.indexOf(element);
+ if (existing >= 0) {
+ if (existing != index) {
+ managedFacetSets.move(index, existing);
+ }
+ } else {
+ managedFacetSets.add(index, element);
+ }
+ }
+ }
+
+ public FacetSet remove(final int index) {
+ return this.managedFacetSets.remove(index);
+ }
+
+ public int indexOf(final Object object) {
+ return this.managedFacetSets.indexOf(object);
+ }
+
+ public int lastIndexOf(final Object object) {
+ return this.managedFacetSets.lastIndexOf(object);
+ }
+
+ public ListIterator<FacetSet> listIterator() {
+ return this.managedFacetSets.listIterator();
+ }
+
+ public ListIterator<FacetSet> listIterator(final int index) {
+ return this.managedFacetSets.listIterator(index);
+ }
+
+ public List<FacetSet> subList(final int fromIndex, final int toIndex) {
+ return this.subList(fromIndex, toIndex);
+ }
+
+ public void addListener(final IFacetManagerListener listener) {
+ this.listeners.add(listener);
+ }
+
+ public void removeListener(final IFacetManagerListener listener) {
+ this.listeners.remove(listener);
+ }
+
+ private void notifyListeners() {
+ for (IFacetManagerListener listener : this.listeners) {
+ listener.facetManagerChanged();
+ }
+ }
+
+ private void facetsUpdated() {
+ if (updateEnabled) {
+ incrementGeneration();
+ notifyListeners();
+ }
+ }
+
+ private void incrementGeneration() {
+ facetGeneration++;
+ }
+
+ private FacetSet configure(FacetSet facetSet) {
+ if ((facetSet != null) && !facetSet.eAdapters().contains(getFacetSetAdapter())) {
+ facetSet.eAdapters().add(getFacetSetAdapter());
+ }
+ return facetSet;
+ }
+
+ private <T> T unconfigure(T facetSet) {
+ if (facetSet instanceof FacetSet) {
+ ((FacetSet) facetSet).eAdapters().remove(getFacetSetAdapter());
+ }
+ return facetSet;
+ }
+
+ private Adapter getFacetSetAdapter() {
+ if (facetAdapter == null) {
+ facetAdapter = new EContentAdapter() {
+ @Override
+ protected void selfAdapt(Notification notification) {
+ if (!notification.isTouch()) {
+ incrementGeneration();
+ }
+
+ super.selfAdapt(notification);
+ }
+ };
+ }
+
+ return facetAdapter;
+ }
+}

Back to the top