/******************************************************************************* * Copyright (c) 2010, 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.utilities; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.ENamedElement; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.xmi.XMLResource; import org.eclipse.emf.ecore.xml.type.XMLTypePackage; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.ocl.pivot.util.DerivedConstants; public class XMIUtil { public static interface IdCreator { /** * Create the id for eObject avoiding any ids in knownIds, which may be null for no exclusions. * * @param eObject * @param knownIds */ public String createId(EObject eObject, Set<String> knownIds); } /** * Create xmi:id's comprising a unique universal identifier */ public static class UUIDCreator implements IdCreator { @Override public String createId(EObject eObject, Set<String> knownIds) { while (true) { String uuid = EcoreUtil.generateUUID(); if ((knownIds == null) || !knownIds.contains(uuid)) return uuid; } } } /** * Create short xmi:id's comprising a prefix and a small random count */ public static class ShortPrefixedIdCreator implements IdCreator { protected final String prefix; public ShortPrefixedIdCreator(String prefix) { this.prefix = prefix; } @Override public String createId(EObject eObject, Set<String> knownIds) { int knownSize = knownIds != null ? knownIds.size() : 1; for (int multiplier = Math.max(10, 10 * knownSize); true; multiplier *= 10) { for (int tries = 0; tries < 10; tries++) { String id = prefix + (int)(Math.random() * multiplier); if ((knownIds == null) || !knownIds.contains(id)) return id; } } } } /** * Create short xmi:id's comprising a prefix and a linearly increasing count */ public static class LinearPrefixedIdCreator implements IdCreator { protected final String prefix; private int next; public LinearPrefixedIdCreator(String prefix) { this.prefix = prefix; } @Override public String createId(EObject eObject, Set<String> knownIds) { while (true) { String id = prefix + ++next; if ((knownIds == null) || !knownIds.contains(id)) return id; } } } /** * Create xmi:id's using the same hierarchical/URI fragment algorithm as EMOFResourceImpl */ public static class HierachicalENamedElementIdCreator implements IdCreator { public HierachicalENamedElementIdCreator() {} @Override public String createId(EObject eObject, Set<String> knownIds) { List<String> uriFragmentPath = new ArrayList<String>(); for (EObject container = eObject.eContainer(); container != null; container = eObject.eContainer()) { uriFragmentPath.add(((InternalEObject)container).eURIFragmentSegment(eObject.eContainmentFeature(), eObject)); eObject = container; } StringBuilder result; if (eObject instanceof ENamedElement) result = new StringBuilder(((ENamedElement)eObject).getName()); else result = new StringBuilder("_" + Integer.toString(eObject.eResource().getContents().indexOf(eObject))); for (ListIterator<String> i = uriFragmentPath.listIterator(uriFragmentPath.size()); i.hasPrevious(); ) { result.append('.'); result.append(i.previous()); } return result.toString(); } } public static interface IdFilter { public boolean createId(EObject eObject); } public static class ExcludedEClassIdFilter implements IdFilter { protected final Set<EClass> excludedClasses; public ExcludedEClassIdFilter(EClass[] excludedClasses) { this.excludedClasses = new HashSet<EClass>(); for (EClass excludedClass : excludedClasses) this.excludedClasses.add(excludedClass); } public ExcludedEClassIdFilter(Set<EClass> excludedClasses) { this.excludedClasses = excludedClasses; } @Override public boolean createId(EObject eObject) { EClass eClass = eObject.eClass(); for (EClass excludedClass : excludedClasses) if (excludedClass.isSuperTypeOf(eClass)) return false; return true; } } public static final UUIDCreator uuidCreator = new UUIDCreator(); /** * Assign an xmi:id to all objects in resource. A non-null idFilter may choose whether an xmi:id * is assigned. The idCreator is responsible for providing a candidate xmi:id, which, if not unique * will be suffixed until it is. * * @param resource * @param idCreator * @param idFilter */ public static void assignIds(Resource resource, IdCreator idCreator, IdFilter idFilter) { if (!(resource instanceof XMLResource)) return; XMLResource xmlResource = (XMLResource) resource; final Set<String> knownIds = new HashSet<String>(256); // The XMLResource.getEObjectToIDMap() method is deprecated // and the replacement slow since we need a total traversal final List<EObject> idLess = new ArrayList<EObject>(100); for (TreeIterator<EObject> iterator = resource.getAllContents(); iterator.hasNext(); ) { EObject eObject = iterator.next(); String id = xmlResource.getID(eObject); if (id != null) { knownIds.add(id); // if (eObject instanceof ENamedElement) // System.out.println(id + " ==> " + eObject.eClass().getName() + "." + ((ENamedElement) eObject).getName()); // else // System.out.println(id + " ==> " + eObject.eClass().getName()); } else if ((idFilter == null) || idFilter.createId(eObject)) idLess.add(eObject); } for (EObject eObject : idLess) { String id = idCreator.createId(eObject, knownIds); if (id != null) { String uniqueId = id; for (int i = 1; knownIds.contains(uniqueId); i++) uniqueId = id + '_' + i; xmlResource.setID(eObject, uniqueId); knownIds.add(uniqueId); // if (eObject instanceof ENamedElement) // System.out.println(uniqueId + " --> " + eObject.eClass().getName() + "." + ((ENamedElement) eObject).getName()); // else // System.out.println(uniqueId + " --> " + eObject.eClass().getName()); } } } public static void assignIds(Resource resource, String xmiIdPrefix) { if (xmiIdPrefix == null) xmiIdPrefix = "_"; assignIds(resource, new ShortPrefixedIdCreator(xmiIdPrefix), new ExcludedEClassIdFilter(new EClass[] { XMLTypePackage.Literals.ANY_TYPE, EcorePackage.Literals.EGENERIC_TYPE/*, EcorePackage.Literals.EANNOTATION, EcorePackage.Literals.ESTRING_TO_STRING_MAP_ENTRY */ })); } public static void assignLinearIds(Resource resource, String xmiIdPrefix) { if (xmiIdPrefix == null) xmiIdPrefix = "_"; assignIds(resource, new LinearPrefixedIdCreator(xmiIdPrefix), new ExcludedEClassIdFilter(new EClass[] { XMLTypePackage.Literals.ANY_TYPE, EcorePackage.Literals.EGENERIC_TYPE/*, EcorePackage.Literals.EANNOTATION, EcorePackage.Literals.ESTRING_TO_STRING_MAP_ENTRY */ })); } /** * Return a set of saveOptions supporting UTF-8 with 132 character Unix lines. */ public static @NonNull Map<Object, Object> createSaveOptions() { Map<Object, Object> saveOptions = new HashMap<Object, Object>(); saveOptions.put(XMLResource.OPTION_ENCODING, "UTF-8"); saveOptions.put(DerivedConstants.RESOURCE_OPTION_LINE_DELIMITER, "\n"); saveOptions.put(XMLResource.OPTION_LINE_WIDTH, Integer.valueOf(132)); return saveOptions; } }