/**
* Copyright 2005-2014 Restlet
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can
* select the license that you prefer but you may not use this file except in
* compliance with one of these Licenses.
*
* You can obtain a copy of the Apache 2.0 license at
* http://www.opensource.org/licenses/apache-2.0
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://restlet.com/products/restlet-framework
*
* Restlet is a registered trademark of Restlet S.A.S.
*/
package org.restlet.ext.emf;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Map;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.xmi.XMLOptions;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.EMOFResourceImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
import org.eclipse.emf.ecore.xmi.impl.XMLOptionsImpl;
import org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl;
import org.restlet.data.CharacterSet;
import org.restlet.data.MediaType;
import org.restlet.ext.emf.internal.EmfHtmlWriter;
import org.restlet.representation.OutputRepresentation;
import org.restlet.representation.Representation;
/**
* Representation based on the EMF library. By default, it can serialize and
* deserialize automatically in either XML, XMI or ECore.
*
* @see <a href="http://www.eclipse.org/modeling/emf/">EMF project</a>
* @author Jerome Louvel
* @param <T>
* The type to wrap.
*/
public class EmfRepresentation<T extends EObject> extends OutputRepresentation {
/** The maximum number of characters per line. */
private volatile int lineWidth;
/** The (parsed) object to format. */
private volatile T object;
/** The representation to parse. */
private volatile Representation representation;
/** Indicates if EMF references should be written as URI anchors. */
private volatile boolean usingEncodedAttributeStyle;
/**
* Constructor.
*
* @param mediaType
* The target media type. Supported values are
* {@link MediaType#APPLICATION_XMI},
* {@link MediaType#APPLICATION_ECORE} and XML media types.
* @param object
* The object to format.
*/
public EmfRepresentation(MediaType mediaType, T object) {
super(mediaType);
this.object = object;
this.representation = null;
this.usingEncodedAttributeStyle = true;
this.lineWidth = 80;
}
/**
* Constructor.
*
* @param representation
* The representation to parse.
*/
public EmfRepresentation(Representation representation) {
super(representation.getMediaType());
this.object = null;
this.representation = representation;
}
/**
* Creates and configure an EMF resource. Not to be confused with a Restlet
* resource. By default, it calls {@link #createEmfXmlResource(MediaType)}.
*
* @param mediaType
* The associated media type.
* @return A new configured EMF resource.
*/
protected Resource createEmfResource(MediaType mediaType) {
return createEmfXmlResource(mediaType);
}
/**
* Creates and configure an EMF resource. Not to be confused with a Restlet
* resource.
*
* @param mediaType
* The associated media type (ECore, XMI or XML).
* @return A new configured EMF resource.
*/
protected XMLResource createEmfXmlResource(MediaType mediaType) {
XMLResource result = null;
if (MediaType.APPLICATION_ECORE.isCompatible(getMediaType())) {
result = new EMOFResourceImpl();
} else if (MediaType.APPLICATION_XMI.isCompatible(getMediaType())) {
result = new XMIResourceImpl();
} else {
result = new XMLResourceImpl();
}
if (getCharacterSet() != null) {
result.setEncoding(getCharacterSet().getName());
} else {
result.setEncoding(CharacterSet.UTF_8.getName());
}
// Set XML load options
result.getDefaultLoadOptions().put(
XMLResource.OPTION_EXTENDED_META_DATA, Boolean.TRUE);
result.getDefaultLoadOptions().put(
XMLResource.OPTION_USE_LEXICAL_HANDLER, Boolean.TRUE);
result.getDefaultLoadOptions().put(
XMLResource.OPTION_USE_ENCODED_ATTRIBUTE_STYLE, Boolean.TRUE);
// Set XML save options
result.getDefaultSaveOptions().put(
XMLResource.OPTION_EXTENDED_META_DATA, Boolean.TRUE);
result.getDefaultSaveOptions().put(XMLResource.OPTION_LINE_WIDTH,
getLineWidth());
result.getDefaultSaveOptions().put(
XMLResource.OPTION_USE_ENCODED_ATTRIBUTE_STYLE,
isUsingEncodedAttributeStyle());
result.getDefaultSaveOptions().put(XMLResource.OPTION_SCHEMA_LOCATION,
Boolean.TRUE);
// Set other XML options
XMLOptions xmlOptions = new XMLOptionsImpl();
xmlOptions.setProcessAnyXML(true);
xmlOptions.setProcessSchemaLocations(true);
result.getDefaultLoadOptions().put(XMLResource.OPTION_XML_OPTIONS,
xmlOptions);
return result;
}
/**
* Returns the maximum number of characters per line. Defaults to 80.
*
* @return The maximum number of characters per line.
*/
public int getLineWidth() {
return lineWidth;
}
/**
* Returns the loading options. Null by default.
*
* @return The loading options.
*/
protected Map<?, ?> getLoadOptions() {
return null;
}
/**
* Returns the wrapped object either parsed from the representation or to be
* formatted.
*
* @return The wrapped object.
* @throws IOException
*/
@SuppressWarnings("unchecked")
public T getObject() throws IOException {
T result = null;
if (this.object != null) {
result = this.object;
} else if (this.representation != null) {
Resource emfResource = createEmfResource(this.representation
.getMediaType());
emfResource.load(this.representation.getStream(), getLoadOptions());
result = (T) emfResource.getContents().get(0);
}
return result;
}
/**
* Returns the saving options. Null by default.
*
* @return The saving options.
*/
protected Map<?, ?> getSaveOptions() {
return null;
}
/**
* Indicates if EMF references should be written as URI anchors.
*
* @return True if EMF references should be written as URI anchors.
*/
public boolean isUsingEncodedAttributeStyle() {
return usingEncodedAttributeStyle;
}
/**
* Sets the maximum number of characters per line.
*
* @param lineWidth
* The maximum number of characters per line.
*/
public void setLineWidth(int lineWidth) {
this.lineWidth = lineWidth;
}
/**
* Indicates if EMF references should be written as URI anchors.
*
* @param usingEncodedAttributeStyle
* True if EMF references should be written as URI anchors.
*/
public void setUsingEncodedAttributeStyle(boolean usingEncodedAttributeStyle) {
this.usingEncodedAttributeStyle = usingEncodedAttributeStyle;
}
/**
* Writes the representation based on a given EMF object.
*
* @param object
* The EMF object to serialize.
* @param outputStream
* The target output stream.
* @throws IOException
*/
public void write(EObject object, OutputStream outputStream)
throws IOException {
if (MediaType.TEXT_HTML.isCompatible(getMediaType())) {
EmfHtmlWriter htmlWriter = new EmfHtmlWriter(object);
htmlWriter.write(new OutputStreamWriter(outputStream,
((getCharacterSet() == null) ? CharacterSet.ISO_8859_1
.getName() : getCharacterSet().getName())));
} else {
Resource emfResource = createEmfResource(getMediaType());
emfResource.getContents().add(object);
emfResource.save(outputStream, getSaveOptions());
}
}
/**
* If this representation wraps an {@link EObject}, then it tries to write
* it as either XML, XMI or ECore/EMOF depending on the media type set.
*
* Note that in order to write this {@link EObject}, an EMF resource is
* created, configured for proper serialization and the {@link EObject} is
* then added to the content of this resource. This could has a side effect
* of removing it from a previous resource/container.
*/
@Override
public void write(OutputStream outputStream) throws IOException {
if (this.representation != null) {
this.representation.write(outputStream);
} else if (object != null) {
write(this.object, outputStream);
}
}
}