blob: 33e10c654b5b841c2d8c46443317c289d80f1fcc [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2008-2013 See4sys, itemis 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
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
*
* Contributors:
* See4sys - Initial API and implementation
* itemis - [409014] Listener URIChangeDetector registered for all transactional editing domains
* itemis - [423669] Asynchronous cleanup of old metamodel descriptor cache occasionally done too early
*
* </copyright>
*/
package org.eclipse.sphinx.emf.internal.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.transaction.NotificationFilter;
import org.eclipse.emf.transaction.ResourceSetChangeEvent;
import org.eclipse.emf.transaction.ResourceSetListenerImpl;
import org.eclipse.sphinx.emf.domain.factory.AbstractResourceSetListenerInstaller;
import org.eclipse.sphinx.emf.internal.metamodel.InternalMetaModelDescriptorRegistry;
import org.eclipse.sphinx.emf.model.ModelDescriptorRegistry;
import org.eclipse.sphinx.emf.util.EcorePlatformUtil;
import org.eclipse.sphinx.emf.util.EcoreResourceUtil;
public class ModelDescriptorSynchronizerComplement extends ResourceSetListenerImpl {
public static class ModelDescriptorSynchronizerComplementInstaller extends
AbstractResourceSetListenerInstaller<ModelDescriptorSynchronizerComplement> {
public ModelDescriptorSynchronizerComplementInstaller() {
super(ModelDescriptorSynchronizerComplement.class);
}
}
public ModelDescriptorSynchronizerComplement() {
super(NotificationFilter.createFeatureFilter(EcorePackage.eINSTANCE.getEResource(), Resource.RESOURCE__IS_LOADED).or(
NotificationFilter.createFeatureFilter(EcorePackage.eINSTANCE.getEResourceSet(), ResourceSet.RESOURCE_SET__RESOURCES)));
}
@Override
public void resourceSetChanged(ResourceSetChangeEvent event) {
Set<Resource> loadedResources = new HashSet<Resource>();
Set<Resource> unloadedResources = new HashSet<Resource>();
Set<Resource> addedResources = new HashSet<Resource>();
Set<Resource> removedResources = new HashSet<Resource>();
// Analyze notifications for loaded and unloaded resources; record only resources which have not got
// unloaded/loaded again later on
for (Notification notification : event.getNotifications()) {
Object notifier = notification.getNotifier();
if (notifier instanceof Resource) {
Resource resource = (Resource) notifier;
Boolean newValue = (Boolean) notification.getNewValue();
if (newValue) {
if (unloadedResources.contains(resource)) {
unloadedResources.remove(resource);
} else {
loadedResources.add(resource);
}
} else {
if (loadedResources.contains(resource)) {
loadedResources.remove(resource);
} else {
unloadedResources.add(resource);
}
}
} else if (notifier instanceof ResourceSet) {
if (notification.getEventType() == Notification.ADD || notification.getEventType() == Notification.ADD_MANY) {
List<Resource> newResources = new ArrayList<Resource>();
Object newValue = notification.getNewValue();
if (newValue instanceof List<?>) {
@SuppressWarnings("unchecked")
List<Resource> newResourcesValue = (List<Resource>) newValue;
newResources.addAll(newResourcesValue);
} else if (newValue instanceof Resource) {
newResources.add((Resource) newValue);
}
for (Resource newResource : newResources) {
if (removedResources.contains(newResource)) {
removedResources.remove(newResource);
} else {
addedResources.add(newResource);
}
}
} else if (notification.getEventType() == Notification.REMOVE || notification.getEventType() == Notification.REMOVE_MANY) {
List<Resource> oldResources = new ArrayList<Resource>();
Object oldValue = notification.getOldValue();
if (oldValue instanceof List<?>) {
@SuppressWarnings("unchecked")
List<Resource> oldResourcesValue = (List<Resource>) oldValue;
oldResources.addAll(oldResourcesValue);
} else if (oldValue instanceof Resource) {
oldResources.add((Resource) oldValue);
}
for (Resource oldResource : oldResources) {
if (addedResources.contains(oldResource)) {
addedResources.remove(oldResource);
} else {
removedResources.add(oldResource);
}
}
}
}
}
loadedResources.addAll(addedResources);
unloadedResources.addAll(removedResources);
// Handle loaded and unloaded resources
handleModelResourceLoaded(loadedResources);
handleModelResourceUnloaded(unloadedResources);
}
/**
* Handles the case where the specified set of {@link Resource resource}s has been loaded.
*
* @param resources
* The set of {@linkplain Resource resource}s that has been loaded.
*/
private void handleModelResourceLoaded(final Collection<Resource> resources) {
if (!resources.isEmpty()) {
for (Resource resource : resources) {
// Add descriptor for model behind loaded resource to ModelDescriptorRegistry if not
// already done so
IFile file = EcorePlatformUtil.getFile(resource);
if (file == null || !file.exists()) {
ModelDescriptorRegistry.INSTANCE.addModel(resource);
}
}
}
}
/**
* Handles the case where the specified set of {@link Resource resource}s has been unloaded.
*
* @param resources
* The set of {@linkplain Resource resource} that has been unloaded.
*/
private void handleModelResourceUnloaded(final Collection<Resource> resources) {
if (!resources.isEmpty()) {
for (Resource resource : resources) {
// Remove underlying model file from file meta-model descriptor cache if resource exists
// only in memory
/*
* !! Important Note !! This should normally be the business of MetaModelDescriptorCacheUpdater.
* However, we have to do so here as well because we depend on that cached metamodel descriptors are up
* to date but cannot know which of both BasicModelDescriptorSynchronizerDelegate or
* MetaModelDescriptorCacheUpdater gets called first.
*/
IFile file = EcorePlatformUtil.getFile(resource);
if (!EcoreResourceUtil.exists(resource.getURI())) {
InternalMetaModelDescriptorRegistry.INSTANCE.removeCachedDescriptor(file);
}
}
for (Resource resource : resources) {
// Remove descriptor for model behind unloaded only in memory resource from
// ModelDescriptorRegistry if it is the last resource of the given model
IFile file = EcorePlatformUtil.getFile(resource);
if (file == null || !file.exists()) {
ModelDescriptorRegistry.INSTANCE.removeModel(resource);
}
}
}
}
}