de.dfki.iui.mmds.core.emf.persistence.EmfPersistence.java Source code

Java tutorial

Introduction

Here is the source code for de.dfki.iui.mmds.core.emf.persistence.EmfPersistence.java

Source

/*******************************************************************************
 * The Creative Commons CC-BY-NC 4.0 License
 * http://creativecommons.org/licenses/by-nc/4.0/legalcode
 *
 * Creative Commons (CC) by DFKI GmbH
 * - Vanessa Hahn <Vanessa.Hahn@dfki.de>
 * - Robert Nesselrath <rnesselrath@gmail.com>
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 *******************************************************************************/
package de.dfki.iui.mmds.core.emf.persistence;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.io.input.ReaderInputStream;
import org.apache.commons.io.output.WriterOutputStream;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EPackage.Registry;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.BasicExtendedMetaData;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.ExtendedMetaData;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.URIHandlerImpl;
import org.eclipse.emf.ecore.xmi.impl.XMLResourceFactoryImpl;
import org.emfjson.EMFJs;
import org.emfjson.jackson.resource.JsonResourceFactory;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

import de.dfki.iui.mmds.core.emf.utils.EmfUtils;

public class EmfPersistence {

    static final Logger logger = Logger.getLogger(EmfPersistence.class);
    static final ExtendedMetaData modelMetaData = new BasicExtendedMetaData(EPackage.Registry.INSTANCE);
    public static int MAX_STRING_SIZE = -1;

    // how to handle non-containment references?
    public enum NonContainmentReferenceHandling {
        INLINE, // references are integrated inline into as child node
        KEEP_ORIGINAL_LOCATION, // keeps original references to external resources
        ADD_TO_RESOURCE, // adds all external resources to this resource (XML representation then)
    }

    public static Resource readFromFile(String file) throws IOException {
        URI uri = URI.createURI(file);
        return readFromUri(uri);
    }

    public static Resource readFromUri(URI uri) throws IOException {
        ResourceSet resourceSet = new ResourceSetImpl();
        Resource resource = resourceSet.createResource(uri);
        read(resource, null);
        return resource;
    }

    public static Resource readFromString(String input) throws IOException {
        StringReader stringReader = new StringReader(input);
        ReaderInputStream is = new ReaderInputStream(stringReader, Charset.forName("UTF-8"));
        Resource resource = readFromStream(is);
        is.close();
        return resource;
    }

    public static Resource readFromJsonString(String input, EClass root) throws IOException {
        StringReader stringReader = new StringReader(input);
        ReaderInputStream is = new ReaderInputStream(stringReader, Charset.forName("UTF-8"));

        Map<String, Object> options = new HashMap<String, Object>();
        options.put(EMFJs.OPTION_ROOT_ELEMENT, root);

        ResourceSet resourceSet = new ResourceSetImpl();
        Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("json", new JsonResourceFactory());

        Resource resource = resourceSet.createResource(URI.createURI("in.json"));
        resource.load(is, options);

        return resource;
    }

    public static Resource readFromStream(InputStream is) throws IOException {
        XMLResourceFactoryImpl factory = new XMLResourceFactoryImpl();

        Resource resource = factory.createResource(URI.createURI(""));
        read(resource, is);
        return resource;
    }

    public static void read(Resource resource, InputStream is) throws IOException {
        Map<Object, Object> loadOptions;
        if (resource.getResourceSet() != null) {
            loadOptions = resource.getResourceSet().getLoadOptions();
        } else {
            loadOptions = new HashMap<Object, Object>();
        }
        loadOptions.put(XMLResource.OPTION_URI_HANDLER, new URIHandlerImpl.PlatformSchemeAware());
        loadOptions.put(XMIResource.OPTION_ENCODING, "UTF-8");
        loadOptions.put(XMIResource.OPTION_EXTENDED_META_DATA, modelMetaData);
        loadOptions.put(XMLResource.OPTION_USE_ENCODED_ATTRIBUTE_STYLE, Boolean.FALSE);
        if (is != null) {
            resource.load(is, loadOptions);
        } else {
            resource.load(loadOptions);
        }
    }

    public static Resource writeToFile(EObject instance, String file, NonContainmentReferenceHandling refOption,
            HashMap<String, Object> saveOptions) throws IOException {
        URI uri = URI.createFileURI(file);
        return writeToUri(instance, uri, refOption, saveOptions);
    }

    public static Resource writeToUri(EObject instance, URI uri, NonContainmentReferenceHandling refOption,
            HashMap<String, Object> saveOptions) throws IOException {

        Resource resource = new XMLResourceFactoryImpl().createResource(uri);
        write(instance, resource, null, refOption, saveOptions);
        return resource;
    }

    public static String writeToString(EObject instance) {
        try {
            String str = writeToString(instance, NonContainmentReferenceHandling.INLINE, null);
            if (MAX_STRING_SIZE > 0 && str.length() > MAX_STRING_SIZE) {
                return str.substring(0, MAX_STRING_SIZE) + " ... ... ... string is too long (length: "
                        + str.length() + " > allowed: " + MAX_STRING_SIZE + ", see MAX_STRING_SIZE)\n";
            }
            return str;
        } catch (IOException e) {
            logger.error(e.getLocalizedMessage());
            e.printStackTrace();
            return "";
        }
    }

    public static String writeToString(EObject instance, NonContainmentReferenceHandling refOption,
            HashMap<String, Object> saveOptions) throws IOException {
        StringWriter stringWriter = new StringWriter();
        WriterOutputStream os = new WriterOutputStream(stringWriter, Charset.forName("UTF-8"));
        writeToStream(instance, os, refOption, saveOptions);
        os.close();
        return stringWriter.toString().trim();
    }

    public static String writeToJsonString(EObject instance) throws IOException {
        return writeToJsonString(instance, NonContainmentReferenceHandling.INLINE, null);
    }

    public static String writeToJsonString(EObject instance, NonContainmentReferenceHandling refOption,
            HashMap<String, Object> saveOptions) throws IOException {
        StringWriter stringWriter = new StringWriter();
        WriterOutputStream os = new WriterOutputStream(stringWriter, Charset.forName("UTF-8"));
        JsonResourceFactory factory = new JsonResourceFactory();
        Resource resource = factory.createResource(URI.createURI(""));
        write(instance, resource, os, refOption, saveOptions);
        os.close();
        return stringWriter.toString().trim();
    }

    public static void writeToStream(EObject instance, OutputStream os, NonContainmentReferenceHandling refOption,
            HashMap<String, Object> saveOptions) throws IOException {
        XMLResourceFactoryImpl factory = new XMLResourceFactoryImpl();
        Resource resource = factory.createResource(URI.createURI(""));
        write(instance, resource, os, refOption, saveOptions);
    }

    public static void writeToStream(EObject instance, OutputStream os) throws IOException {
        writeToStream(instance, os, NonContainmentReferenceHandling.INLINE, null);
    }

    public static void write(Resource resource, OutputStream os, Map<String, Object> saveOptions)
            throws IOException {
        ExtendedMetaData modelMetaData;
        if (resource.getResourceSet() != null) {
            modelMetaData = new BasicExtendedMetaData(resource.getResourceSet().getPackageRegistry());
        } else {
            modelMetaData = new BasicExtendedMetaData(Registry.INSTANCE);
        }
        HashMap<String, Object> options = new HashMap<String, Object>();

        options.put(XMIResource.OPTION_ENCODING, "UTF-8");
        if (!options.containsKey(XMIResource.OPTION_KEEP_DEFAULT_CONTENT)) {
            options.put(XMIResource.OPTION_KEEP_DEFAULT_CONTENT, Boolean.TRUE);
        }
        options.put(XMIResource.OPTION_EXTENDED_META_DATA, modelMetaData);
        options.put(XMLResource.OPTION_URI_HANDLER, new URIHandlerImpl.PlatformSchemeAware());
        options.put(XMLResource.OPTION_USE_ENCODED_ATTRIBUTE_STYLE, Boolean.FALSE);
        if (saveOptions != null) {
            options.putAll(saveOptions);
        }
        if (os != null) {
            resource.save(os, options);
        } else {
            resource.save(options);
        }
    }

    private static void write(EObject instance, Resource resource, OutputStream os,
            NonContainmentReferenceHandling refOption, Map<String, Object> saveOptions) throws IOException {
        if (refOption == null) {
            refOption = NonContainmentReferenceHandling.KEEP_ORIGINAL_LOCATION;
        }
        HashSet<EObject> alreadyVisited = new HashSet<EObject>();
        List<EObject> rootList = new ArrayList<EObject>();

        if (refOption == NonContainmentReferenceHandling.ADD_TO_RESOURCE) {
            instance = EmfUtils.clone(instance);
            resource.getContents().add(instance);
            collectObjectsWithoutResource(instance, alreadyVisited, rootList, refOption);
            resource.getContents().addAll(rootList);
            write(resource, os, saveOptions);
        } else if (refOption == NonContainmentReferenceHandling.KEEP_ORIGINAL_LOCATION) {
            instance = EcoreUtil.copy(instance);
            resource.getContents().add(instance);
            collectObjectsWithoutResource(instance, alreadyVisited, rootList, refOption);

            Resource resourceTemp = new XMLResourceFactoryImpl().createResource(URI.createURI(""));
            resourceTemp.getContents().addAll(rootList);
            write(resource, os, saveOptions);
        } else if (refOption == NonContainmentReferenceHandling.INLINE) {
            instance = EmfUtils.clone(instance);

            resource.getContents().add(instance);
            // Reads to DOM and injects dependencies(replaces href nodes)

            Document d;
            Map<String, Namespace> namespaces = new HashMap<String, Namespace>();
            try {
                d = createDocFromEObject(instance, namespaces);
                Set<EObject> alreadyHandled = new HashSet<EObject>();
                dfs(instance, d.getRootElement(), alreadyHandled, namespaces);
                for (String prefix : namespaces.keySet()) {
                    Namespace namespace = d.getRootElement().getNamespace(prefix);
                    if (namespace == null)
                        d.getRootElement().addNamespaceDeclaration(namespaces.get(prefix));
                }
                XMLOutputter out = new XMLOutputter();
                out.setFormat(Format.getPrettyFormat());
                out.output(d, os);
            } catch (Exception e) {
                logger.error("An error occured while serializing an object:\n" + e.getLocalizedMessage());
                e.printStackTrace();
            }
        }
    }

    /**
     * Run DFS on EObject reference tree
     * 
     * @param object
     * @param node
     * @param alreadyHandled
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    protected static void dfs(EObject object, Element node, Set<EObject> alreadyHandled,
            Map<String, Namespace> namespaces) throws Exception {

        if (alreadyHandled.contains(object))
            return;
        else {
            alreadyHandled.add(object);
        }

        // traverse references and append them to the document
        for (EReference ref : object.eClass().getEAllReferences()) {
            if (ref.isTransient())
                continue;

            if (ref.isMany()) {
                int id = 0;

                for (EObject content : ((List<EObject>) object.eGet(ref))) {
                    if (!(content.eClass().getEPackage() instanceof EcorePackage)) {
                        Document n = createDocFromEObject(content, namespaces);
                        dfs(content, n.getRootElement(), alreadyHandled, namespaces);
                        if (!ref.isContainment()) {
                            updateMember(node, ref, id, content, n);
                            id++;
                        }
                    }
                }
            } else {
                EObject content = ((EObject) object.eGet(ref));
                if (content != null && !(content.eClass().getEPackage() instanceof EcorePackage)) {
                    Document n = createDocFromEObject(content, namespaces);
                    dfs(content, n.getRootElement(), alreadyHandled, namespaces);
                    if (!ref.isContainment())
                        updateMember(node, ref, 0, content, n);
                }
            }
        }
    }

    /**
     * Serialize an object to XML using standard EMF
     * 
     * @param object
     * @return
     * @throws Exception
     */
    private static Document createDocFromEObject(EObject object, Map<String, Namespace> namespaces)
            throws Exception {
        HashMap<String, Object> options = new HashMap<String, Object>();
        options.put(XMIResource.OPTION_USE_ENCODED_ATTRIBUTE_STYLE, Boolean.FALSE);
        options.put(XMIResource.OPTION_DECLARE_XML, Boolean.FALSE);
        options.put(XMLResource.OPTION_FORMATTED, Boolean.FALSE);
        options.put(XMLResource.OPTION_KEEP_DEFAULT_CONTENT, Boolean.TRUE);

        String result = writeToString(object, NonContainmentReferenceHandling.KEEP_ORIGINAL_LOCATION, options);
        Document doc = null;
        try {
            doc = new SAXBuilder().build(new StringReader(result));
            Namespace namespace = doc.getRootElement().getNamespace();
            if (!namespaces.containsKey(namespace.getPrefix()))
                namespaces.put(namespace.getPrefix(), namespace);
            for (Namespace additionalNamespace : doc.getRootElement().getAdditionalNamespaces()) {
                if (!namespaces.containsKey(additionalNamespace.getPrefix()))
                    namespaces.put(additionalNamespace.getPrefix(), additionalNamespace);
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
            assert (false);
        }
        return doc;
    }

    private static void collectObjectsWithoutResource(EObject eObject, HashSet<EObject> alreadyVisited,
            List<EObject> rootList, NonContainmentReferenceHandling refOption) {

        if (alreadyVisited.contains(eObject))
            return;
        alreadyVisited.add(eObject);
        if ((eObject.eClass().getEPackage() instanceof EcorePackage))
            return;
        if (eObject.eResource() == null) {
            if (eObject.eContainer() == null) {
                rootList.add(eObject);
            } else {
                collectObjectsWithoutResource(eObject.eContainer(), alreadyVisited, rootList, refOption);
            }
        }
        for (EReference eref : eObject.eClass().getEAllReferences()) {
            if (eref.isTransient()) {
                EAnnotation annotation = eref.getEAnnotation("http:///org/eclipse/emf/ecore/util/ExtendedMetaData");
                if (annotation == null || !annotation.getDetails().containsKey("group")) {
                    continue;
                }
            }
            final Object value = eObject.eGet(eref);
            if (value == null) {
                continue;
            }
            if (value instanceof List) {
                for (Object obj : (List<?>) value) {
                    if (obj == null) {
                        logger.error("There seems to be a unresolved reference. Please check the console output!");
                    }
                    collectObjectsWithoutResource((EObject) obj, alreadyVisited, rootList, refOption);
                }
            } else {
                collectObjectsWithoutResource((EObject) value, alreadyVisited, rootList, refOption);
            }
        }
    }

    /**
     * Update member contents with resolved data
     * 
     * @param node
     * @param memberReference
     * @param memberDocument
     * @return
     * @throws Exception
     */
    private static Element updateMember(Element node, EReference memberReference, int id, EObject memberObject,
            Document memberDocument) throws Exception {
        Namespace namespace = node.getNamespace(modelMetaData.getNamespace(memberReference));

        List<Element> children = null;
        if (namespace == null) {
            children = node.getChildren(modelMetaData.getName(memberReference));
        } else
            throw new IllegalArgumentException();
        if (id < children.size()) {
            // Inserting resolved contents to the member
            Element element = children.get(id);
            Element clone = memberDocument.getRootElement().clone();
            clone.setName(memberReference.getName());
            node.setContent(node.indexOf(element), clone);
            // Setting xsi:type
            clone.setAttribute("type",
                    memberObject.eClass().getEPackage().getName() + ":" + memberObject.eClass().getName(),
                    node.getNamespace("xsi"));
        }

        return node;
    }
}