blob: 803cffa35fd9276c708d686884353804dc3e3e94 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008-2011 Chair for Applied Software Engineering,
* Technische Universitaet Muenchen.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Otto von Wesendonk, Edgar Mueller - initial API and implementation
******************************************************************************/
package org.eclipse.emf.emfstore.internal.server.connection.xmlrpc.util;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.ws.commons.util.Base64;
import org.apache.ws.commons.util.Base64.Encoder;
import org.apache.ws.commons.util.Base64.EncoderOutputStream;
import org.apache.xmlrpc.serializer.TypeSerializerImpl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.ResourceImpl;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.emfstore.common.extensionpoint.ESExtensionElement;
import org.eclipse.emf.emfstore.common.extensionpoint.ESExtensionPoint;
import org.eclipse.emf.emfstore.internal.common.CommonUtil;
import org.eclipse.emf.emfstore.internal.common.model.IdEObjectCollection;
import org.eclipse.emf.emfstore.internal.common.model.ModelElementId;
import org.eclipse.emf.emfstore.internal.common.model.util.ModelUtil;
import org.eclipse.emf.emfstore.internal.server.exceptions.SerializationException;
import org.eclipse.emf.emfstore.internal.server.model.versioning.ChangePackage;
import org.eclipse.emf.emfstore.internal.server.model.versioning.FileBasedChangePackage;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
/**
* Serializer for EObjects.
*
* @author ovonwesen
* @author emueller
*/
public class EObjectSerializer extends TypeSerializerImpl {
private static final String SELF_CONTAINMENT_CHECK_OPTION = "SelfContainmentCheck"; //$NON-NLS-1$
private static final String SERIALIZATION_OPTIONS_EXT = "org.eclipse.emf.emfstore.common.model.serializationOptions"; //$NON-NLS-1$
/**
* EObject Tag for parsing.
*/
public static final String EOBJECT_TAG = "EObject"; //$NON-NLS-1$
private static final String EX_EOBJECT_TAG = "ex:" + EOBJECT_TAG; //$NON-NLS-1$
private static boolean containmentCheckEnabled;
private static boolean serializationOptionsInitialized;
/**
* {@inheritDoc}
*/
public void write(ContentHandler pHandler, Object pObject) throws SAXException {
initSerializationOptions();
startElements(pHandler);
final char[] buffer = new char[1024];
final Encoder encoder = new Base64.SAXEncoder(buffer, 0, null, pHandler);
try {
URIConverter.WriteableOutputStream uws = null;
final OutputStream ostream = new EncoderOutputStream(encoder);
final BufferedOutputStream bos = new BufferedOutputStream(ostream);
try {
EObject eObject = (EObject) pObject;
XMIResource resource = (XMIResource) eObject.eResource();
if (eObject instanceof FileBasedChangePackage) {
final ChangePackage changePackage = FileBasedChangePackage.class.cast(eObject)
.toInMemoryChangePackage();
eObject = changePackage;
}
if (eObject instanceof IdEObjectCollection && resource != null) {
OutputStreamWriter writer = null;
try {
writer = new OutputStreamWriter(bos, CommonUtil.getEncoding());
uws = new URIConverter.WriteableOutputStream(writer, CommonUtil.getEncoding());
final Resource res = eObject.eResource();
checkResource(res);
res.save(uws, ModelUtil.getResourceSaveOptions());
} finally {
if (writer != null) {
writer.close();
}
}
} else {
EObject copy;
resource = createXmiResource();
if (eObject instanceof IdEObjectCollection) {
copy = ModelUtil.copyIdEObjectCollection((IdEObjectCollection) eObject, resource);
setResourceIds(eObject, resource);
} else {
copy = ModelUtil.clone(eObject);
}
resource.getContents().add(copy);
checkResource(resource);
resource.save(bos, ModelUtil.getResourceSaveOptions());
}
} catch (final SerializationException e) {
throw new SAXException(e);
} finally {
bos.close();
if (uws != null) {
uws.close();
}
}
} catch (final Base64.SAXIOException e) {
throw e.getSAXException();
} catch (final IOException e) {
throw new SAXException(e);
}
endElements(pHandler);
}
private static XMIResource createXmiResource() {
final XMIResource resource = (XMIResource) new ResourceSetImpl().createResource(ModelUtil.VIRTUAL_URI);
((ResourceImpl) resource).setIntrinsicIDToEObjectMap(new HashMap<String, EObject>());
return resource;
}
/**
* @param eObject
* @param resource
*/
private void setResourceIds(EObject eObject, XMIResource resource) {
final IdEObjectCollection collection = (IdEObjectCollection) eObject;
for (final EObject element : collection.getAllModelElements()) {
if (ModelUtil.isIgnoredDatatype(element)) {
continue;
}
final ModelElementId elementId = collection.getModelElementId(element);
resource.setID(element, elementId.getId());
}
}
/**
* @param pHandler
* @throws SAXException
*/
private void endElements(ContentHandler pHandler) throws SAXException {
pHandler.endElement(StringUtils.EMPTY, EOBJECT_TAG, EX_EOBJECT_TAG);
pHandler.endElement(StringUtils.EMPTY, VALUE_TAG, VALUE_TAG);
}
/**
* @param pHandler
* @throws SAXException
*/
private void startElements(ContentHandler pHandler) throws SAXException {
pHandler.startElement(StringUtils.EMPTY, VALUE_TAG, VALUE_TAG, ZERO_ATTRIBUTES);
pHandler.startElement(StringUtils.EMPTY, EOBJECT_TAG, EX_EOBJECT_TAG, ZERO_ATTRIBUTES);
}
private void checkResource(Resource resource) throws SerializationException {
if (!containmentCheckEnabled) {
return;
}
if (resource.getContents().size() != 1) {
throw new SerializationException(Messages.EObjectSerializer_UnexpectedNumberOfEObjects);
}
final EObject root = resource.getContents().get(0);
final Set<EObject> allChildEObjects = CommonUtil.getNonTransientContents(root);
final Set<EObject> allEObjects = new LinkedHashSet<EObject>(allChildEObjects);
allEObjects.add(root);
for (final EObject eObject : allEObjects) {
if (resource != eObject.eResource()) {
throw new SerializationException(Messages.EObjectSerializer_NonSelfContainedResource);
}
if (eObject.eIsProxy()) {
throw new SerializationException(Messages.EObjectSerializer_UnresolvedProxy);
}
}
}
/**
* Initializes the serialization options.
*/
private static void initSerializationOptions() {
if (serializationOptionsInitialized) {
return;
}
final ESExtensionElement element = new ESExtensionPoint(
SERIALIZATION_OPTIONS_EXT).getFirst();
if (element != null) {
containmentCheckEnabled = element.getBoolean(SELF_CONTAINMENT_CHECK_OPTION);
}
serializationOptionsInitialized = true;
}
}