| /******************************************************************************* |
| * Copyright (c) 2011, 2012, 2013 Red Hat, Inc. |
| * All rights reserved. |
| * This program is 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: |
| * Red Hat, Inc. - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.bpmn2.modeler.runtime.jboss.jbpm5; |
| |
| import org.eclipse.bpmn2.DataInput; |
| import org.eclipse.bpmn2.DataObject; |
| import org.eclipse.bpmn2.DataOutput; |
| import org.eclipse.bpmn2.Error; |
| import org.eclipse.bpmn2.Escalation; |
| import org.eclipse.bpmn2.Message; |
| import org.eclipse.bpmn2.MultiInstanceLoopCharacteristics; |
| import org.eclipse.bpmn2.Signal; |
| import org.eclipse.bpmn2.modeler.core.validation.SyntaxCheckerUtils; |
| import org.eclipse.bpmn2.modeler.runtime.jboss.jbpm5.model.drools.GlobalType; |
| import org.eclipse.emf.common.notify.Adapter; |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.Notifier; |
| import org.eclipse.emf.common.util.TreeIterator; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.InternalEObject; |
| import org.eclipse.emf.ecore.impl.ENotificationImpl; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.validation.model.EvaluationMode; |
| import org.eclipse.emf.validation.service.IValidator; |
| import org.eclipse.emf.validation.service.ModelValidationService; |
| |
| /* |
| * jBPM uses certain objects' IDs instead of names because...well, I don't know why...bad decision maybe? |
| * Anyway, it means that the names and IDs MUST be kept in sync because our UI does not allow IDs to be |
| * edited directly since that would be a VERY BAD THING. IDs MUST be unique, and allowing the user to edit |
| * them could violate that constraint, e.g. I should be able to create a org.eclipse.bpmn2.Property named |
| * "counter" in every SubProcess in my file, but that would violate the ID constraint. |
| * |
| * But, it is what it is... |
| */ |
| public class ProcessVariableNameChangeAdapter implements Adapter { |
| |
| private ProcessVariableNameChangeAdapter() { |
| } |
| |
| public static ProcessVariableNameChangeAdapter adapt(EObject object) { |
| if (appliesTo(object)) { |
| for (Adapter a : ((EObject)object).eAdapters()) { |
| if (a instanceof ProcessVariableNameChangeAdapter) { |
| return (ProcessVariableNameChangeAdapter)a; |
| } |
| } |
| ProcessVariableNameChangeAdapter a = new ProcessVariableNameChangeAdapter(); |
| object.eAdapters().add(a); |
| return a; |
| } |
| return null; |
| } |
| |
| public static boolean appliesTo(EObject object) { |
| return (object instanceof org.eclipse.bpmn2.Property || |
| object instanceof DataObject || |
| object instanceof Message || |
| object instanceof Signal || |
| object instanceof Error || |
| object instanceof Escalation || |
| object instanceof GlobalType || |
| ( |
| // DataInput and DataOutput objects are a special case: Only |
| // keep the name and ID in sync if the DataInput is being used |
| // as the instance parameter for MultiInstanceLoopCharacteristics |
| // (or if the object hasn't been added to a container yet). |
| (object instanceof DataInput || object instanceof DataOutput) && ( |
| object.eContainer() instanceof MultiInstanceLoopCharacteristics || |
| object.eContainer()==null) |
| ) |
| ); |
| |
| } |
| |
| @Override |
| public void notifyChanged(Notification notification) { |
| if (notification.getNotifier() instanceof EObject) { |
| EObject object = (EObject)notification.getNotifier(); |
| if (notification.getEventType()==Notification.SET && appliesTo(object)) { |
| |
| Object f = notification.getFeature(); |
| if (f instanceof EStructuralFeature) { |
| EStructuralFeature feature = (EStructuralFeature)f; |
| EStructuralFeature idFeature = object.eClass().getEStructuralFeature("id"); //$NON-NLS-1$ |
| EStructuralFeature nameFeature = object.eClass().getEStructuralFeature("name"); //$NON-NLS-1$ |
| EStructuralFeature identifierFeature = object.eClass().getEStructuralFeature("identifier"); //$NON-NLS-1$ |
| if (identifierFeature!=null) |
| nameFeature = identifierFeature; |
| if ("name".equals(feature.getName()) || "identifier".equals(feature.getName())) { //$NON-NLS-1$ //$NON-NLS-2$ |
| // you guys are killing me here! |
| // if the object is still being created and it hasn't been added to a container |
| // yet, then don't keep the ID in sync with the name...in other words, if the name |
| // is being set, don't alter the ID to match the name...yet... |
| if (object.eContainer()==null) |
| return; |
| Object newValue = notification.getNewValue(); |
| Object oldValue = notification.getOldValue(); |
| if (newValue!=null && !newValue.equals(oldValue)) |
| { |
| if (idFeature!=null) { |
| if (!SyntaxCheckerUtils.isJavaIdentifier((String)newValue)) |
| newValue = SyntaxCheckerUtils.toJavaIdentifier((String)newValue); |
| boolean deliver = object.eDeliver(); |
| try { |
| if (deliver) |
| object.eSetDeliver(false); |
| |
| // No need to make this ID unique; ID collisions will be detected |
| // during validate() and will be reported in the UI. |
| object.eSet(nameFeature, newValue); |
| object.eSet(idFeature, newValue); |
| } |
| catch (Exception e) { |
| |
| } |
| finally { |
| object.eSetDeliver(deliver); |
| } |
| validate(notification); |
| } |
| } |
| } |
| else if ("id".equals(feature.getName())) { //$NON-NLS-1$ |
| Object newValue = notification.getNewValue(); |
| Object oldValue = notification.getOldValue(); |
| if (newValue!=null && !newValue.equals(oldValue)) |
| { |
| if (nameFeature!=null) { |
| boolean deliver = object.eDeliver(); |
| try { |
| if (deliver) |
| object.eSetDeliver(false); |
| |
| Object uniqueId = makeUniqueId(object,newValue); |
| object.eSet(nameFeature, uniqueId); |
| object.eSet(idFeature, uniqueId); |
| } |
| catch (Exception e) { |
| |
| } |
| finally { |
| object.eSetDeliver(deliver); |
| } |
| validate(notification); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private Object makeUniqueId(EObject object, Object id) { |
| int i = 1; |
| Object uniqueId = id; |
| EObject dup = null; |
| do { |
| dup = findDuplicateId(object,uniqueId); |
| if (dup!=null) { |
| uniqueId = id + "_" + i++; //$NON-NLS-1$ |
| } |
| } |
| while (dup!=null); |
| return uniqueId; |
| } |
| |
| private EObject findDuplicateId(EObject object, Object id) { |
| if (object!=null && id!=null) { |
| Resource resource = object.eResource(); |
| |
| TreeIterator<EObject> iter = resource.getAllContents(); |
| while (iter.hasNext()) { |
| EObject o = iter.next(); |
| if (o!=object) { |
| EStructuralFeature f = o.eClass().getEStructuralFeature("id"); //$NON-NLS-1$ |
| if (f!=null) { |
| Object existingId = o.eGet(f); |
| if (id.equals(existingId)) |
| return o; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public Notifier getTarget() { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public void setTarget(Notifier newTarget) { |
| if (newTarget instanceof EObject) { |
| EObject object = (EObject)newTarget; |
| EStructuralFeature feature = object.eClass().getEStructuralFeature("name"); //$NON-NLS-1$ |
| if (feature!=null) { |
| Object oldValue = ""; //$NON-NLS-1$ |
| Object newValue = object.eGet(feature); |
| Notification notification = new ENotificationImpl((InternalEObject)object, |
| Notification.SET, feature, |
| oldValue, newValue); |
| notifyChanged(notification); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isAdapterForType(Object type) { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| |
| private void validate(Notification notification) { |
| IValidator<Notification> validator = ModelValidationService.getInstance().newValidator(EvaluationMode.LIVE); |
| validator.validate(notification); |
| } |
| } |