blob: 5e2586a01991380d810f2898a7b829b3ef773866 [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2008-2018 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 - [400897] ExtendedResourceAdapter's approach of reflectively clearing all EObject fields when performing memory-optimized unloads bears the risk of leaving some EObjects leaked
* itemis - [441970] Result returned by ExtendedResourceAdapter#getHREF(EObject) must default to complete object URI (edit)
* itemis - [442342] Sphinx doen't trim context information from proxy URIs when serializing proxyfied cross-document references
* itemis - [443647] Enable HREF representing serialized cross-document references to be customized through ExtendedResource of resource being serialized
* itemis - [458862] Navigation from problem markers in Check Validation view to model editors and Model Explorer view broken
* itemis - [460260] Expanded paths are collapsed on resource reload
* itemis - [485407] Enable eager post-load proxy resolution to support manifold URI fragments referring to the same object
* itemis - [501108] The tree viewer state restoration upon Eclipse startup not working for model elements being added after the loading of the underlying has been finished
* itemis - [531560] Provide better control over schema locations being added to serialized model files
*
* </copyright>
*/
package org.eclipse.sphinx.emf.resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.sphinx.emf.Activator;
import org.eclipse.sphinx.emf.util.EcoreResourceUtil;
import org.eclipse.sphinx.platform.util.ReflectUtil;
/**
* {@link Adapter}-based implementation of {@link ExtendedResource}.
*/
public class ExtendedResourceAdapter extends AdapterImpl implements ExtendedResource {
private static final URI EMPTY_URI = URI.createURI(""); //$NON-NLS-1$
/**
* The map of options that are used to to control the handling of problems encountered while the {@link Resource
* resource} has been loaded or saved.
*/
protected Map<Object, Object> problemHandlingOptions;
/**
* Whether to use context-aware proxy URIs or not.
*/
private Boolean useContextAwareProxyURIs = null;
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#getDefaultLoadOptions()
*/
@Override
@SuppressWarnings("unchecked")
public Map<Object, Object> getDefaultLoadOptions() {
Map<Object, Object> defaultLoadOptions = null;
Resource targetResource = (Resource) getTarget();
if (targetResource instanceof XMLResource) {
defaultLoadOptions = ((XMLResource) targetResource).getDefaultLoadOptions();
} else {
try {
defaultLoadOptions = (Map<Object, Object>) ReflectUtil.getInvisibleFieldValue(targetResource, "defaultLoadOptions"); //$NON-NLS-1$
} catch (Exception ex) {
// Ignore exception
}
}
return defaultLoadOptions != null ? defaultLoadOptions : new HashMap<Object, Object>();
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#getDefaultSaveOptions()
*/
@Override
@SuppressWarnings("unchecked")
public Map<Object, Object> getDefaultSaveOptions() {
Map<Object, Object> defaultSaveOptions = null;
Resource targetResource = (Resource) getTarget();
if (targetResource instanceof XMLResource) {
defaultSaveOptions = ((XMLResource) targetResource).getDefaultLoadOptions();
} else {
try {
defaultSaveOptions = (Map<Object, Object>) ReflectUtil.getInvisibleFieldValue(targetResource, "defaultSaveOptions"); //$NON-NLS-1$
} catch (Exception ex) {
// Ignore exception
}
}
return defaultSaveOptions != null ? defaultSaveOptions : new HashMap<Object, Object>();
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#getLoadProblemOptions()
*/
@Override
public Map<Object, Object> getProblemHandlingOptions() {
if (problemHandlingOptions == null) {
problemHandlingOptions = new HashMap<Object, Object>();
problemHandlingOptions.put(OPTION_MAX_PROBLEM_MARKER_COUNT, OPTION_MAX_PROBLEM_MARKER_COUNT_DEFAULT);
problemHandlingOptions.put(OPTION_XML_WELLFORMEDNESS_PROBLEM_FORMAT_STRING, OPTION_XML_WELLFORMEDNESS_PROBLEM_FORMAT_STRING_DEFAULT);
problemHandlingOptions.put(OPTION_XML_VALIDITY_PROBLEM_FORMAT_STRING, OPTION_XML_VALIDITY_PROBLEM_FORMAT_STRING_DEFAULT);
}
return problemHandlingOptions;
}
protected boolean isUseContextAwareProxyURIs() {
if (useContextAwareProxyURIs == null) {
Map<Object, Object> loadOptions = getDefaultLoadOptions();
useContextAwareProxyURIs = !Boolean.FALSE.equals(loadOptions.get(OPTION_USE_CONTEXT_AWARE_PROXY_URIS));
}
return useContextAwareProxyURIs;
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#isResourceFullyLoaded()
*/
@Override
public boolean isFullyLoaded() {
return ((Resource) getTarget()).isLoaded();
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#unloaded(org.eclipse.emf.ecore.InternalEObject)
*/
@Override
public void unloaded(EObject eObject) {
// Remove all adapters from unloaded eObject
eObject.eAdapters().clear();
// Memory-optimized unload to be performed?
if (getDefaultLoadOptions().get(ExtendedResource.OPTION_UNLOAD_MEMORY_OPTIMIZED) == Boolean.TRUE) {
// Turn unloaded eObject into a proxy using an as short as possible dummy URI
/*
* !! Important Note !! Setting the regular full proxy URI would take way too much memory and is generally useless when
* the complete ResourceSet, or a self-contained set of resources with no outgoing and incoming cross-document
* references gets unloaded (which typically happens when a project or the entire workbench is closed). However, we must
* leave an as short as possible dummy URI in place to make sure that clients which access the unloaded eObject for
* whatever reason subsequently don't end up considering it a regular eObject that is still loaded.
*/
((InternalEObject) eObject).eSetProxyURI(EMPTY_URI);
} else {
// Turn unloaded eObject into a proxy using the regular full proxy URI; enable proxy URI creation to be
// customized by delegating it to #getURI()
URI uri = getURI(eObject);
if (!eObject.eIsProxy()) {
((InternalEObject) eObject).eSetProxyURI(uri);
}
// Is proxy URI a fragment-based URI not knowing the resource containing the eObject it refers to?
if (uri != null && uri.segmentCount() == 0) {
// Add adapter to unloaded eObject keeping track of the resource which formerly contained it
eObject.eAdapters().add(new OldResourceProviderAdapter((Resource) getTarget()));
}
}
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#getURI(org.eclipse.emf.ecore.EObject)
*/
@Override
public URI getURI(EObject eObject) {
return getURI(null, null, eObject, false);
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#getURI(org.eclipse.emf.ecore.EObject, boolean)
*/
@Override
public URI getURI(EObject eObject, boolean resolve) {
return getURI(null, null, eObject, resolve);
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#getURI(org.eclipse.emf.ecore.EObject,
* org.eclipse.emf.ecore.EStructuralFeature, org.eclipse.emf.ecore.EObject)
*/
@Override
public URI getURI(EObject oldOwner, EStructuralFeature oldFeature, EObject eObject) {
return getURI(oldOwner, oldFeature, eObject, false);
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#getURI(org.eclipse.emf.ecore.EObject,
* org.eclipse.emf.ecore.EStructuralFeature, org.eclipse.emf.ecore.EObject, boolean)
*/
@Override
public URI getURI(EObject oldOwner, EStructuralFeature oldFeature, EObject eObject, boolean resolve) {
Assert.isNotNull(eObject);
// eObject being a proxy?
if (eObject.eIsProxy()) {
// Use proxy URI
URI uri = ((InternalEObject) eObject).eProxyURI();
// Be sure that it gets resolved when needed
if (resolve) {
return resolveURI(uri);
}
return uri;
}
// Removed eObject?
else if (eObject.eResource() == null) {
// Restore the given eObject's old URI that it had before it got removed from its resource
return getOldURI(oldOwner, oldFeature, eObject, resolve);
}
// Normal eObject
else {
// Form the given eObject's URI relative to its resource
Resource resource = eObject.eResource();
return getURI(resource.getURI(), resource.getURIFragment(eObject), resolve);
}
}
protected URI getURI(URI resourceURI, String eObjectURIFragment, boolean resolve) {
return getURI(resourceURI, eObjectURIFragment);
}
protected URI getURI(URI resourceURI, String eObjectURIFragment) {
return resourceURI == null ? URI.createURI(URI_FRAGMENT_SEPARATOR + eObjectURIFragment) : resourceURI.appendFragment(eObjectURIFragment);
}
protected URI getOldURI(EObject oldOwner, EStructuralFeature oldFeature, EObject eObject, boolean resolve) {
Assert.isNotNull(eObject);
if (oldOwner != null && oldFeature != null) {
// Retrieve the oldOwner's URI fragment
URI oldOwnerURI = EcoreResourceUtil.getURI(oldOwner);
String oldOwnerURIFragment = oldOwnerURI.fragment();
// Restore URI fragment segment that pointed from oldOwner to removed eObject (which may be the
// given eObject itself or some other eObject that directly or indirectly contains given eObject
EObject eObjectRootContainer = EcoreUtil.getRootContainer(eObject);
String eObjectRootContainerURIFragmentSegment = ((InternalEObject) oldOwner).eURIFragmentSegment(oldFeature, eObjectRootContainer);
// Calculate URI fragment segments for given eObject in case that it is an eObject that is directly
// or indirectly contained by the removed eObject
List<String> eObjectURIFragmentSegments = new ArrayList<String>();
InternalEObject internalEObject = (InternalEObject) eObject;
for (InternalEObject container = internalEObject.eInternalContainer(); container != null; container = internalEObject
.eInternalContainer()) {
eObjectURIFragmentSegments.add(container.eURIFragmentSegment(internalEObject.eContainingFeature(), internalEObject));
internalEObject = container;
}
// Compose and return the eObject' old URI
StringBuilder oldEObjectURIFragment = new StringBuilder();
oldEObjectURIFragment.append(oldOwnerURIFragment);
oldEObjectURIFragment.append(URI_SEGMENT_SEPARATOR);
oldEObjectURIFragment.append(eObjectRootContainerURIFragmentSegment);
for (int i = eObjectURIFragmentSegments.size() - 1; i >= 0; --i) {
oldEObjectURIFragment.append(URI_SEGMENT_SEPARATOR);
oldEObjectURIFragment.append(eObjectURIFragmentSegments.get(i));
}
return getURI(oldOwnerURI.trimFragment(), oldEObjectURIFragment.toString(), resolve);
} else {
// Form the given eObject's URI relative to this adapter's target resource
Resource oldResource = (Resource) getTarget();
return getURI(oldResource.getURI(), oldResource.getURIFragment(eObject), resolve);
}
}
protected URI resolveURI(URI uri) {
Assert.isNotNull(uri);
// Is given URI a fragment-based URI not knowing the resource that contains the eObject it refers to?
if (uri.segmentCount() == 0) {
// Form resolved URI by using the URI of this adapter's target resource as prefix and the fragment of given
// URI as postfix
Resource resource = (Resource) getTarget();
URI resourceURI = resource.getURI();
String eObjectURIFragment = uri.fragment();
return getURI(resourceURI, eObjectURIFragment);
}
return uri;
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#createURI(java.lang.String, org.eclipse.emf.ecore.EClass)
*/
@Override
public URI createURI(String uriLiteral, EClass eClass) {
// Return URI object corresponding to given URI literal as is
return URI.createURI(uriLiteral);
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#getHREF(org.eclipse.emf.ecore.EObject)
*/
@Override
public URI getHREF(EObject eObject) {
// Let HREF default to URI of given object
return getURI(eObject);
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#nomalizeURIFragment(java.lang.String)
*/
@Override
public String nomalizeURIFragment(String uriFragment) {
// Let normalized URI fragment default to given URI fragment
return uriFragment;
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#validateURI(java.lang.String)
*/
@Override
public Diagnostic validateURI(String uri) {
Assert.isNotNull(uri);
try {
URI.createURI(uri, true);
} catch (IllegalArgumentException ex) {
return new BasicDiagnostic(Activator.getPlugin().getSymbolicName(), Diagnostic.ERROR, ex.getMessage(), new Object[] {});
}
return Diagnostic.OK_INSTANCE;
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#augmentToContextAwareProxy(org.eclipse.emf.ecore.EObject)
*/
@Override
public void augmentToContextAwareProxy(EObject proxy) {
// Context-aware proxy URIs required?
if (isUseContextAwareProxyURIs()) {
Resource contextResource = (Resource) getTarget();
ResourceSet resourceSet = contextResource.getResourceSet();
// Augment the proxy's URI to a context-aware proxy URI
if (resourceSet instanceof ExtendedResourceSet) {
((ExtendedResourceSet) resourceSet).augmentToContextAwareProxy(proxy, contextResource);
}
}
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#getSchemaLocationEntries(java.util.Map)
*/
@Override
public Map<String, String> getSchemaLocationEntries(Map<?, ?> options) {
return Collections.emptyMap();
}
/*
* @see org.eclipse.sphinx.emf.resource.ExtendedResource#trimProxyContextInfo(org.eclipse.emf.common.util.URI)
*/
@Override
public URI trimProxyContextInfo(URI proxyURI) {
// Context-aware proxy URIs being used?
if (isUseContextAwareProxyURIs()) {
Resource contextResource = (Resource) getTarget();
ResourceSet resourceSet = contextResource.getResourceSet();
// Trim all proxy context information
if (resourceSet instanceof ExtendedResourceSet) {
return ((ExtendedResourceSet) resourceSet).trimProxyContextInfo(proxyURI);
}
}
return proxyURI;
}
/*
* @see org.eclipse.emf.common.notify.impl.AdapterImpl#isAdapterForType(java.lang.Object)
*/
@Override
public boolean isAdapterForType(Object type) {
return type == ExtendedResource.class;
}
/*
* @see org.eclipse.emf.common.notify.impl.AdapterImpl#setTarget(org.eclipse.emf.common.notify.Notifier)
*/
@Override
public void setTarget(Notifier newTarget) {
Assert.isLegal(newTarget == null || newTarget instanceof Resource);
super.setTarget(newTarget);
}
}