package org.eclipse.bpmn2.modeler.core.utils; import java.util.HashMap; import java.util.Hashtable; import org.eclipse.bpmn2.BaseElement; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; public class ModelUtil { // TODO: need to determine whether IDs need to be unique within a Resource or ResourceSet - see getKey() // Map of EMF resource sets to ID mapping tables. The ID mapping tables map a BPMN2 element ID string to the EObject. // The EObject is not used anywhere (yet!) just a placeholder to allow use of a HashMap for fast lookups of the ID string. // The ID strings are composed from the BPMN2 element type name and a sequence number (starting at 1). // When a new ID is requested, generateID() simply increments the sequence number until an ID is found that isn't // already in the table. public static HashMap<Object, Hashtable<String, EObject>> ids = new HashMap<Object, Hashtable<String, EObject>>(); // Map of ID strings and sequential counters for each BPMN2 element type. public static HashMap<String, Integer> defaultIds = new HashMap<String, Integer>(); /** * Clear the IDs hashmap for the given EMF Resource. This should be called * when the editor is disposed to avoid unnecessary growth of the IDs table. * * @param res - the EMF Resource that was used to generate the ID strings. */ public static void clearIDs(Resource res, boolean all) { ids.remove( getKey(res) ); if (all) { defaultIds.clear(); } } /** * Construct the first part of the ID string using the BPMN2 element type name. * If the object is a DI element, concatenate the BPMN2 element type name. * * @param obj - the BPMN2 object * @return name string */ public static String getObjectName(EObject obj) { String name; EStructuralFeature feature = ((EObject)obj).eClass().getEStructuralFeature("bpmnElement"); if (feature!=null) { EObject bpmnElement = (EObject) obj.eGet(feature); name = obj.eClass().getName() + "_" + bpmnElement.eClass().getName(); } else { name = obj.eClass().getName(); } return name; } private static Object getKey(EObject obj) { assert(obj!=null); return getKey(obj.eResource()); } private static Object getKey(Resource res) { assert(res!=null); return res.getResourceSet(); } /** * If an EObject has not yet been added to a Resource (e.g. during construction) * generate an ID string using a different strategy (basically same ID prefixed with an underscore). * The "defaultIds" table is used to track the next sequential ID value for a given element type. * * @param obj - the BPMN2 object * @return the ID string */ private static String generateDefaultID(EObject obj) { String name = getObjectName(obj); Integer value = defaultIds.get(name); if (value==null) value = Integer.valueOf(1); value = Integer.valueOf( value.intValue() + 1 ); defaultIds.put(name, Integer.valueOf(value)); return "_" + name + "_" + value; } /** * Generate an ID string for a given BPMN2 object. * * @param obj - the BPMN2 object * @return the ID string */ public static String generateID(EObject obj) { return generateID(obj,obj.eResource()); } /** * Generate an ID string for a given BPMN2 object that will (eventually!) be added to the given Resource. * * CAUTION: IDs for objects that have already been deleted WILL be reused. * * @param obj - the BPMN2 object * @param res - the Resource to which the object will be added * @return the ID string */ public static String generateID(EObject obj, Resource res) { Object key = (res==null ? getKey(obj) : getKey(res)); if (key!=null) { Hashtable<String, EObject> tab = ids.get(key); if (tab==null) { tab = new Hashtable<String, EObject>(); ids.put(key, tab); } String name = getObjectName(obj); for (int i=1;; ++i) { String id = name + "_" + i; if (tab.get(id)==null) { tab.put(id, obj); return id; } } } return generateDefaultID(obj); } /** * Add an ID string to the ID mapping table(s). This must be used during model import * to add existing BPMN2 element IDs to the table so we don't generate duplicates. * * @param obj - the BPMN2 object */ public static void addID(EObject obj) { EStructuralFeature feature = ((EObject)obj).eClass().getEStructuralFeature("id"); if (feature!=null) { Object value = obj.eGet(feature); if (value!=null) { addID(obj,(String)value); } else { // TODO: what to do here if the BPMN2 element has an "id" attribute which is not set? // should we generate one and set it? } } } /** * Add an ID string to the ID mapping table(s). This must be used during model import * to add existing BPMN2 element IDs to the table so we don't generate duplicates. * * @param obj - the BPMN2 object * @param id - the object's ID string */ public static void addID(EObject obj, String id) { Object key = getKey(obj); String name = getObjectName(obj); if (key==null || id.startsWith("_" + name + "_")) { int newValue = 0; try { int i = id.lastIndexOf('_') + 1; if (i<id.length()) newValue = Integer.parseInt(id.substring(i)); } catch (Exception e) { } Integer oldValue = defaultIds.get(name); if (oldValue==null || newValue > oldValue.intValue()) defaultIds.put(name, Integer.valueOf(newValue)); } else { Hashtable<String, EObject> tab = ids.get(key); if (tab==null) { tab = new Hashtable<String, EObject>(); ids.put(key, tab); } tab.put(id, obj); } } /** * Generate a unique ID for the given BPMN2 element and set it. * This should only be used during object construction AFTER an object has * already been added to a Resource. * * @param obj - the BPMN2 object */ public static void setID(EObject obj) { setID(obj,obj.eResource()); } /** * Generate a unique ID for the given BPMN2 element and set it. * This should be used during object construction if the object has NOT YET * been added to a Resource. * * @param obj - the BPMN2 object * @param res - the Resource to which the object will be added */ public static void setID(EObject obj, Resource res) { EStructuralFeature feature = ((EObject)obj).eClass().getEStructuralFeature("id"); if (feature!=null) { obj.eSet(feature, generateID(obj,res)); } } public static String getName(BaseElement element) { EStructuralFeature feature = element.eClass().getEStructuralFeature("name"); if (feature!=null && element.eGet(feature) instanceof String) return (String) element.eGet(feature); return null; } public static boolean hasName(BaseElement element) { EStructuralFeature feature = element.eClass().getEStructuralFeature("name"); return feature!=null; } }