/******************************************************************************* * Copyright (c) 2013, 2015 Willink Transformations 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: * E.D.Willink - initial API and implementation *******************************************************************************/ package org.eclipse.ocl.pivot.internal.utilities; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.emf.common.notify.Adapter; 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.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.XMLResource; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.ocl.pivot.Element; import org.eclipse.ocl.pivot.internal.messages.PivotMessagesInternal; import org.eclipse.ocl.pivot.internal.resource.ASResourceFactory; import org.eclipse.ocl.pivot.internal.resource.ASResourceFactoryRegistry; import org.eclipse.ocl.pivot.resource.ASResource; import org.eclipse.ocl.pivot.utilities.AS2MonikerVisitor; import org.eclipse.ocl.pivot.utilities.AS2XMIidVisitor; import org.eclipse.ocl.pivot.utilities.ClassUtil; import org.eclipse.ocl.pivot.utilities.StringUtil; public class AS2XMIid { /** * Aggregated Diagnostic added to ASResource.errors if xmi:id assignment has to allocate * an unstable random uuid to avoid a duplicate stable id. */ public static final class UnstableXMIidDiagnostics implements Resource.Diagnostic { protected final @NonNull String message; public UnstableXMIidDiagnostics(@NonNull String message) { this.message = message; } @Override public String getMessage() { return message.replace("\n", "\\n"); } @Override public String getLocation() { return null; } @Override public int getLine() { return 0; } @Override public int getColumn() { return 0; } @Override public String toString() { return message; } } /** * Create an AS2ID conversion primed with the xmi:id values obtained by loading uri. */ public static @NonNull AS2XMIid load(@NonNull URI uri) { Map<String, String> moniker2id = new HashMap<String, String>(); ResourceSet resourceSet = new ResourceSetImpl(); ASResourceFactoryRegistry.INSTANCE.configureResourceSet(resourceSet); // resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(PivotConstants.OCL_AS_FILE_EXTENSION, OCLASResourceFactory.INSTANCE); try { Resource resource = resourceSet.getResource(uri, true); if (resource instanceof XMLResource) { @SuppressWarnings("null")@NonNull List<Adapter> eAdapters = resource.eAdapters(); AS2MonikerVisitor monikerVisitor = ClassUtil.getAdapter(AS2MonikerVisitor.class, eAdapters); if (monikerVisitor != null) { XMLResource xmlResource = (XMLResource) resource; for (TreeIterator<EObject> tit = xmlResource.getAllContents(); tit.hasNext(); ) { EObject eObject = tit.next(); if (eObject instanceof Element) { Element element = (Element) eObject; String oldID = xmlResource.getID(element); if (oldID != null) { Object moniker = element.accept(monikerVisitor); if (moniker instanceof String) { moniker2id.put((String) moniker, oldID); } } } } } } } catch (Exception e) {} return new AS2XMIid(moniker2id); } protected final @NonNull Map<String, String> moniker2id; public AS2XMIid() { this.moniker2id = new HashMap<String, String>(); } protected AS2XMIid(@NonNull Map<String, String> moniker2id) { this.moniker2id = moniker2id; } /** * Assign xmi:id values to referenceable elements in asResource re-using the xmi:id * values read when this AS2ID was constructed. */ public void assignIds(@NonNull ASResource asResource, @Nullable Map<?, ?> options) { StringBuilder s = null; Map<String, EObject> allIds = new HashMap<String, EObject>(); ASResourceFactory resourceFactory = asResource.getASResourceFactory(); Object optionInternalUUIDs = options != null ? options.get(ASResource.OPTION_INTERNAL_UUIDS) : null; boolean internalUUIDs = (optionInternalUUIDs != null) && Boolean.valueOf(optionInternalUUIDs.toString()); for (TreeIterator<EObject> tit = asResource.getAllContents(); tit.hasNext(); ) { EObject eObject = tit.next(); if (eObject instanceof Element) { Element element = (Element)eObject; AS2XMIidVisitor idVisitor = resourceFactory.createAS2XMIidVisitor(this); String idOld = asResource.getID(element); String idAuto = idVisitor.getID(element, internalUUIDs); String idNew = idOld != null ? idOld : idAuto; if (idNew != null) { EObject oldElement = allIds.get(idNew); boolean badId = (idNew.length() <= 0) || (oldElement != null); boolean changedId = (idAuto != null) && !idNew.equals(idAuto); if (badId) { idNew = EcoreUtil.generateUUID(); } else if (changedId) { idNew = idAuto; } if (badId || changedId) { if (s == null) { s = new StringBuilder(); } s.append("\n " + element.eClass().getName() + " '" + idAuto + "'"); } assert element != null; allIds.put(idNew, element); if (idNew != idOld) { asResource.setID(element, idNew); } } } } if (s != null) { String message = StringUtil.bind(PivotMessagesInternal.UnstableXMIid_ERROR_, s.toString()); asResource.getErrors().add(new UnstableXMIidDiagnostics(message)); } } /** * Assign xmi:id values to referenceable elements in asResourceSet re-using the xmi:id * values read when this AS2ID was constructed. */ public void assignIds(@NonNull ResourceSet asResourceSet, @Nullable Map<?, ?> options) { EcoreUtil.resolveAll(asResourceSet); // Pending a fix to BUG 451928 this may provoke CME unless all resources already loaded for (@SuppressWarnings("null")@NonNull Resource resource : asResourceSet.getResources()) { if (resource instanceof ASResource) { assignIds((ASResource)resource, options); } } EnvironmentFactoryInternal environmentFactory = PivotUtilInternal.findEnvironmentFactory(asResourceSet); if (environmentFactory != null) { environmentFactory.getMetamodelManager().assignLibraryIds(this, options); } } public String getID(@NonNull Element element, boolean internalUUIDs) { String moniker = AS2Moniker.toString(element); String id = moniker2id.get(moniker); if ((id == null) && internalUUIDs) { id = EcoreUtil.generateUUID(); // System.out.println(id + " for " + element + " " + ClassUtil.debugSimpleName(element)); } return id; } }