EmfHelper.java

package de.uka.ipd.sdq.beagle.core.pcmconnection;

import de.uka.ipd.sdq.identifier.Identifier;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
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.EcoreUtil;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.palladiosimulator.pcm.allocation.AllocationPackage;
import org.palladiosimulator.pcm.parameter.ParameterPackage;
import org.palladiosimulator.pcm.repository.RepositoryPackage;
import org.palladiosimulator.pcm.resourceenvironment.ResourceenvironmentPackage;
import org.palladiosimulator.pcm.resourcetype.ResourcetypePackage;
import org.palladiosimulator.pcm.seff.SeffPackage;
import org.palladiosimulator.pcm.system.SystemPackage;
import org.palladiosimulator.pcm.usagemodel.UsagemodelPackage;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;

/**
 * Helper class provided by the SDQ for working with PCM repository files. Also see
 * {@link EcoreUtil} for more helper functions like
 * {@link EcoreUtil#equals(EObject, EObject)} to test for equality.
 *
 * @author Anne Koziolek
 *
 */
public final class EmfHelper {

	/**
	 * Filename length for which storing is tried again. If a filename has at least this
	 * length when trying to store in {@link #saveToXMIFile(EObject, String)} and the
	 * storing fails, we’ll try again once more.
	 */
	private static final int RETRY_FILENAME_LENGTH = 250;

	/**
	 * Private constructor because this is an utility class.
	 */
	private EmfHelper() {
	}

	/**
	 * Checks for two PCM model elements whether they are the same, i.e. whether they have
	 * the same ID. The model elements have to be derived from Identifier. Note that two
	 * systems might use the same assembly contexts and components, but still are two
	 * different systems. If one of the Identifiers in null, false is returned.
	 *
	 * @param identifier1 One Identifier
	 * @param identifier2 Another Identifier
	 * @return true if i1.getId().equals(i2.getId()), false otherwise
	 */
	public static boolean checkIdentity(final EObject identifier1, final EObject identifier2) {
		if (identifier1 == null || identifier2 == null) {
			return false;
		}
		if (identifier1 instanceof Identifier && identifier2 instanceof Identifier) {
			return ((Identifier) identifier1).getId().equals(((Identifier) identifier2).getId());
		} else {
			return EcoreUtil.equals(identifier1, identifier2);
		}
	}

	/**
	 * Implements an identifier-based contains. Calls
	 * {@link #checkIdentity(EObject, EObject)} to compare the {@link Identifier} i with
	 * the contents of the collection.
	 *
	 * @param coll A collection of objects.
	 * @param identity An identity to search for.
	 * @return true if there is an {@link Identifier} in {@code coll} with an id equal to
	 *         i.getID().
	 */
	public static boolean contains(final Collection<? extends EObject> coll, final EObject identity) {
		for (final EObject identifier : coll) {
			if (checkIdentity(identifier, identity)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * {@link Collection#retainAll(Collection)} for {@link Identifier Identifiers} based
	 * on {@link #checkIdentity(EObject, EObject)}. This method will leave all identifiers
	 * in {@code collection} that describe an object is {@code itemsToRetain}.
	 *
	 * @param collection A collection of identifiers.
	 * @param itemsToRetain The items whose identifiers should be left in
	 *            {@code collection}.
	 * @return {@code true} only if an element was removed from {@code collection}.
	 */
	public static boolean retainAll(final Collection<? extends Identifier> collection,
		final Collection<? extends EObject> itemsToRetain) {
		boolean removedAny = false;
		for (final Iterator<? extends Identifier> iterator = collection.iterator(); iterator.hasNext();) {
			final Identifier identifier = iterator.next();
			boolean identifierContainedInItemsToRetain = false;
			for (final EObject identifierToRetain : itemsToRetain) {
				identifierContainedInItemsToRetain |= checkIdentity(identifier, identifierToRetain);
			}
			if (!identifierContainedInItemsToRetain) {
				iterator.remove();
				removedAny = true;
			}
		}
		return removedAny;
	}

	/**
	 * Save the given EObject to the file given by filename.
	 *
	 * @param modelToSave The EObject to save
	 * @param fileName The filename where to save.
	 * @throws IOException If accessing the file fails.
	 */
	public static void saveToXMIFile(final EObject modelToSave, final String fileName) throws IOException {
		saveToXMIFile(modelToSave, fileName, true);
	}

	/**
	 * Additional parameter mayRetry to detect to deep recursion.
	 *
	 * @param modelToSave The EObject to save
	 * @param fileName The filename where to save.
	 * @param mayRetry {@code true} saving shall be tried once again if a
	 *            {@link FileNotFoundException} is thrown and {@code fileName} is longer
	 *            as {@link #RETRY_FILENAME_LENGTH}.
	 * @throws IOException If accessing the file fails.
	 */
	private static void saveToXMIFile(final EObject modelToSave, final String fileName, final boolean mayRetry)
		throws IOException {
		// final Logger logger = Logger.getLogger("de.uka.ipd.sdq.dsexplore");

		// logger.debug("Saving " + modelToSave.toString() + " to " + fileName);

		// Create a resource set.
		final ResourceSet resourceSet = new ResourceSetImpl();

		// Register the default resource factory -- only needed for stand-alone!
		resourceSet.getResourceFactoryRegistry()
			.getExtensionToFactoryMap()
			.put(Resource.Factory.Registry.DEFAULT_EXTENSION, new XMIResourceFactoryImpl());

		final URI myURI = URI.createFileURI(fileName);

		final Resource resource = resourceSet.createResource(myURI);
		resource.getContents().add(modelToSave);

		try {
			resource.save(Collections.EMPTY_MAP);
		} catch (final FileNotFoundException fileNotFound) {
			if (mayRetry && fileName.length() > RETRY_FILENAME_LENGTH) {
				// try again with a shorter filename, but just one more try (mayRetry =
				// false).
				saveToXMIFile(modelToSave,
					fileName.substring(0, fileName.indexOf("-")) + "-shortened-" + fileName.hashCode(), false);
			} else {
				throw fileNotFound;
			}
		}
	}

	/**
	 * Loads the root object from an EMF File.
	 *
	 * @param fileName Name of the file to load from.
	 * @param ePackage The package to load for.
	 * @return The root element read.
	 */
	public static EObject loadFromXMIFile(final String fileName, final EPackage ePackage) {
		// Create a resource set to hold the resources.
		final ResourceSet resourceSet = new ResourceSetImpl();

		// Register the appropriate resource factory to handle all file
		// extensions.
		resourceSet.getResourceFactoryRegistry()
			.getExtensionToFactoryMap()
			.put(Resource.Factory.Registry.DEFAULT_EXTENSION, new XMIResourceFactoryImpl());

		// Register the package to ensure it is available during loading.
		registerPackages(resourceSet);

		return loadFromXMIFile(fileName, resourceSet, ePackage);
	}

	/**
	 * Loads the root object from an EMF File.
	 *
	 * @param fileName Name of the file to load from.
	 * @param resourceSet The resource set to load.
	 * @param ePackage The package to load for.
	 * @return The root element read.
	 */
	public static EObject loadFromXMIFile(final String fileName, final ResourceSet resourceSet,
		final EPackage ePackage) {
		// Construct the URI for the instance file.
		// The argument is treated as a file path only if it denotes an existing
		// file. Otherwise, it's directly treated as a URL.
		final File file = new File(fileName);
		final URI fileUri = file.isFile() ? URI.createFileURI(file.getAbsolutePath()) : URI.createURI(fileName);

		Resource resource = null;
		// Demand load resource for this file.
		resourceSet.getPackageRegistry().put(ePackage.getNsURI(), ePackage);
		resource = resourceSet.getResource(fileUri, true);

		final EObject eObject = resource.getContents().iterator().next();
		return EcoreUtil.getRootContainer(eObject);
	}

	/**
	 * Copied From de.uka.ipd.sdq.pcmsolver.models.PCMInstance.
	 *
	 * @param resourceSet The resource set to register all contained model packages with.
	 */
	private static void registerPackages(final ResourceSet resourceSet) {
		resourceSet.getPackageRegistry().put(AllocationPackage.eNS_URI, AllocationPackage.eINSTANCE);
		resourceSet.getPackageRegistry().put(ParameterPackage.eNS_URI, ParameterPackage.eINSTANCE);
		resourceSet.getPackageRegistry().put(ResourceenvironmentPackage.eNS_URI, ResourceenvironmentPackage.eINSTANCE);
		resourceSet.getPackageRegistry().put(ResourcetypePackage.eNS_URI, ResourcetypePackage.eINSTANCE);
		resourceSet.getPackageRegistry().put(RepositoryPackage.eNS_URI, RepositoryPackage.eINSTANCE);
		resourceSet.getPackageRegistry().put(SeffPackage.eNS_URI, SeffPackage.eINSTANCE);
		resourceSet.getPackageRegistry().put(SystemPackage.eNS_URI, SystemPackage.eINSTANCE);
		resourceSet.getPackageRegistry().put(UsagemodelPackage.eNS_URI, UsagemodelPackage.eINSTANCE);

	}
}