blob: 6a7bcca3b73047d91a1dc661d85ad5c335b837dc [file] [log] [blame]
/*******************************************************************************
* 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();
}
}