/**
* Copyright (c) 2006-2012 IBM Corporation and others
* 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:
* IBM - Initial API and implementation
*/
package org.eclipse.emf.ecore.xcore.exporter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.codegen.ecore.genmodel.GenBase;
import org.eclipse.emf.codegen.ecore.genmodel.GenClassifier;
import org.eclipse.emf.codegen.ecore.genmodel.GenFeature;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
import org.eclipse.emf.codegen.ecore.genmodel.GenModelFactory;
import org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage;
import org.eclipse.emf.codegen.ecore.genmodel.GenPackage;
import org.eclipse.emf.codegen.util.ImportManager;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EGenericType;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.plugin.EcorePlugin;
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.xcore.XGenericType;
import org.eclipse.emf.ecore.xcore.XImportDirective;
import org.eclipse.emf.ecore.xcore.XPackage;
import org.eclipse.emf.ecore.xcore.XReference;
import org.eclipse.emf.ecore.xcore.XcoreFactory;
import org.eclipse.emf.ecore.xcore.XcorePackage;
import org.eclipse.emf.ecore.xcore.mappings.XcoreMapper;
import org.eclipse.emf.ecore.xcore.scoping.XcoreImportedNamespaceAwareScopeProvider;
import org.eclipse.emf.ecore.xcore.util.EcoreXcoreBuilder;
import org.eclipse.emf.ecore.xcore.util.XcoreGenModelBuilder;
import org.eclipse.emf.ecore.xcore.util.XcoreGenModelInitializer;
import org.eclipse.emf.exporter.ModelExporter;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.SaveOptions;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.IScopeProvider;
import org.eclipse.xtext.ui.XtextProjectHelper;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class XcoreExporter extends ModelExporter
{
@Inject
Provider<EcoreXcoreBuilder> ecoreXcoreBuilderProvider;
@Inject
XcoreGenModelBuilder genModelBuilder;
@Inject
XcoreGenModelInitializer genModelInitializer;
@Inject
XcoreMapper mapper;
@Inject
private IScopeProvider scopeProvider;
@Inject
private IQualifiedNameProvider qualifiedNameProvider;
@Inject
private IQualifiedNameConverter qualifiedNameConverter;
private static final Set<String> IMPLICIT_ALIASES = Sets.newHashSet();
static
{
for (EDataType eDataType : XcoreImportedNamespaceAwareScopeProvider.IMPLICIT_ALIASES)
{
IMPLICIT_ALIASES.add("org.eclipse.emf.ecore." + eDataType.getName());
}
}
@Override
public String getID()
{
return "org.eclipse.emf.ecore.xcore.exporter";
}
@Override
public void dispose()
{
super.dispose();
}
@Override
protected String getDefaultArtifactLocation(EPackage ePackage)
{
return getEPackageToGenPackageMap().get(ePackage).getPrefix() + ".xcore";
}
@Override
protected String doCheckEPackageArtifactLocation(String location, String packageName)
{
if (!location.endsWith(".xcore"))
{
return XcoreExporterPlugin.INSTANCE.getString("_UI_InvalidArtifactFileNameExtension_message");
}
return super.doCheckEPackageArtifactLocation(location, packageName);
}
@Override
protected Diagnostic doExport(Monitor monitor, ModelExporter.ExportData exportData) throws Exception
{
for (Map.Entry<GenPackage, URI> entry : exportData.genPackageToArtifactURI.entrySet())
{
GenPackage genPackage = entry.getKey();
URI xcoreLocationURI = entry.getValue();
// Add the Xtext nature if it's absent.
//
IFile xcoreFile = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(xcoreLocationURI.toPlatformString(true)));
final IProject xcoreProject = xcoreFile.getProject();
if (xcoreProject.isAccessible())
{
if (!xcoreProject.hasNature(XtextProjectHelper.NATURE_ID))
{
IProjectDescription description = xcoreProject.getDescription();
String[] natures = description.getNatureIds();
String[] newNatures = new String[natures.length + 1];
System.arraycopy(natures, 0, newNatures, 0, natures.length);
newNatures[natures.length] = XtextProjectHelper.NATURE_ID;
description.setNatureIds(newNatures);
xcoreProject.setDescription(description, null);
}
}
// Create an appropriate resource set for Xcore models.
//
ResourceSet resourceSet = new ResourceSetImpl();
resourceSet.getURIConverter().getURIMap().putAll(EcorePlugin.computePlatformURIMap());
// Load a clone of the GenModel in the new resource set.
//
GenModel inputGenModel = (GenModel)resourceSet.getEObject(EcoreUtil.getURI(getGenModel()), true);
inputGenModel.reconcile();
Resource inputResource = inputGenModel.eResource();
Resource outputResource = resourceSet.createResource(xcoreLocationURI);
// Create a fresh newly GenModel for the packages we just loaded.
//
GenModel genModel = GenModelFactory.eINSTANCE.createGenModel();
List<EPackage> ePackageClones = new ArrayList<EPackage>();
for (GenPackage inputGenPackage : inputGenModel.getGenPackages())
{
ePackageClones.add(inputGenPackage.getEcorePackage());
}
genModel.initialize(ePackageClones);
// Add it to the resource and initialize it like we do any other new GenModel
//
inputResource.getContents().add(genModel);
genModelInitializer.initialize(genModel, true);
// Walk the two GenModels in lockstep and compare settings.
// We need to know which ones should be converted to annotations and which would be redundant because they represent defaults.
//
final GenPackage inputGenPackage = (GenPackage)resourceSet.getEObject(EcoreUtil.getURI(genPackage), true);
new Object()
{
void visit(GenBase genBase1, GenBase genBase2)
{
if (genBase1.eClass() == genBase2.eClass())
{
// TODO handle multi-valued attribues and references.
//
for (EAttribute eAttribute : genBase1.eClass().getEAllAttributes())
{
if (!eAttribute.isMany() && genBase1.eIsSet(eAttribute))
{
Object value1 = genBase1.eGet(eAttribute);
Object value2 = genBase2.eGet(eAttribute);
if (value1 == null ? value2 != null : !value1.equals(value2))
{
// For the case of the GenModel, we record its annotations on the package.
//
EModelElement eModelElement = genBase2.getEcoreModelElement();
if (eModelElement == null && genBase2 instanceof GenModel)
{
eModelElement = inputGenPackage.getEcorePackage();
}
EcoreUtil.setAnnotation(eModelElement, GenModelPackage.eNS_URI, eAttribute.getName(), EcoreUtil.convertToString(eAttribute.getEAttributeType(), value1));
}
for (Iterator<EObject> i = genBase1.eContents().iterator(), j = genBase2.eContents().iterator(); i.hasNext() && j.hasNext();)
{
EObject content1 = i.next();
EObject content2 = j.next();
if (content1 instanceof GenBase && content2 instanceof GenBase)
{
visit((GenBase)content1, (GenBase)content2);
}
}
}
}
}
}
}.visit(inputGenModel, genModel);
// Use the builder to create an appropriate Xcore instance for populating the output resource.
//
EcoreXcoreBuilder ecoreXcoreBuilder = ecoreXcoreBuilderProvider.get();
ecoreXcoreBuilder.initialize(inputGenModel);
XPackage xPackage = ecoreXcoreBuilder.getXPackage(inputGenPackage.getEcorePackage());
outputResource.getContents().add(xPackage);
outputResource.getContents().add(inputGenModel);
outputResource.getContents().add(inputGenPackage.getEcorePackage());
// Put all the specialize internal GenPackages in appropriately named resources so the serialize can resolve them.
//
GenPackage ecoreGenPackage = inputGenModel.getEcoreGenPackage();
if (ecoreGenPackage != null)
{
Resource ecoreResource = resourceSet.createResource(URI.createURI("platform:/plugin/org.eclipse.emf.ecore/model/Ecore.genmodel"));
ecoreResource.getContents().add(ecoreGenPackage.getGenModel());
}
GenPackage xmlTypeGenPackage = inputGenModel.getXMLTypeGenPackage();
if (xmlTypeGenPackage != null)
{
Resource xmlTypeResource = resourceSet.createResource(URI.createURI("platform:/plugin/org.eclipse.emf.ecore/model/XMLType.genmodel"));
xmlTypeResource.getContents().add(xmlTypeGenPackage.getGenModel());
}
GenPackage xmlNamespaceGenPackage = inputGenModel.getXMLNamespaceGenPackage();
if (xmlNamespaceGenPackage != null)
{
Resource xmlNamespaceResource = resourceSet.createResource(URI.createURI("platform:/plugin/org.eclipse.emf.ecore/model/XMLNamespace.genmodel"));
xmlNamespaceResource.getContents().add(xmlNamespaceGenPackage.getGenModel());
}
// Do the final linking step and build the map.
//
ecoreXcoreBuilder.link();
genModelBuilder.buildMap(inputGenModel);
// Use an import manager to create imports for all the things we reference in the Xcore instance.
//
ImportManager importManager =
new ImportManager(inputGenPackage.getInterfacePackageName())
{
@Override
protected boolean shouldImport(String packageName, String shortName, String importName)
{
return true;
}
};
Map<EGenericType, XGenericType> genericTypeMap = ecoreXcoreBuilder.getGenericTypeMap();
for (TreeIterator<EObject> i = inputGenPackage.getEcorePackage().eAllContents(); i.hasNext();)
{
EObject eObject = i.next();
if (eObject instanceof EGenericType)
{
EGenericType eGenericType = (EGenericType)eObject;
EClassifier eClassifier = eGenericType.getEClassifier();
if (eClassifier != null)
{
GenClassifier genClassifier = inputGenModel.findGenClassifier(eClassifier);
QualifiedName qualifiedName = qualifiedNameProvider.getFullyQualifiedName(genClassifier);
String qualifiedNameValue = qualifiedNameConverter.toString(qualifiedName);
if (!IMPLICIT_ALIASES.contains(qualifiedNameValue))
{
importManager.addImport(qualifiedNameValue);
}
// We need to ensure that if we resolve to a different instance, we switch to use that so that serialization will find the right instance.
//
XGenericType xGenericType = genericTypeMap.get(eGenericType);
IScope scope = scopeProvider.getScope(xGenericType, XcorePackage.Literals.XGENERIC_TYPE__TYPE);
IEObjectDescription genClassifierDescription = scope.getSingleElement(qualifiedName);
if (genClassifierDescription != null)
{
EObject resolvedGenClassifier = resourceSet.getEObject(genClassifierDescription.getEObjectURI(), true);
if (resolvedGenClassifier != null && resolvedGenClassifier != genClassifier)
{
xGenericType.setType((GenClassifier)resolvedGenClassifier);
}
}
}
}
else if (eObject instanceof EReference)
{
EReference eReference = (EReference)eObject;
XReference xReference = (XReference)mapper.getToXcoreMapping(eReference).getXcoreElement();
EList<GenFeature> keys = xReference.getKeys();
GenFeature opposite = xReference.getOpposite();
if (opposite != null)
{
IScope scope = scopeProvider.getScope(xReference, XcorePackage.Literals.XREFERENCE__OPPOSITE);
IEObjectDescription genFeatureDescription = scope.getSingleElement(QualifiedName.create(opposite.getName()));
if (genFeatureDescription != null)
{
EObject resolvedGenFeature = resourceSet.getEObject(genFeatureDescription.getEObjectURI(), true);
if (resolvedGenFeature != null)
{
xReference.setOpposite((GenFeature)resolvedGenFeature);
}
}
}
if (!keys.isEmpty())
{
IScope scope = scopeProvider.getScope(xReference, XcorePackage.Literals.XREFERENCE__KEYS);
for (ListIterator<GenFeature> k = keys.listIterator(); k.hasNext(); )
{
GenFeature key = k.next();
IEObjectDescription genFeatureDescription = scope.getSingleElement(QualifiedName.create(key.getName()));
if (genFeatureDescription != null)
{
EObject resolvedGenFeature = resourceSet.getEObject(genFeatureDescription.getEObjectURI(), true);
if (resolvedGenFeature != null)
{
k.set((GenFeature)resolvedGenFeature);
}
}
}
}
}
else if (eObject instanceof EPackage)
{
i.prune();
}
}
// Convert the needed imports to import directives.
//
for (String qualifiedName : importManager.getImports())
{
XImportDirective xImportDirective = XcoreFactory.eINSTANCE.createXImportDirective();
xImportDirective.setImportedNamespace(qualifiedName);
xPackage.getImportDirectives().add(xImportDirective);
}
// Save the final result.
//
Map<Object, Object> options = new HashMap<Object, Object>();
SaveOptions.newBuilder().format().noValidation().getOptions().addTo(options);
outputResource.save(options);
}
return Diagnostic.OK_INSTANCE;
}
protected URI getReferencedGenPackageArtifactURI(ModelExporter.ExportData exportData, GenPackage genPackage)
{
URI artifactURI = exportData.referencedGenPackagesToArtifactURI.get(genPackage);
if (artifactURI == null)
{
artifactURI = exportData.genPackageToArtifactURI.get(genPackage);
if (artifactURI == null)
{
for (Map.Entry<GenPackage, URI> entry : exportData.referencedGenPackagesToArtifactURI.entrySet())
{
GenPackage referencedGenPackage = entry.getKey();
if (genPackage.getNSURI().equals(referencedGenPackage.getNSURI()) &&
genPackage.getEcorePackage().getName().equals(referencedGenPackage.getEcorePackage().getName()))
{
artifactURI = entry.getValue();
}
}
}
}
return artifactURI;
}
@Override
protected void adjustGenModel()
{
// Ignore all packages not actually contained in the GenModel.
// We don't need information about where referenced GenPackages are located.
//
GenModel genModel = getGenModel();
for (ListIterator<EPackage> i = getEPackages().listIterator(); i.hasNext();)
{
if (!EcoreUtil.isAncestor(genModel, getEPackageToGenPackageMap().get(i.next())))
{
i.remove();
}
}
super.adjustGenModel();
}
}