package de.hub.emffrag.fragmentation; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.resource.URIConverter; import org.eclipse.emf.ecore.resource.impl.BinaryResourceImpl; import com.google.common.base.Throwables; import de.hub.emffrag.EmfFragActivator; import de.hub.emffrag.util.EMFFragUtil; import de.hub.emffrag.util.EMFFragUtil.FragmentationType; public class BinaryFragmentImpl extends BinaryResourceImpl implements Fragment { private final FragmentedModel model; public BinaryFragmentImpl(URI uri, FragmentedModel model) { super(uri); this.model = model; } @Override public FragmentedModel getFragmentedModel() { return model; } @Override public void detached(EObject eObject) { super.detached(eObject); if (getContents().isEmpty()) { try { delete(null); } catch (IOException e) { Throwables.propagate(e); } } } /** * EMF does not break down the reference graph of a resources contents * property. Under some circumstances the JVM cannot remove this contents * even if the resource is unloaded and removed from the resource set. This * change in the EMF standard behavior of the resources fixes that. */ @Override protected void doUnload() { Collection<FInternalObject> nonFragmentingContents = new ArrayList<FInternalObject>(); for (EObject topLevel : unloadingContents) { EMFFragUtil.collectAllNonFragmentingContents((FInternalObject) topLevel, nonFragmentingContents); } super.doUnload(); for (EObject eObject : nonFragmentingContents) { FInternalObjectImpl internalObject = (FInternalObjectImpl) eObject; internalObject.trulyUnload(); } } @Override protected TreeIterator<EObject> getAllProperContents(EObject eObject) { return EMFFragUtil.getAllNonFragmentingContentsIterator(eObject); } @Override protected TreeIterator<EObject> getAllProperContents(List<EObject> contents) { return EMFFragUtil.getAllNonFragmentingContentsIterator(contents); } @Override protected void doSave(OutputStream outputStream, Map<?, ?> options) throws IOException { // This is a copy of the super method, except for the market pieces // of code if (outputStream instanceof URIConverter.Saveable) { ((URIConverter.Saveable) outputStream).saveResource(this); } else { boolean buffer = !(outputStream instanceof BufferedOutputStream); if (buffer) { int bufferCapacity = getBufferCapacity(options); if (bufferCapacity > 0) { outputStream = new BufferedOutputStream(outputStream, bufferCapacity); } else { buffer = false; } } try { // We use our own EObjectOutputStream // EObjectOutputStream eObjectOutputStream = new // EObjectOutputStream(outputStream, options); EObjectOutputStream eObjectOutputStream = new MyEObjectOutputStream(outputStream, options); eObjectOutputStream.saveResource(this); } finally { if (buffer) { outputStream.flush(); } } } } @Override protected void doLoad(InputStream inputStream, Map<?, ?> options) throws IOException { // This is a copy of the super method, except for the market pieces // of code if (inputStream instanceof URIConverter.Loadable) { ((URIConverter.Loadable) inputStream).loadResource(this); } else { if (!(inputStream instanceof BufferedInputStream)) { int bufferCapacity = getBufferCapacity(options); if (bufferCapacity > 0) { inputStream = new BufferedInputStream(inputStream, bufferCapacity); } } // We use our own EObjectOutputStream // EObjectInputStream eObjectInputStream = new // EObjectInputStream(inputStream, options); EObjectInputStream eObjectInputStream = new MyEObjectInputStream(inputStream, options); eObjectInputStream.loadResource(this); } } @Override protected void unloaded(InternalEObject internalEObject) { super.unloaded(internalEObject); EmfFragActivator.instance.globalEventListener.onUnloadInternalObject((FInternalObjectImpl)internalEObject); } @Override protected boolean isAttachedDetachedHelperRequired() { return false; } private class MyEObjectInputStream extends EObjectInputStream { public MyEObjectInputStream(InputStream is, Map<?, ?> options) throws IOException { super(is, options); } @Override protected EPackageData readEPackage() throws IOException { EPackageData ePackageData = super.readEPackage(); if (resourceSet != null) { EPackage ePackage = resourceSet.getPackageRegistry().getEPackage(ePackageData.ePackage.getNsURI()); if (ePackage != null) { ePackageData.ePackage = ePackage; } if (ePackageData.eClassData.length != ePackage.getEClassifiers().size()) { ePackageData.eClassData = new EClassData[ePackage.getEClassifiers().size()]; } } return ePackageData; } @Override public InternalEObject loadEObject() throws IOException { InternalEObject loadEObject = super.loadEObject(); FInternalObjectImpl fInternalObject = (FInternalObjectImpl)loadEObject; String id = fInternalObject.getId(); if (id != null) { // pre-populate the ID map on load getIntrinsicIDToEObjectMap().put(id, loadEObject); } return loadEObject; } } public String getURIFragment(EObject eObject) { if (eObject.eIsProxy()) { URI eProxyURI = ((InternalEObject) eObject).eProxyURI(); if (eProxyURI.trimFragment().equals(getURI())) { return eProxyURI.fragment(); } else { throw new RuntimeException("Unreachable?"); } } else { return super.getURIFragment(eObject); } } public class MyEObjectOutputStream extends EObjectOutputStream { boolean isWritingCrossReferenceURI = false; FInternalObjectImpl currentObject = null; public MyEObjectOutputStream(OutputStream os, Map<?, ?> options) throws IOException { super(os, options); } @Override public void saveEObject(InternalEObject internalEObject, Check check) throws IOException { // Ensure that URIs in the id // index are saved, when the object is saved. FInternalObjectImpl fInternalObject = (FInternalObjectImpl) internalEObject; EmfFragActivator.instance.idSemantics.onObjectSaved(fInternalObject); if (check == Check.RESOURCE) { isWritingCrossReferenceURI = true; currentObject = (FInternalObjectImpl) internalEObject; if (internalEObject.eIsProxy()) { // Here I am not sure about what EMF is doing. On // DIRECT_RESOURCE is simply saves the URI of eProxies, on // RESOURCE it tries to determine a proxies resource by // walking up its containers, finding the wrong resources. // But for some reason, EMF chooses RESOURCE for all proxy // resolving non containment lists. check = Check.DIRECT_RESOURCE; } } else { isWritingCrossReferenceURI = false; } super.saveEObject(internalEObject, check); } @Override protected void saveFeatureValue(InternalEObject internalEObject, int featureID, EStructuralFeatureData eStructuralFeatureData) throws IOException { FragmentationType type = EMFFragUtil.getFragmentationType(internalEObject.eClass().getEStructuralFeature(featureID)); if (type == FragmentationType.FragmentsContainment || type == FragmentationType.None) { super.saveFeatureValue(internalEObject, featureID, eStructuralFeatureData); } } @Override public void writeURI(URI uri, String uriFragment) throws IOException { if (!isWritingCrossReferenceURI) { super.writeURI(uri, uriFragment); } else { URI refURI = null; Fragment fragment = (Fragment) currentObject.eResource(); FragmentedModel model = null; if (fragment != null) { model = fragment.getFragmentedModel(); } else { model = EmfFragActivator.instance.defaultModel; } if (model != null) { refURI = EmfFragActivator.instance.idSemantics.getURI(currentObject, model, false, null); } if (refURI != null) { // basically the code from [super.writeURI(refURI, null)] // but without the failing assertion that I do not // understand. Integer id = uriToIDMap.get(refURI); if (id == null) { int idValue = uriToIDMap.size(); uriToIDMap.put(refURI, idValue); writeCompressedInt(idValue); writeString(deresolve(refURI).toString()); } else { writeCompressedInt(id); } writeString(null); } else { super.writeURI(uri, uriFragment); } } } } }