/**
* <copyright>
*
* Copyright (c) 2010 SAP AG.
* 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:
* Reiner Hille-Doering (SAP AG) - initial API and implementation and/or initial documentation
*
* </copyright>
*/
package org.eclipse.bpmn2.tools.ecoremerger;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import org.eclipse.bpmn2.tools.ecoremerger.exceptions.AttributeMappingExcpetion;
import org.eclipse.bpmn2.tools.ecoremerger.exceptions.ClassMappingException;
import org.eclipse.bpmn2.tools.ecoremerger.exceptions.ExceptionsPackage;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
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.EStructuralFeature;
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.EcoreUtil;
import org.eclipse.emf.ecore.util.ExtendedMetaData;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
public class Processor {
public class Model {
private Resource resource;
public EPackage getPackage() {
return (EPackage) resource.getContents().get(0);
}
public Resource getResource() {
return resource;
}
public void load(String path) throws IOException {
if (resource != null) {
resource.unload();
}
URI fileUri = URI.createFileURI(path);
XMIResourceFactoryImpl factory = new XMIResourceFactoryImpl();
resource = factory.createResource(fileUri);
resource.load(null);
resourceSet.getResources().add(resource);
}
}
private final static Logger LOGGER = Logger.getLogger(Processor.class.getName());
private ResourceSet resourceSet;
private Resource exceptionsResource;
private String logfilePath;
public Model mofBpmnModel = new Model();
public Model mofBpmndiModel = new Model();
public Model mofDiModel = new Model();
public Model mofDcModel = new Model();
public Model xsdBpmnModel = new Model();
public Model xsdBpmndiModel = new Model();
public Model xsdDiModel = new Model();
public Model xsdDcModel = new Model();
public Processor() {
this.resourceSet = new ResourceSetImpl();
}
private void applyExtendedMetadata(EModelElement target, EModelElement source) {
if (source == null || target == null) {
// LOGGER.severe("Extended Metadata for nothing.");
return;
}
EAnnotation targetExtendedMetadata = target.getEAnnotation(ExtendedMetaData.ANNOTATION_URI);
if (targetExtendedMetadata != null) {
// LOGGER.warning(getName(target) + " already has ExtendedMetadata");
return;
}
EAnnotation extendedMetadata = source.getEAnnotation(ExtendedMetaData.ANNOTATION_URI);
if (extendedMetadata != null) {
LOGGER.info("Patching ExtendedMetadata from " + getName(source) + " to "
+ getName(target));
extendedMetadata = (EAnnotation) EcoreUtil.copy(extendedMetadata);
patchExtendedMetadata(target, extendedMetadata);
target.getEAnnotations().add(extendedMetadata);
}
}
private void patchExtendedMetadata(EModelElement target, EAnnotation extendedMetadata) {
// Patch ExtendedMetadata entries with key "group". By default they have a value
// called "<grouname>:group" which refers to the FeatureMap-type attribute called "<groupname>Group".
// As we don't copy those feature maps (we don't need, as we have only one feature the refers),
// we patch the group to the "global" group
for (Map.Entry<String, String> entry : extendedMetadata.getDetails()) {
EClassifier clazz = null;
if (target instanceof EClassifier)
clazz = (EClassifier) target;
else if (target instanceof EStructuralFeature) {
clazz = ((EStructuralFeature) target).getEContainingClass();
}
String packageUri = clazz.getEPackage().getNsURI();
if (packageUri.endsWith("-XMI")) {
packageUri = packageUri.substring(0, packageUri.length() - 4);
}
if ("group".equalsIgnoreCase(entry.getKey())) {
String value = entry.getValue();
int colonIndex = value.indexOf(':');
if (colonIndex >= 0) {
value = value.substring(0, colonIndex);
value = packageUri + "#" + value;
entry.setValue(value);
}
} else if ("namespace".equalsIgnoreCase(entry.getKey())) {
String value = entry.getValue();
if ("##targetNamespace".equals(value)) {
value = packageUri;
entry.setValue(value);
}
} else if ("affiliation".equalsIgnoreCase(entry.getKey())) {
String value = entry.getValue();
if (!value.contains("#")) {
value = packageUri + "#" + value;
entry.setValue(value);
}
}
}
}
private EStructuralFeature getCorrespondingFeature(EClass mofClass,
EStructuralFeature xsdFeature, ClassMappingException exception) {
EStructuralFeature result = null;
if (mofClass != null) {
String mofFeatureName = xsdFeature.getName();
if (exception != null) {
for (AttributeMappingExcpetion ex : exception.getAttributeExceptions()) {
if (ex.getXsdAttributeName() != null
&& ex.getXsdAttributeName().equals(xsdFeature.getName())) {
mofFeatureName = ex.getMofAttributeName();
break;
}
}
}
result = mofClass.getEStructuralFeature(mofFeatureName);
if (FeatureMap.Entry.class.equals(xsdFeature.getEType().getInstanceClass())) {
// TODO Check if FeatureMaps are needed in our case
return null;
}
if (result == null) {
// Hack: DI/DC metamodels don't have ID
if ("id".equals(xsdFeature.getName())) {
result = (EStructuralFeature) EcoreUtil.copy(xsdFeature);
mofClass.getEStructuralFeatures().add(result);
} else {
LOGGER.severe("No corresponding field found for "
+ xsdFeature.getEContainingClass().getName() + "."
+ xsdFeature.getName() + " on " + mofClass.getName());
}
}
}
return result;
}
private ClassMappingException getExceptionForXsdClass(EClassifier xsdClass) {
for (EObject object : this.exceptionsResource.getContents()) {
ClassMappingException exception = (ClassMappingException) object;
if (xsdClass.getName().equals(exception.getXsdClassName())) {
return exception;
}
}
return null;
}
private EClassifier getMofClass(EClassifier xsdClass, ClassMappingException exception) {
String xsdClassName = xsdClass.getName();
String mofClassName = null;
if (exception != null) {
mofClassName = exception.getMofClassName();
} else if (xsdClassName.startsWith("T")) {
mofClassName = xsdClassName.substring(1);
} else {
mofClassName = xsdClassName;
}
return getMofClass(mofClassName);
}
private EClassifier getClass(EPackage ePackage, String name) {
for (EClassifier cl : ePackage.getEClassifiers()) {
if (name.equals(cl.getName()))
return cl;
}
return null;
}
private EClassifier getMofClass(String name) {
for (EClassifier eClassifier : getMofContent()) {
if (name.equals(eClassifier.getName())) {
return eClassifier;
}
}
LOGGER.severe("Did not find MOF Class " + name);
return null;
}
public List<EClassifier> getMofContent() {
ArrayList<EClassifier> result = new ArrayList<EClassifier>();
addPackage(mofBpmnModel.getPackage(), result);
addPackage(mofBpmndiModel.getPackage(), result);
addPackage(mofDiModel.getPackage(), result);
addPackage(mofDcModel.getPackage(), result);
return result;
}
private void addPackage(EPackage pack, ArrayList<EClassifier> result) {
for (EClassifier classifier : pack.getEClassifiers()) {
if (!"DocumentRoot".equals(classifier.getName())) {
result.add(classifier);
}
}
}
private String getName(EModelElement element) {
if (element instanceof EStructuralFeature) {
EStructuralFeature attribute = (EStructuralFeature) element;
return attribute.getEContainingClass().getName() + "." + attribute.getName();
}
if (element instanceof EClassifier) {
return ((EClassifier) element).getName();
}
return element.toString();
}
public List<EClassifier> getXsdContent(boolean includeDocRoot) {
ArrayList<EClassifier> result = new ArrayList<EClassifier>();
addPackage(xsdBpmnModel.getPackage(), result);
addPackage(xsdBpmndiModel.getPackage(), result);
addPackage(xsdDiModel.getPackage(), result);
addPackage(xsdDcModel.getPackage(), result);
return result;
}
private void importDocumentRoot(EPackage mofPackage, EPackage xsdPackage) {
// Import DocumentRoot
LOGGER.info("<H1>Processing Global Elements - Importing DocumentRoot for "
+ mofPackage.getName() + "</H1>");
EClass oldDocumentRoot = (EClass) getClass(mofPackage, "DocumentRoot");
if (oldDocumentRoot != null) {
LOGGER.warning("Deleting old DocumentRoot");
EcoreUtil.delete(oldDocumentRoot);
}
EClass documentRoot = (EClass) getClass(xsdPackage, "DocumentRoot");
documentRoot = (EClass) EcoreUtil.copy(documentRoot);
mofPackage.getEClassifiers().add(documentRoot);
for (EStructuralFeature feature : documentRoot.getEStructuralFeatures()) {
EClassifier newEClass = getMofClass(feature.getEType(), null);
if (newEClass != null) {
feature.setEType(newEClass);
LOGGER.info("Changing DocumentRoot." + feature.getName() + " to "
+ newEClass.getName());
} else if (xsdPackage.equals(feature.getEType().eContainer())) {
LOGGER.severe("This would cause cross link. Removing type and replace by EObject.");
feature.setEType(EcorePackage.Literals.EOBJECT);
}
EAnnotation extendedMetadata = feature
.getEAnnotation("http:///org/eclipse/emf/ecore/util/ExtendedMetaData");
if (extendedMetadata != null) {
patchExtendedMetadata(feature, extendedMetadata);
}
}
}
public void loadAll(String[] args) throws IOException {
String cmofpath = args[0];
mofBpmnModel.load(cmofpath + '/' + args[2]);
mofBpmndiModel.load(cmofpath + '/' + args[3]);
mofDiModel.load(cmofpath + '/' + args[4]);
mofDcModel.load(cmofpath + '/' + args[5]);
String xsdpath = args[1];
xsdBpmnModel.load(xsdpath + '/' + args[2]);
xsdBpmndiModel.load(xsdpath + '/' + args[3]);
xsdDiModel.load(xsdpath + '/' + args[4]);
xsdDcModel.load(xsdpath + '/' + args[5]);
logfilePath = cmofpath + '/' + "log.html";
@SuppressWarnings("unused")
ExceptionsPackage dummy = ExceptionsPackage.eINSTANCE; // This is needed to ensure that package registry is filled
InputStream exceptionsStream = this.getClass().getResourceAsStream("BPMN20.exceptions");
URI exceptionsURI = URI.createFileURI("BPMN20.exceptions");
XMIResourceFactoryImpl xmiFactory = new XMIResourceFactoryImpl();
exceptionsResource = xmiFactory.createResource(exceptionsURI);
exceptionsResource.load(exceptionsStream, null);
resourceSet.getResources().add(exceptionsResource);
}
public void process() {
FileHandler fh = null;
try {
fh = new FileHandler(logfilePath);
} catch (SecurityException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
fh.setFormatter(new SimpleHTMLFormatter());
LOGGER.addHandler(fh);
importXsdComplexTypesMetadata();
fixModelFlags();
importDocumentRoot(this.mofBpmnModel.getPackage(), this.xsdBpmnModel.getPackage());
importDocumentRoot(this.mofBpmndiModel.getPackage(), this.xsdBpmndiModel.getPackage());
importDocumentRoot(this.mofDiModel.getPackage(), this.xsdDiModel.getPackage());
importDocumentRoot(this.mofDcModel.getPackage(), this.xsdDcModel.getPackage());
// Sort classes alphabetically
sortClassifiersOfPackage(this.mofBpmnModel.getPackage());
sortClassifiersOfPackage(this.mofBpmndiModel.getPackage());
sortClassifiersOfPackage(this.mofDiModel.getPackage());
sortClassifiersOfPackage(this.mofDcModel.getPackage());
try {
this.mofBpmnModel.getResource().save(null);
this.mofBpmndiModel.getResource().save(null);
this.mofDiModel.getResource().save(null);
this.mofDcModel.getResource().save(null);
fh.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void importXsdComplexTypesMetadata() {
// Import all ExtendedMetatdata
LOGGER.info("<H1>Processing Complex Types - Import ExtendedMetadata</H1>");
for (EClassifier xsdClassifier : getXsdContent(false)) {
LOGGER.info("<H3>Processing " + xsdClassifier.getName() + "</H3>");
ClassMappingException exception = getExceptionForXsdClass(xsdClassifier);
EClassifier mofClassifier = getMofClass(xsdClassifier, exception);
if (mofClassifier != null) {
applyExtendedMetadata(mofClassifier, xsdClassifier);
if (xsdClassifier instanceof EClass && mofClassifier instanceof EClass) {
EClass mofClass = (EClass) mofClassifier;
EClass xsdClass = (EClass) xsdClassifier;
for (EStructuralFeature xsdFeature : xsdClass.getEStructuralFeatures()) {
EStructuralFeature mofFeature = getCorrespondingFeature(mofClass,
xsdFeature, exception);
if (mofFeature != null) {
applyExtendedMetadata(mofFeature, xsdFeature);
// Set the references in MOF side based on the type on XSD side.
// IDREF will be local only - this non proxy resolving
if (mofFeature instanceof EReference) {
EReference mofReference = (EReference) mofFeature;
if (!mofReference.isContainer() && !mofReference.isContainment()) {
if ("IDREF".equals(xsdFeature.getEType().getName())) {
mofReference.setResolveProxies(false);
} else if ("QName".equals(xsdFeature.getEType().getName())) {
} else {
LOGGER.warning(mofClass.getName()
+ "."
+ mofFeature.getName()
+ " reference corresponds to neither IDREF nor QName, but "
+ xsdFeature.getEType().getName());
}
}
}
int index = xsdClass.getEStructuralFeatures().indexOf(xsdFeature);
applyIndex(mofFeature, index);
}
}
}
}
}
}
private int getIndexOfFeature(EStructuralFeature o1) {
Integer value = featureSortIndex.get(o1);
if (value != null)
return value.intValue();
return 0;
}
/**
*
*/
private void fixModelFlags() {
LOGGER.info("<H1>Checking merged metamodel - fixing ECore flags</H1>");
for (EClassifier classifier : getMofContent()) {
if (classifier instanceof EClass) {
EClass mofClass = (EClass) classifier;
// Sort features as in original
ECollections.sort(mofClass.getEStructuralFeatures(),
new Comparator<EStructuralFeature>() {
@Override
public int compare(EStructuralFeature o1, EStructuralFeature o2) {
int index1 = getIndexOfFeature(o1);
int index2 = getIndexOfFeature(o2);
return index1 - index2;
}
});
for (EStructuralFeature feature : mofClass.getEStructuralFeatures()) {
String featureName = feature.getEContainingClass().getName() + "."
+ feature.getName();
Boolean hasNoExtendedMetadata = feature
.getEAnnotation(ExtendedMetaData.ANNOTATION_URI) == null;
if (hasNoExtendedMetadata) {
LOGGER.warning(featureName + " does not have XSD metadata");
}
if (feature instanceof EReference) {
EReference reference = (EReference) feature;
EReference opposite = reference.getEOpposite();
if (feature.isVolatile()) {
LOGGER.severe(featureName + " is volatile. Turning off.");
reference.setVolatile(false);
}
if (feature.isDerived() != hasNoExtendedMetadata) {
LOGGER.severe(featureName
+ " has inconsistent derived flag. Correcting.");
feature.setDerived(hasNoExtendedMetadata);
}
if (feature.isTransient() != hasNoExtendedMetadata) {
LOGGER.severe(featureName
+ " has inconsistent transient flag. Correcting.");
feature.setTransient(hasNoExtendedMetadata);
if (hasNoExtendedMetadata && opposite != null) {
opposite.setResolveProxies(false);
}
}
} else if (feature instanceof EAttribute && "id".equals(feature.getName())) {
((EAttribute) feature).setID(true);
}
}
}
}
}
private Map<EStructuralFeature, Integer> featureSortIndex = new HashMap<EStructuralFeature, Integer>();
private void applyIndex(EStructuralFeature mofFeature, int index) {
featureSortIndex.put(mofFeature, index);
}
private void sortClassifiersOfPackage(EPackage pack) {
EList<EClassifier> classifierList = pack.getEClassifiers();
ECollections.sort(classifierList, new Comparator<EClassifier>() {
@Override
public int compare(EClassifier o1, EClassifier o2) {
return o1.getName().compareTo(o2.getName());
}
});
classifierList.move(0, pack.getEClassifier("DocumentRoot"));
}
}