/*
 * $RCSfile: Emf2XmiConversion.java,v $
 * $Date: 2006/01/26 14:48:59 $
 * $Revision: 1.1.2.4 $
 * $Author: andreys $
 */

/*
 * Copyright (c) 2002-2003 IST-2004-2006-511731 ModelWare - ModelBus.
 * All rights reserved.
 *
 * This software is published under the terms of the ModelBus Software License
 * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * A copy of ModelBus Software License is provided with this distribution in
 * doc/LICENSE.txt file.
 */

/*
 * Emf2XmiConversion This class is able to convert a collection of EObjects to a String. This
 * string contains the model in XMI2.0 format.
 * 
 * @author Prawee Sriplakich (LIP6)

 * @version $Revision: 1.1.2.4 $ $Date: 2006/01/26 14:48:59 $
 */
package org.eclipse.mddi.modelbus.adapter.infrastructure.serialize.emf_xmi;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.mddi.modelbus.adapter.infrastructure.model_manipulation.ModelBusResourceSet;
import org.eclipse.mddi.modelbus.adapter.infrastructure.model_manipulation.ModelUtil;

public class Emf2XmiConversion {


    static {
        ModelBusResourceSet.init();
    }

    /**
     * Serializes the elements in the collection to a String. The elements that
     * are in the same "Resources" as the elements in the collection will be
     * also serialized. <br>
     * If there are cross-referenced elements but not in collection, they will
     * be skipped. <br>
     * If the resources identified as global resource (see
     * ModelBusResourceSet.GlobalResourceRegistry) will be skipped.
     * 
     * @param emfobjs
     * @return
     * @throws IOException
     * 
     * 
     */
    public static String convertToString(Collection emfobjs) throws IOException {
        return convertToString(emfobjs, true, true);
    }

    /**
     * Serializes the elements in the collection to a String. The elements that
     * are in the same "Resources" as the elements in the collection will be
     * also serialized <br>
     * skipCrossReferences: If there are cross-referenced elements but not in
     * collection, they will be skipped. <br>
     * skipGlobalResources: The resources identified as global resource (see
     * ModelBusResourceSet.GlobalResourceRegistry) will be skipped.
     * 
     * @param emfobjs
     * @return
     * @throws IOException
     * 
     */
    public static String convertToString(Collection emfobjs,
            boolean skipCrossReferences, boolean skipGlobalResources)
            throws IOException {

        Set resources = getResourcesFromObjects(emfobjs, skipGlobalResources);

        if (!skipCrossReferences) {
            // search for cross references
            addCrossReferencedResources(resources, skipGlobalResources);
        }

        if (resources.size() == 1) {
            Resource r = (Resource) resources.iterator().next();
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            r.save(bout, null);
            String s = bout.toString();
            return s;
        } else {
            throw new IOException(
                    "We will support multiple documents transmission soon");
            // To transmit multiple documents, we should transmit the URI of
            // each document as well.
            // The objective is to construct the same set of documents at the
            // receiver side.
            // proposed format :
            // URI1 + file content + URI2 + file content + ...

        }
    }

    /**
     * Add resources that are crossed-referenced by the resources to the
     * specified set. <br>
     * skipGlobalResources: The crossed-referenced resources identified as
     * global resource (see ModelBusResourceSet.GlobalResourceRegistry) will be
     * skipped.
     * 
     * @param resources
     * @param skipGlobalResources
     * 
     * 
     */
    public static void addCrossReferencedResources(Set resources,
            boolean skipGlobalResources) {
        // the queue contains model elements to be examined.
        // add all model elements to the queue.
        List queue = new Vector();
        for (Iterator it = resources.iterator(); it.hasNext();) {
            Resource r = (Resource) it.next();
            for (Iterator it2 = r.getAllContents(); it2.hasNext();) {
                EObject containedObject = (EObject) it2.next();
                queue.add(containedObject);
            }
        }
        // examine elements in the queue
        while (!queue.isEmpty()) {
            EObject o = (EObject) queue.remove(0);
            for (Iterator it = o.eCrossReferences().iterator(); it.hasNext();) {
                EObject referencedObject = (EObject) it.next();
                Resource cr = referencedObject.eResource();
                if (!resources.contains(cr) && cr!=null) {
 
                    if (!skipGlobalResources
                            || !ModelBusResourceSet.hasGlobalURIScheme(cr
                                    .getURI())) {
                        resources.add(cr);
                        // we need to examine the cross-ref resource from this
                        // resouce too.
                        // Therefore, we add all elements in this resource in
                        // the queue.
                        for (Iterator it2 = cr.getAllContents(); it2.hasNext();) {
                            queue.add(it2.next());
                        }
                    }
                }
            } // end for
        }
    }

    /**
     * 
     * Obtain a set of resources that contained the specified objects. For
     * objects has no resource, a default resource is created for containing
     * them.
     * 
     * For option "skipGlobalResource", the resources that are contained in the
     * GlobalResourceRegistry will be excluded from the returned set.
     * 
     * @param emfobjs
     * @param skipGlobalResources
     * @return a set of container resources
     * 
     * 
     */
    public static Set getResourcesFromObjects(Collection emfobjs,
            boolean skipGlobalResources) {
        Set resources = new HashSet();

        Collection elemsWithNoResource = new HashSet();
        for (Iterator it = emfobjs.iterator(); it.hasNext();) {
            EObject o = (EObject) it.next();
            Resource r = o.eResource();
            if (r == null) {
                // add this object to resource;
                EObject top = ModelUtil.getTopElement(o);
                elemsWithNoResource.add(top);
            } else {
                if (!skipGlobalResources
                        || !ModelBusResourceSet.hasGlobalURIScheme(r.getURI())) {
                    resources.add(r);
                }
            }
        }

        if (resources.size() == 0) {
            // no resource
            ResourceSet rSet = new ModelBusResourceSet();
            Resource defaultResource = rSet
                    .createResource(ModelBusResourceSet.MB_DEFAULT_RES_URI);
            resources.add(defaultResource);
        }

        manageOrphanElement(elemsWithNoResource, resources);
        return resources;
    }

    /**
     * Create a resource "ModelBus_default_resource" for container elements that
     * has no resource (orphan elements). Then add this resource to "resources".
     * If "resources" alreay has "ModelBus_default_resource", then use it for
     * storing orphan elements.
     */
    static void manageOrphanElement(Collection orphanTopElements, Set resources) {
        if (orphanTopElements.isEmpty()) {
            return;
        }
        Resource defaultResource = null;
        for (Iterator it = resources.iterator(); it.hasNext();) {
            Resource r = (Resource) it.next();
            if (ModelBusResourceSet.MB_DEFAULT_RES_URI.equals(r.getURI())) {
                defaultResource = r;
            }
        }
        if (defaultResource == null) {
            ResourceSet rSet = new ModelBusResourceSet();
            defaultResource = rSet
                    .createResource(ModelBusResourceSet.MB_DEFAULT_RES_URI);
        }
        defaultResource.getContents().addAll(orphanTopElements);
    }

}
