/*******************************************************************************
* Copyright (c) 2010-2015 Henshin developers. 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:
* TU Berlin, University of Luxembourg, SES S.A.
*******************************************************************************/
package de.tub.tfs.muvitor.ui.utils;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.EPackageRegistryImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.BasicExtendedMetaData;
import org.eclipse.emf.ecore.util.ExtendedMetaData;
import org.eclipse.emf.ecore.xmi.XMLHelper;
import org.eclipse.emf.ecore.xmi.XMLLoad;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.XMLSave;
import org.eclipse.emf.ecore.xmi.impl.SAXXMIHandler;
import org.eclipse.emf.ecore.xmi.impl.XMIHelperImpl;
import org.eclipse.emf.ecore.xmi.impl.XMILoadImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
import org.eclipse.emf.ecore.xmi.impl.XMISaveImpl;
import org.eclipse.swt.widgets.Display;
import org.xml.sax.helpers.DefaultHandler;
import de.tub.tfs.muvitor.ui.MuvitorActivator;
/**
* Manager for persistence operations on an EMF model.
*
* @author Tony Modica
*/
public class EMFModelManager {
static HashMap<EPackage,HashMap<String, EClassifier>> conversionsClass = new HashMap<EPackage, HashMap<String,EClassifier>>();
static HashSet<EClassifier> replacedClasses = new HashSet<EClassifier>();
static HashMap<EClassifier,String> replacedClassesToStringMap = new HashMap();
IProgressMonitor monitor = null;
int lastLine = 0;
static HashMap<EClassifier,SaveDelegate> saveDelegates = new HashMap<EClassifier, SaveDelegate>();
static HashMap<EClassifier,LoadDelegate> loadDelegates = new HashMap<EClassifier, LoadDelegate>();
/**
* The ResourceSet
*/
static ResourceSet resourceSet = new MuvitorResourceSet();
private Map<String, Object> options = new HashMap<String, Object>();
/**
* The top level models in the resource.
*/
private List<EObject> models = null;
/**
* In EMF, a Resource provides the way to have access to the model content.
*/
private Resource resource = null;
public static boolean hasClassConversion(EPackage sourceUri,String sourceClass,EClassifier targetClass){
HashMap<String, EClassifier> hashMap = conversionsClass.get(sourceUri);
if (hashMap == null)
return false;
if (!saveDelegates.containsKey(targetClass)){
return false;
}
if (!loadDelegates.containsKey(targetClass)){
return false;
}
if (!replacedClasses.contains(targetClass)){
return false;
}
if (!replacedClassesToStringMap.containsKey(targetClass)){
return false;
}
return true;
}
public static boolean registerClassConversion(EPackage sourceUri,
EClass sourceClass, EClass targetClass) {
return registerClassConversion(sourceUri, sourceClass, targetClass,
new SaveDelegateOneClass(sourceClass,targetClass),
new LoadDelegateOneClass());
}
public static boolean registerClassConversion(EPackage sourceUri,
EClass sourceClass, EClass targetClass,SaveDelegate saveDelegate) {
return registerClassConversion(sourceUri, sourceClass, targetClass,
saveDelegate,
new LoadDelegateOneClass());
}
public static boolean registerClassConversion(EPackage sourceUri,
EClass sourceClass, EClass targetClass,LoadDelegate loadDelegate) {
return registerClassConversion(sourceUri, sourceClass, targetClass,
new SaveDelegateOneClass(sourceClass,targetClass),
loadDelegate);
}
public static boolean registerClassConversion(EPackage sourceUri,EClass sourceClass,EClassifier targetClass,SaveDelegate delegate,LoadDelegate load){
if (!(sourceUri.getEFactoryInstance() instanceof DelegatingEFactory)){
sourceUri.setEFactoryInstance(new DelegatingEFactory(sourceUri.getEFactoryInstance(),sourceUri));
}
if (sourceClass == null){
saveDelegates.put(targetClass, delegate);
loadDelegates.put(targetClass, load);
replacedClasses.add(targetClass);
replacedClassesToStringMap.put(targetClass, targetClass.getName());
return true;
} else {
HashMap<String, EClassifier> hashMap = conversionsClass.get(sourceUri);
if (hashMap == null)
conversionsClass.put(sourceUri, hashMap = new HashMap<String, EClassifier>());
hashMap.put(sourceClass.getName(), targetClass);
saveDelegates.put(targetClass, delegate);
loadDelegates.put(targetClass, load);
replacedClasses.add(targetClass);
replacedClassesToStringMap.put(targetClass, sourceClass.getName());
return true;
}
}
protected static LinkedList<File> findEcoreFiles(File[] listFiles) {
LinkedList<File> result = new LinkedList<File>();
if (listFiles == null)
return result;
for (File file : listFiles) {
if (file.isDirectory()) {
if (file.getName().equals(".svn")){
continue;
}
if (file.getName().equals(".cvs")){
continue;
}
if (file.getName().equals(".git")){
continue;
}
LinkedList<File> f = findEcoreFiles(file.listFiles());
result.addAll(f);
} else {
if (file.getName().endsWith("ecore"))
result.add(file);
}
}
return result;
}
/**
* FIXED: This ensures compatibility if models are changed from using
* {@link ENamedElement}s to using a custom NamedElement with default name
* "" or vice versa. All names of {@link ENamedElement} that are
* <code>null</code> are set to " " when saving the file.
*
* FIXED: Additionally, we do not allow empty names "", because these
* models' URIs can not be used to resolve the models again. This only
* affects old models, MuvitorTreeEditor cares about this before saving as
* well.
*/
private static void recursiveSetNamesIfUnset(final List<EObject> models) {
if (models == null)
return;
for (final EObject model : models) {
if (model instanceof ENamedElement) {
final ENamedElement namedElement = (ENamedElement) model;
final String name = namedElement.getName();
if (name == null || name.equals("")) {
namedElement.setName(" ");
}
recursiveSetNamesIfUnset(model.eContents());
}
recursiveSetNamesIfUnset(model.eContents());
}
}
/**
* This constructor initializes the EMF model package and registers a file
* extension.
*
* @param extension
* The file extension
*/
public FragmentResource requestFragmentResource(Resource r){
try {
return (FragmentResource) resourceSet.getResource(r.getURI().appendFileExtension("fragment"),true);
} catch (Exception ex){
return (FragmentResource) resourceSet.getResource(r.getURI().appendFileExtension("fragment"),true);
}
}
public FragmentResource getFragmentResource(Resource r){
return (FragmentResource) resourceSet.getResource(r.getURI().appendFileExtension("fragment"), false);
}
private static HashMap<String, EMFModelManager> modelmanager = new HashMap<String,EMFModelManager>();
public static EMFModelManager createModelManager(String extension){
EMFModelManager m = modelmanager.get(extension);
if (m == null) {
m = new EMFModelManager(extension);
modelmanager.put(extension, m);
}
return m;
}
private EMFModelManager(final String extension) {
options.put(XMLResource.OPTION_DECLARE_XML, Boolean.TRUE);
options.put(XMLResource.OPTION_KEEP_DEFAULT_CONTENT, Boolean.TRUE);
options.put(XMLResource.OPTION_EXTENDED_META_DATA, new BasicExtendedMetaData(){
@Override
public EClassifier getType(EPackage ePackage, String name) {
HashMap<String, EClassifier> map = conversionsClass.get(ePackage);
if (map != null){
EClassifier cl = map.get(name);
if (cl == null)
return super.getType(ePackage, name);
return cl;
}
return super.getType(ePackage, name);
}
@Override
public EClassifier getType(String namespace, String name) {
// TODO Auto-generated method stub
return super.getType(namespace, name);
}
});
final Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
reg.getExtensionToFactoryMap().put("fragment" ,
new XMIResourceFactoryImpl() {
@Override
public Resource createResource(final URI uri) {
return new FragmentResource(uri,EMFModelManager.this);
}
}
);
/*
* Register the XMI resource factory for the editor's file extension. We
* use a subclass that creates a special XMIResource that makes use of
* UUID to support EMF Clipboard as described in GMF's
* "Tutorial: EMF Clipboard Copy and Paste"
*/
reg.getExtensionToFactoryMap().put(extension,
new XMIResourceFactoryImpl() {
@Override
public Resource createResource(final URI uri) {
return new EMFResource(EMFModelManager.this, uri);
}
});
}
/**
* Loads the model from the file. It this fails then the passed list of
* default models will be set in the resource for saving later with
* {@link #save(IPath)}. Return the loaded or the default models,
* respectively.
*
* @param path
* A {@link IPath} to a file containing a {@link Resource}.
* @param defaultModels
* a list of default models to use when loading fails
* @return the loaded models or the default models
*/
public List<EObject> load(final IPath path,
final List<EObject> defaultModels) {
try {
resourceSet.getLoadOptions().putAll(options);
resource = resourceSet.getResource(
URI.createPlatformResourceURI(path.toString(), true), false);
if (resource != null)
resource.unload();
resource = resourceSet.getResource(
URI.createPlatformResourceURI(path.toString(), true), true);
/*
* FIX: without calling unload() reverting does not work correctly
*/
//resource.unload();
//resource.load(options);
/*
* copy the contents because the resource will be emptied on
* "save as"
*/
models = new BasicEList<EObject>(resource.getContents());
} catch (final Exception e) {
// e.printStackTrace();
// something failed - maybe file does not exist, so try again without loading the model and use
// the defaultModel instead
if (resource == null) {
// create a resource if getting one has failed before
resource = resourceSet.createResource(URI.createPlatformResourceURI(
path.toString(), true));
}
if (resource == null) {
MuvitorActivator.logError(
"Unerwarteter Fehler in EMFModelmanager: Keine Resource erzeugbar.",
e);
throw new NullPointerException();
}
resource.getContents().clear();
resource.getContents().addAll(defaultModels);
models = defaultModels;
}
recursiveSetNamesIfUnset(models);
return models;
}
/**
* Loads the model from the file. It this fails then the passed list of
* default models will be set in the resource for saving later with
* {@link #save(IPath)}. Return the loaded or the default models,
* respectively.
*
* @param path
* A {@link IPath} to a file containing a {@link Resource}.
* @param defaultModels
* a list of default models to use when loading fails
* @return the loaded models or the default models
*/
public List<EObject> load(final String path,
final List<EObject> defaultModels) {
try {
resourceSet.getLoadOptions().putAll(options);
resource = resourceSet.getResource(
URI.createURI(path.toString(), true), false);
if (resource != null)
resource.unload();
resource = resourceSet.getResource(
URI.createURI(path.toString(), true), true);
/*
* FIX: without calling unload() reverting does not work correctly
*/
//resource.unload();
//resource.load(options);
/*
* copy the contents because the resource will be emptied on
* "save as"
*/
models = new BasicEList<EObject>(resource.getContents());
} catch (final Exception e) {
e.printStackTrace();
// something failed, so try again without loading the model and use
// the defaultModel instead
if (resource == null) {
// create a resource if getting one has failed before
resource = resourceSet.createResource(URI.createURI(
path.toString(), true));
}
if (resource == null) {
MuvitorActivator.logError(
"Unerwarteter Fehler in EMFModelmanager: Keine Resource erzeugbar.",
e);
throw new NullPointerException();
}
resource.getContents().clear();
resource.getContents().addAll(defaultModels);
models = defaultModels;
}
recursiveSetNamesIfUnset(models);
return models;
}
public void save(final IPath path, boolean isPlatform,EObject... rootObjects) throws IOException {
// This sets the model as contents in a new resource when using save as.
URI uri = isPlatform ? URI.createPlatformResourceURI(path.toString(),
true) : URI.createFileURI(path.toString());
try {
resource = resourceSet.getResource(uri, true);
} catch (final Exception e) {
resource = resourceSet.createResource(uri);
}
options.put(XMLResource.OPTION_DECLARE_XML, Boolean.TRUE);
options.put(XMLResource.OPTION_KEEP_DEFAULT_CONTENT, Boolean.TRUE);
options.put(XMLResource.OPTION_ENCODING, "UTF-8");
recursiveSetNamesIfUnset(models);
resource.getContents().clear();
if (rootObjects.length > 0)
resource.getContents().addAll(Arrays.asList(rootObjects));
else
resource.getContents().addAll(models);
resource.save(options);
}
/**
* Saves the content of the model to the file.
*/
public void save(final IPath path,EObject... rootObjects) throws IOException {
save(path, true,rootObjects);
}
public void setMonitor(IProgressMonitor mon){
this.monitor = mon;
this.lastLine = 0;
}
public void cleanUp() {
conversionsClass = new HashMap<EPackage, HashMap<String,EClassifier>>();
replacedClasses = new HashSet<EClassifier>();
replacedClassesToStringMap = new HashMap<EClassifier, String>();
monitor = null;
lastLine = 0;
saveDelegates = new HashMap<EClassifier,SaveDelegate>();
loadDelegates = new HashMap<EClassifier,LoadDelegate>();
resourceSet = new MuvitorResourceSet();
options = new HashMap<String, Object>();
/**
* The top level models in the resource.
*/
models = null;
/**
* In EMF, a Resource provides the way to have access to the model content.
*/
resource = null;
}
}