blob: c7d464f48adf44d54201c3f58aaaeebe927feb46 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2019 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* E.D.Willink - initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.examples.build.utilities;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.InternalEObject;
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.EcoreEList;
import org.eclipse.emf.ecore.util.EcoreUtil;
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.mwe.core.WorkflowContext;
import org.eclipse.emf.mwe.core.issues.Issues;
import org.eclipse.emf.mwe.core.lib.AbstractWorkflowComponent;
import org.eclipse.emf.mwe.core.monitor.ProgressMonitor;
import org.eclipse.emf.mwe.utils.Mapping;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.uml.internal.utilities.UMLXMIID;
import org.eclipse.ocl.pivot.utilities.XMIUtil;
import org.eclipse.uml2.common.util.DerivedEObjectEList;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.internal.resource.XMI2UMLHandler;
import org.eclipse.uml2.uml.internal.resource.XMI2UMLLoadImpl;
import org.eclipse.uml2.uml.internal.resource.XMI2UMLResourceFactoryImpl;
import org.eclipse.uml2.uml.internal.resource.XMI2UMLResourceImpl;
import org.eclipse.uml2.uml.resource.UMLResource;
import org.eclipse.uml2.uml.resource.XMI2UMLResource;
import org.xml.sax.helpers.DefaultHandler;
/**
* Assigns simple package/type/type-name xmi:ids to Packages, Types, Properties that do not already have them.
* *.xmi inputs are handled so as to use local rather than registered OMG resources.
*/
@SuppressWarnings("restriction")
public class IdAssigner extends AbstractWorkflowComponent
{
protected static final class LocalXMI2UMLResourceFactory extends XMI2UMLResourceFactoryImpl
{
public static final @NonNull LocalXMI2UMLResourceFactory INSTANCE = new LocalXMI2UMLResourceFactory();
@Override
public Resource createResourceGen(URI uri) {
XMI2UMLResource result = new LocalXMI2UMLResource(uri);
result.setEncoding(XMI2UMLResource.DEFAULT_ENCODING);
return result;
}
}
protected static final class LocalXMI2UMLResource extends XMI2UMLResourceImpl
{
protected LocalXMI2UMLResource(URI uri) {
super(uri);
}
@Override
protected XMLLoad createXMLLoad() {
return new LocalXMI2UMLLoad(createXMLHelper());
}
}
protected static final class LocalXMI2UMLLoad extends XMI2UMLLoadImpl
{
protected LocalXMI2UMLLoad(XMLHelper helper) {
super(helper);
}
@Override
protected DefaultHandler makeDefaultHandler() {
return new LocalXMI2UMLHandler(resource, helper, options);
}
}
protected static final class LocalXMI2UMLHandler extends XMI2UMLHandler
{
protected LocalXMI2UMLHandler(XMLResource xmiResource, XMLHelper helper, Map<?, ?> options) {
super(xmiResource, helper, options);
}
@Override
protected void handleProxy(InternalEObject proxy, String uriLiteral) {
if (uriLiteral.startsWith(XMI2UMLResource.UML_PRIMITIVE_TYPES_LIBRARY_URI)) {
URI uri = URI.createURI(uriLiteral);
uriLiteral = uri.lastSegment() + "#" + uri.fragment();
}
super.handleProxy(proxy, uriLiteral);
}
}
protected Logger log = Logger.getLogger(getClass());
private ResourceSet resourceSet = null;
protected Map<URI, URI> uriMapping = new HashMap<URI, URI>();
protected boolean alphabeticize = false;
protected boolean assignFlatIds = true;
protected boolean normalizeEcore = false;
protected URI normalizePrimitives = null;
protected boolean removeEcoreStereotypes = false;
protected boolean removeProfileApplications = false;
/**
* Define a mapping from a source UML/CMOF file to a UML file with resolved assignments.
*/
public void addMapping(final Mapping uriMap) {
final URI fromURI = URI.createPlatformResourceURI(uriMap.getFrom(), true);
final URI toURI = URI.createPlatformResourceURI(uriMap.getTo(), true);
uriMapping.put(fromURI, toURI);
}
@Override
public void checkConfiguration(Issues issues) {
}
public String computeId(@NonNull StringBuilder s, @NonNull NamedElement namedElement) {
EObject eContainer = namedElement.eContainer();
if (eContainer instanceof NamedElement) {
computeId(s, (NamedElement) eContainer);
s.append("-");
}
String name = namedElement.getName();
if (name == null) {
Resource eResource = namedElement.eResource();
if (eResource instanceof XMLResource) {
UMLXMIID umlXMIid = new UMLXMIID((XMLResource) eResource);
name = umlXMIid.doSwitch(namedElement);
}
}
s.append(name);
return s.toString();
}
public @NonNull ResourceSet getResourceSet() {
ResourceSet resourceSet2 = resourceSet;
if (resourceSet2 == null) {
resourceSet = resourceSet2 = new ResourceSetImpl();
}
return resourceSet2;
}
@Override
public void invokeInternal(WorkflowContext ctx, ProgressMonitor arg1, Issues arg2) {
ResourceSetImpl resourceSet = (ResourceSetImpl) getResourceSet();
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("xmi", LocalXMI2UMLResourceFactory.INSTANCE);
List<EObject> explicitRootContents = new ArrayList<EObject>();
Map<UMLResource, UMLResource> resourceMap = new HashMap<UMLResource, UMLResource>();
for (URI fromURI : uriMapping.keySet()) {
log.info("Assigning Ids from '" + fromURI + "'");
URI toURI = uriMapping.get(fromURI);
UMLResource fromResource = (UMLResource) resourceSet.getResource(fromURI, true);
explicitRootContents.addAll(fromResource.getContents());
EcoreUtil.resolveAll(fromResource);
for (EObject eRoot : fromResource.getContents()) {
if (eRoot instanceof org.eclipse.uml2.uml.Package) {
org.eclipse.uml2.uml.Package umlPackage = (org.eclipse.uml2.uml.Package)eRoot;
String uri = umlPackage.getURI();
if (uri != null) {
resourceSet.getURIResourceMap().put(URI.createURI(uri), fromResource);
}
} // http://www.omg.org/spec/UML/20131001/PrimitiveTypes.xmi
}
UMLResource toResource = (UMLResource) resourceSet.createResource(toURI);
resourceMap.put(fromResource, toResource);
}
EcoreUtil.resolveAll(resourceSet);
ResourceUtils.checkResourceSet(resourceSet);
if (normalizeEcore || (normalizePrimitives != null)) {
log.info("UMLEcoreNormalizing");
Resource ecorePrimitiveTypes = resourceSet.getResource(URI.createURI(UMLResource.ECORE_PRIMITIVE_TYPES_LIBRARY_URI/*"pathmap://UML_LIBRARIES/EcorePrimitiveTypes.library.uml"*/), true);
Resource pivotResource = (normalizePrimitives != null) ? resourceSet.getResource(normalizePrimitives, true) : null;
Map<EObject, Collection<EStructuralFeature.Setting>> map = EcoreUtil.ExternalCrossReferencer.find(explicitRootContents);
for (EObject eObject : map.keySet()) {
EObject eReplacement = null;
Resource eResource = eObject.eResource();
if ((pivotResource != null) && eResource == ecorePrimitiveTypes) {
if (eObject instanceof org.eclipse.uml2.uml.PrimitiveType) {
String className = ((org.eclipse.uml2.uml.PrimitiveType)eObject).getName();
String replacementClassName;
if ("EBooleanObject".equals(className) || "EBoolean".equals(className)) {
replacementClassName = "Boolean";
}
else if ("EDoubleObject".equals(className) || "EDouble".equals(className)) {
replacementClassName = "Real";
}
else if ("EIntegerObject".equals(className) || "EInt".equals(className)) {
replacementClassName = "Integer";
}
else if ("EString".equals(className)) {
replacementClassName = "String";
}
else {
replacementClassName = null;
}
if (replacementClassName != null) {
eReplacement = ((org.eclipse.uml2.uml.Package)pivotResource.getContents().get(0)).getOwnedType(replacementClassName);
}
}
}
else if (normalizeEcore) {
EObject eRoot = EcoreUtil.getRootContainer(eObject);
if (eRoot instanceof Model) {
String uri = ((Model)eRoot).getURI();
if (EcorePackage.eNS_URI.equals(uri)) {
if (eObject instanceof org.eclipse.uml2.uml.Class) {
String className = ((org.eclipse.uml2.uml.Class)eObject).getName();
eReplacement = resourceSet.getEObject(URI.createURI(UMLResource.ECORE_METAMODEL_URI + "#" + className), true);
}
}
}
}
if (eReplacement != null) {
Collection<Setting> eSettings = map.get(eObject);
assert eSettings != null;
for (EStructuralFeature.Setting eSetting : eSettings) {
if (eSetting instanceof DerivedEObjectEList<?>) {}
else if (eSetting instanceof EcoreEList.UnmodifiableEList<?>) {}
else {
try {
eSetting.set(eReplacement);
}
catch (Exception e) {
log.error("eSetting failed for a " + eSetting.getClass().getName());
}
}
}
}
}
}
for (UMLResource fromResource : resourceMap.keySet()) {
UMLResource toResource = resourceMap.get(fromResource);
assert toResource != null;
toResource.getContents().addAll(fromResource.getContents());
if (assignFlatIds) {
for (TreeIterator<EObject> tit = toResource.getAllContents(); tit.hasNext(); ) {
EObject eObject = tit.next();
if (eObject instanceof org.eclipse.uml2.uml.Package) {
NamedElement namedElement = (NamedElement) eObject;
toResource.setID(eObject, namedElement.getName());
}
/* else if (eObject instanceof Property) {
Property property = (Property) eObject;
Type type = property.getClass_();
if (type != null) {
String id = toResource.getID(eObject);
if (assignIds || (id == null)) { // || !id.startsWith(type.getName())) { // If it starts with type it may be a good name
toResource.setID(eObject, computeId(new StringBuilder(), property));
}
}
} */
}
}
else {
// new UMLXMIID(toResource).assign();
for (TreeIterator<EObject> tit = toResource.getAllContents(); tit.hasNext(); ) {
EObject eObject = tit.next();
if ((eObject instanceof org.eclipse.ocl.pivot.Package) || (eObject instanceof Type) || (eObject instanceof Property) || (eObject instanceof Operation)) {
@SuppressWarnings("null")@NonNull NamedElement namedElement = (NamedElement) eObject;
toResource.setID(eObject, computeId(new StringBuilder(), namedElement));
}
else if ((eObject instanceof org.eclipse.uml2.uml.Package) || (eObject instanceof org.eclipse.uml2.uml.Type) || (eObject instanceof org.eclipse.uml2.uml.Property) || (eObject instanceof org.eclipse.uml2.uml.Operation)) {
@SuppressWarnings("null")@NonNull NamedElement namedElement = (NamedElement) eObject;
toResource.setID(eObject, computeId(new StringBuilder(), namedElement));
}
/* else if (eObject instanceof Property) {
Property property = (Property) eObject;
Type type = property.getClass_();
if (type != null) {
String id = toResource.getID(eObject);
if (assignIds || (id == null)) { // || !id.startsWith(type.getName())) { // If it starts with type it may be a good name
toResource.setID(eObject, computeId(new StringBuilder(), property));
}
}
} */
}
}
}
if (removeEcoreStereotypes) {
for (UMLResource toResource : resourceMap.values()) {
List<EObject> unwanted = new ArrayList<EObject>();
for (EObject eObject : toResource.getContents()) {
if (!(eObject instanceof org.eclipse.uml2.uml.Element)) {
unwanted.add(eObject);
}
}
toResource.getContents().removeAll(unwanted);
}
}
if (removeProfileApplications) {
for (UMLResource toResource : resourceMap.values()) {
if (toResource != null) {
for (EObject eObject : toResource.getContents()) {
if (eObject instanceof org.eclipse.uml2.uml.Package) {
((org.eclipse.uml2.uml.Package)eObject).getProfileApplications().clear();
}
}
}
}
}
if (alphabeticize) {
PackageAlphabetizer alphabeticizer = new PackageAlphabetizer();
for (UMLResource toResource : resourceMap.values()) {
if (toResource != null) {
alphabeticizer.alphabeticize(toResource);
}
}
}
for (UMLResource toResource : resourceMap.values()) {
log.info("Assigned Ids to '" + toResource.getURI() + "'");
try {
Map<Object, Object> saveOptions = getSaveOptions();
XMIUtil.retainLineWidth(saveOptions, toResource);
toResource.save(saveOptions);
} catch (IOException e) {
throw new RuntimeException("Problems running " + getClass().getSimpleName(), e);
}
}
}
protected Map<Object, Object> getSaveOptions() {
Map<Object, Object> result = XMIUtil.createSaveOptions();
result.put(Resource.OPTION_SAVE_ONLY_IF_CHANGED, Resource.OPTION_SAVE_ONLY_IF_CHANGED_MEMORY_BUFFER);
return result;
}
/**
* True to alphabeticize the UML packages and contents.
*/
public void setAlphabeticize(boolean alphabeticize) {
this.alphabeticize = alphabeticize;
}
public void setAssignFlatIds(boolean assignFlatIds) {
this.assignFlatIds = assignFlatIds;
}
/**
* True to normalize the UML models by replacing references to an ecore.uml by Ecore.metamodel.uml - Bug 453771.
*/
public void setNormalizeEcore(boolean normalizeEcore) {
this.normalizeEcore = normalizeEcore;
}
/**
* Non-null to normalize the UML models by replacing references to a Ecore primitives by those prefixed by this URI.
*/
public void setNormalizePrimitives(String normalizePrimitives) {
this.normalizePrimitives = URI.createPlatformResourceURI(normalizePrimitives, true);
}
/**
* True to remove Ecore stereotypes from the UML models making them OMG-like.
*/
public void setRemoveEcoreStereotypes(boolean removeEcoreStereotypes) {
this.removeEcoreStereotypes = removeEcoreStereotypes;
}
/**
* True to remove all profile applications from the UML models making them OMG-like.
*/
public void setRemoveProfileApplications(boolean removeProfileApplications) {
this.removeProfileApplications = removeProfileApplications;
}
public void setResourceSet(@NonNull ResourceSet resourceSet) {
this.resourceSet = resourceSet;
}
}