/******************************************************************************* | |
* Copyright (c) 2008, 2019 Mia-Software and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v2.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v20.html | |
* | |
* Contributors: | |
* Nicolas Bros (Mia-Software) - initial API and implementation | |
* Nicolas Bros (Mia-Software) - Bug 341252 - [Model Browser] instances in composed models not displayed | |
* Grégoire Dupé (Mia-Software) - Bug 471096 - MetaclassInstance features have to be moved to an EMF dedicated plug-in | |
*******************************************************************************/ | |
package org.eclipse.modisco.infra.browser.core; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Comparator; | |
import java.util.HashSet; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Set; | |
import java.util.TreeMap; | |
import org.eclipse.emf.common.notify.Notification; | |
import org.eclipse.emf.ecore.EClass; | |
import org.eclipse.emf.ecore.resource.Resource; | |
import org.eclipse.modisco.infra.browser.editors.BrowserConfiguration; | |
import org.eclipse.modisco.infra.browser.uicore.internal.util.EMFUtil; | |
import org.eclipse.modisco.infra.common.core.internal.utils.ModelUtils; | |
import org.eclipse.modisco.infra.facet.Facet; | |
import org.eclipse.modisco.infra.facet.core.FacetContext; | |
import org.eclipse.modisco.infra.facet.core.FacetContextListener; | |
import org.eclipse.modisco.infra.facet.core.adapters.instances.MetaclassInstancesAdapterFactoryWithFacet; | |
import org.eclipse.modisco.util.emf.core.internal.allinstances.MetaclassInstances; | |
import org.eclipse.modisco.util.emf.core.internal.allinstances.ModelChangeListener; | |
/** | |
* A list of metaclasses from the model, each metaclass having an associated | |
* list of instances of the metaclass. | |
*/ | |
public class InstancesForMetaclasses implements FacetContextListener, ModelChangeListener { | |
/** Model elements sorted by EClass (using their full qualified name). */ | |
private final TreeMap<EClass, InstancesForMetaclass> modelElements; | |
/** | |
* A list of all metaclasses which do not have a parent. That is, a list of | |
* metaclasses from which all other metaclasses can be found by derivation. | |
*/ | |
private final ArrayList<InstancesForMetaclass> rootMetaclasses = new ArrayList<InstancesForMetaclass>(); | |
private final BrowserConfiguration browserConfiguration; | |
/** | |
* The set of resources the contents of which are currently displayed in the | |
* types panel | |
*/ | |
private final Set<Resource> fResources; | |
private final List<MetaclassesChangeListener> listeners = new ArrayList<MetaclassesChangeListener>(); | |
/** | |
* @param browserConfiguration | |
* the browser configuration | |
* @param resources | |
* the resources from which to compute the list of metaclasses | |
*/ | |
public InstancesForMetaclasses(final BrowserConfiguration browserConfiguration, | |
final Set<Resource> resources) { | |
this.browserConfiguration = browserConfiguration; | |
this.fResources = new HashSet<Resource>(); | |
// sorted by qualified name | |
this.modelElements = new TreeMap<EClass, InstancesForMetaclass>(new Comparator<EClass>() { | |
public int compare(final EClass e1, final EClass e2) { | |
String q1 = ModelUtils.getMetaclassQualifiedName(e1); | |
String q2 = ModelUtils.getMetaclassQualifiedName(e2); | |
if (q1 == null) { | |
return -1; | |
} | |
return q1.compareTo(q2); | |
} | |
}); | |
final FacetContext facetContext = browserConfiguration.getAppearanceConfiguration() | |
.getFacetContext(); | |
MetaclassInstancesAdapterFactoryWithFacet.getInstance().setFacetContext(facetContext); | |
for (Resource resource : resources) { | |
internalWatchResource(resource); | |
} | |
computeModelElements(); | |
facetContext.addListener(this); | |
} | |
private void computeModelElements() { | |
List<Resource> resources; | |
synchronized (this.fResources) { | |
resources = new ArrayList<Resource>(this.fResources); | |
} | |
Collection<EClass> allClasses = EMFUtil.findAllClasses(resources); | |
for (EClass eClass : allClasses) { | |
synchronized (this.modelElements) { | |
if (!this.modelElements.containsKey(eClass)) { | |
InstancesForMetaclass instancesForMetaclass = new InstancesForMetaclass(eClass, | |
this, this.browserConfiguration, this.fResources); | |
this.modelElements.put(eClass, instancesForMetaclass); | |
} | |
} | |
} | |
} | |
public void watchResource(final Resource resource) { | |
boolean contained; | |
synchronized (this.fResources) { | |
contained = this.fResources.contains(resource); | |
} | |
if (!contained) { | |
final FacetContext facetContext = this.browserConfiguration | |
.getAppearanceConfiguration().getFacetContext(); | |
MetaclassInstancesAdapterFactoryWithFacet.getInstance().setFacetContext(facetContext); | |
internalWatchResource(resource); | |
computeModelElements(); | |
buildDerivationTree(); | |
} | |
} | |
private void internalWatchResource(final Resource resource) { | |
MetaclassInstances instances = (MetaclassInstances) MetaclassInstancesAdapterFactoryWithFacet | |
.getInstance().adapt(resource, MetaclassInstances.class); | |
instances.addListener(this); | |
synchronized (this.fResources) { | |
this.fResources.add(resource); | |
} | |
} | |
public void unwatchResource(final Resource resource) { | |
boolean contained; | |
synchronized (this.fResources) { | |
contained = this.fResources.contains(resource); | |
} | |
if (contained) { | |
// always set FacetContext before using | |
// MetaclassInstancesAdapterFactoryWithFacet | |
MetaclassInstancesAdapterFactoryWithFacet.getInstance().setFacetContext( | |
this.browserConfiguration.getAppearanceConfiguration().getFacetContext()); | |
MetaclassInstances instances = (MetaclassInstances) MetaclassInstancesAdapterFactoryWithFacet | |
.getInstance().adapt(resource, MetaclassInstances.class); | |
// "touch" to mark the cache dirty | |
this.browserConfiguration.getAppearanceConfiguration().touch(); | |
instances.removeListener(this); | |
synchronized (this.fResources) { | |
this.fResources.remove(resource); | |
} | |
computeModelElements(); | |
buildDerivationTree(); | |
} | |
} | |
public InstancesForMetaclass[] getInstancesForMetaclasses() { | |
synchronized (this.modelElements) { | |
final Collection<InstancesForMetaclass> instancesForMetaclasses = this.modelElements | |
.values(); | |
return instancesForMetaclasses | |
.toArray(new InstancesForMetaclass[instancesForMetaclasses.size()]); | |
} | |
} | |
/** | |
* @param eClass | |
* the metaclass | |
* @return the {@link InstancesForMetaclass} object or <code>null</code> if | |
* the metaclass was not found | |
*/ | |
public InstancesForMetaclass getInstancesForMetaclass(final EClass eClass) { | |
synchronized (this.modelElements) { | |
return this.modelElements.get(eClass); | |
} | |
} | |
/** Builds the derivation tree of metaclasses */ | |
public void buildDerivationTree() { | |
final InstancesForMetaclass[] instancesByMetaclass = getInstancesForMetaclasses(); | |
// clear previous state | |
this.rootMetaclasses.clear(); | |
for (final InstancesForMetaclass instancesForMetaclass : instancesByMetaclass) { | |
instancesForMetaclass.clearSubclasses(); | |
} | |
// build the derivation tree | |
for (final InstancesForMetaclass instancesForMetaclass : instancesByMetaclass) { | |
instancesForMetaclass.buildParentsSubclasses(); | |
if (instancesForMetaclass.getEClass().getESuperTypes().isEmpty()) { | |
this.rootMetaclasses.add(instancesForMetaclass); | |
} | |
} | |
} | |
public InstancesForMetaclass[] getRootMetaclasses() { | |
return this.rootMetaclasses.toArray(new InstancesForMetaclass[this.rootMetaclasses.size()]); | |
} | |
public void facetAdded(final Facet facet) { | |
List<Resource> resources; | |
synchronized (this.fResources) { | |
resources = new ArrayList<Resource>(this.fResources); | |
} | |
InstancesForMetaclass instancesForMetaclass = new InstancesForMetaclass(facet, this, | |
this.browserConfiguration, resources); | |
synchronized (this.modelElements) { | |
this.modelElements.put(facet, instancesForMetaclass); | |
} | |
} | |
public void facetsCleared() { | |
synchronized (this.modelElements) { | |
Iterator<EClass> iterator = this.modelElements.keySet().iterator(); | |
while (iterator.hasNext()) { | |
EClass eClass = iterator.next(); | |
if (eClass instanceof Facet) { | |
iterator.remove(); | |
} | |
} | |
} | |
} | |
public interface MetaclassesChangeListener { | |
void modelChanged(); | |
} | |
public void addListener(final MetaclassesChangeListener listener) { | |
if (!this.listeners.contains(listener)) { | |
this.listeners.add(listener); | |
} | |
} | |
public void removeListener(final MetaclassesChangeListener listener) { | |
this.listeners.remove(listener); | |
} | |
public void notifyChanged() { | |
for (MetaclassesChangeListener listener : this.listeners) { | |
listener.modelChanged(); | |
} | |
} | |
public void modelChanged(final Notification msg) { | |
if (msg != null && msg.getEventType() == Notification.RESOLVE) { | |
computeModelElements(); | |
buildDerivationTree(); | |
} | |
// pass on the notification to our listeners | |
notifyChanged(); | |
} | |
} |