blob: fe297282cd205a8bfd9d23c78286333deefa095d [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2008-2010 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 - [400895] Provide workarounds for memory leaks caused by EMF's ECrossReferenceAdapter
*
* </copyright>
*/
package org.eclipse.sphinx.emf.ecore;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.notify.impl.AdapterFactoryImpl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
import org.eclipse.sphinx.emf.resource.ExtendedResource;
import org.eclipse.sphinx.emf.resource.ExtendedResourceAdapterFactory;
import org.eclipse.sphinx.platform.util.ReflectUtil;
/**
*
*/
public class ECrossReferenceAdapterFactory extends AdapterFactoryImpl {
/**
* The singleton instance of this {@link AdapterFactory adapter factory}.
*/
public static ECrossReferenceAdapterFactory INSTANCE = new ECrossReferenceAdapterFactory();
// Protected default constructor for singleton pattern
protected ECrossReferenceAdapterFactory() {
// Nothing to do
}
/*
* @see org.eclipse.emf.common.notify.impl.AdapterFactoryImpl#isFactoryForType(java.lang.Object)
*/
@Override
public boolean isFactoryForType(Object type) {
return type == ECrossReferenceAdapter.class;
}
/**
* Returns either a previously associated {@link ECrossReferenceAdapter} adapter or a newly associated one, as
* appropriate. {@link Adapter#isAdapterForType(Object) Checks} if the an adapter for {@link ECrossReferenceAdapter}
* is already associated with the target and returns it in that case;
* {@link AdapterFactory#adaptNew(Notifier, Object) creates} a new {@link ECrossReferenceAdapter} adapter if
* possible otherwise.
*
* @param target
* The notifier to adapt.
* @return A previously existing associated {@link ECrossReferenceAdapter} adapter, a new associated
* {@link ECrossReferenceAdapter} adapter if possible, or <code>null</code> otherwise.
* @see AdapterFactory#adapt(Notifier, Object)
* @see Adapter#isAdapterForType(Object)
* @see AdapterFactory#adaptNew(Notifier, Object)
*/
public ECrossReferenceAdapter adapt(Notifier target) {
if (target != null) {
ECrossReferenceAdapter adapter = ECrossReferenceAdapter.getCrossReferenceAdapter(target);
if (adapter == null) {
adapter = (ECrossReferenceAdapter) adaptNew(target, ECrossReferenceAdapter.class);
}
return adapter;
}
return null;
}
/*
* @see org.eclipse.emf.common.notify.impl.AdapterFactoryImpl#createAdapter(org.eclipse.emf.common.notify.Notifier)
*/
@Override
protected Adapter createAdapter(Notifier target) {
if (target instanceof ResourceSet || target instanceof Resource || target instanceof EObject) {
return new ECrossReferenceAdapter() {
// Overridden to provide workaround for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=400887
@Override
protected void unsetTarget(Resource target) {
List<EObject> contents = target.getContents();
for (int i = 0, size = contents.size(); i < size; ++i) {
Notifier notifier = contents.get(i);
removeAdapter(notifier);
}
unloadedResources.remove(target);
}
// Overridden to provide workaround for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=400891
@Override
public void selfAdapt(Notification notification) {
Object notifier = notification.getNotifier();
if (notifier instanceof Resource) {
switch (notification.getFeatureID(Resource.class)) {
case Resource.RESOURCE__IS_LOADED: {
if (!notification.getNewBooleanValue()) {
unloadedResources.add((Resource) notifier);
for (Iterator<Map.Entry<EObject, Resource>> i = unloadedEObjects.entrySet().iterator(); i.hasNext();) {
Map.Entry<EObject, Resource> entry = i.next();
if (entry.getValue() == notifier) {
i.remove();
// Don't keep track of proxies if memory-optimized unload is to be performed -
// they would never ever be cleared off again and result in a memory-leak
ExtendedResource extendedResource = ExtendedResourceAdapterFactory.INSTANCE.adapt((Resource) notifier);
if (extendedResource == null
|| extendedResource.getDefaultLoadOptions().get(ExtendedResource.OPTION_UNLOAD_MEMORY_OPTIMIZED) != Boolean.TRUE) {
EObject eObject = entry.getKey();
Collection<EStructuralFeature.Setting> settings = inverseCrossReferencer.get(eObject);
if (settings != null) {
for (EStructuralFeature.Setting setting : settings) {
try {
ReflectUtil.invokeInvisibleMethod(inverseCrossReferencer, "addProxy", eObject, //$NON-NLS-1$
setting.getEObject());
} catch (Exception ex) {
// Ignore exception
}
}
}
}
}
}
return;
}
}
}
}
super.selfAdapt(notification);
}
};
}
return null;
}
}