blob: 75cb9029fadbc81f5d2c0baa35a22782a86206f0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2019 Willink Transformations 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:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.pivot.internal.utilities;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import org.eclipse.emf.common.EMFPlugin;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.EMOFResourceFactoryImpl;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.internal.manager.PivotMetamodelManager;
import org.eclipse.ocl.pivot.internal.resource.ASResourceFactoryRegistry;
import org.eclipse.ocl.pivot.resource.ProjectManager;
import org.eclipse.ocl.pivot.utilities.AbstractEnvironmentFactory;
import org.eclipse.ocl.pivot.utilities.EnvironmentFactory;
import com.google.common.collect.MapMaker;
/**
* The sole instance of GlobalEnvironmentFactory provides the OCL in global contexts such as the
* execution of OCL delegates for which there is no OCL application to provide a customized OCL
* with additional Complete OCL documents or optimized project lists. The global OCL uses a
* global ProjectManager providing access to all models on the standalone classpath or in the
* Eclipse workspace.
*/
public class GlobalEnvironmentFactory extends AbstractEnvironmentFactory
{
/**
* An GlobalEnvironmentFactory.Listener reports GlobalEnvironmentFactory disposal to
* consumers such as OCLDelegateDomains that need to clean up when their OCL support vanishes.
*/
public interface Listener
{
/**
* Respond to disposal of the EnvironmentFactory by eliminating all further usage.
*/
void environmentFactoryDisposed(@NonNull EnvironmentFactory environmentFactory);
}
protected class UnloadResourceAdapter extends AdapterImpl implements Listener
{
@Override
public void environmentFactoryDisposed(@NonNull EnvironmentFactory environmentFactory) {
Notifier target2 = getTarget();
if (target2 != null) {
target2.eAdapters().remove(this);
}
}
@Override
public void unsetTarget(Notifier oldTarget) {
assert oldTarget instanceof Resource;
PivotMetamodelManager metamodelManager = basicGetMetamodelManager();
if (metamodelManager != null) {
metamodelManager.removeExternalResource((Resource)oldTarget);
}
ResourceSet externalResourceSet2 = getResourceSet();
if (externalResourceSet2 instanceof ResourceSetImpl) {
List<URI> deadKeys = new ArrayList<URI>();
Map<URI, Resource> uriResourceMap = ((ResourceSetImpl)externalResourceSet2).getURIResourceMap();
for (Map.Entry<URI, Resource> entry : uriResourceMap.entrySet()) {
if (entry.getValue() == oldTarget) {
deadKeys.add(entry.getKey());
}
}
for (URI deadKey : deadKeys) {
uriResourceMap.remove(deadKey);
}
}
super.unsetTarget(oldTarget);
}
}
private static @Nullable GlobalEnvironmentFactory INSTANCE = null;
/**
* Return the global EnvironmentFactory that may be shared amongst OCL consumers or
* null if none has been created.
*/
public static @Nullable GlobalEnvironmentFactory basicGetInstance() {
return INSTANCE;
}
private static @NonNull ResourceSet createGlobalResourceSet() {
ResourceSetImpl resourceSet = new ResourceSetImpl();
ConcurrentMap<URI, Resource> weakMap = new MapMaker().weakValues().makeMap();
resourceSet.setURIResourceMap(weakMap); // Must use weak values to allow garbage collection of stale models
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("emof", new EMOFResourceFactoryImpl()); //$NON-NLS-1$
ASResourceFactoryRegistry.INSTANCE.configureResourceSets(null, resourceSet);
return resourceSet;
}
/**
* Dispose of the global instance; this is intended for leakage detection in tests.
*/
public static void disposeInstance() {
GlobalEnvironmentFactory instance = INSTANCE;
if (instance != null) {
// PivotUtilInternal.debugPrintln("Dispose Global " + NameUtil.debugSimpleName(INSTANCE));
instance.dispose();
}
}
/**
* Return the global EnvironmentFactory that may be shared amongst OCL consumers for which Complete OCL
* complements are not possible. Typically this is the OCLDelegateDomains that supported interpreted
* execution of OCL embedded in Ecore.
*/
public static @NonNull GlobalEnvironmentFactory getInstance() {
GlobalEnvironmentFactory instance = INSTANCE;
if (instance == null) {
INSTANCE = instance = new GlobalEnvironmentFactory();
// PivotUtilInternal.debugPrintln("Create Global " + NameUtil.debugSimpleName(INSTANCE));
}
// else {
// PivotUtilInternal.debugPrintln("Re-use Global " + NameUtil.debugSimpleName(INSTANCE));
// }
return instance;
}
/**
* Re-enable safe navigation validations.
*/
public static void resetSafeNavigationValidations() {
if (INSTANCE != null) {
INSTANCE.resetSeverities();
}
}
/**
* EnvironmentFactoryListener instances to be notified of significant state changes; most notably disposal.
*/
private @Nullable List<Listener> listeners = null;
private GlobalEnvironmentFactory() {
super(ProjectManager.CLASS_PATH, createGlobalResourceSet(), null);
// System.out.println("GlobalEnvironmentFactory()");
if (!EMFPlugin.IS_ECLIPSE_RUNNING) {
getProjectManager().initializeResourceSet(null);
}
}
@Override
public void addExternal2AS(@NonNull External2AS external2as) {
super.addExternal2AS(external2as);
Resource esResource = external2as.getResource();
if (esResource != null) {
UnloadResourceAdapter adapter = new UnloadResourceAdapter();
esResource.eAdapters().add(adapter);
addListener(adapter);
}
}
public void addListener(@NonNull Listener listener) {
List<Listener> listeners2 = listeners;
if (listeners2 == null) {
listeners = listeners2 = new ArrayList<Listener>();
}
if (!listeners2.contains(listener)) {
listeners2.add(listener);
}
}
@Override
protected void disposeInternal() {
assert this == INSTANCE;
INSTANCE = null;
if (listeners != null) {
List<Listener> savedListeners = listeners;
listeners = null;
for (Listener listener : savedListeners) {
listener.environmentFactoryDisposed(this);
}
}
super.disposeInternal();
}
public void removeListener(@NonNull Listener listener) {
if (listeners != null) {
listeners.remove(listener);
}
}
}