/******************************************************************************* * Copyright (c) 2011, 2012 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 * * @author Bob Brodt ******************************************************************************/ package org.eclipse.bpmn2.modeler.core.model; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.eclipse.bpmn2.Assignment; import org.eclipse.bpmn2.BaseElement; import org.eclipse.bpmn2.Bpmn2Factory; import org.eclipse.bpmn2.Bpmn2Package; import org.eclipse.bpmn2.CompensateEventDefinition; import org.eclipse.bpmn2.DataAssociation; import org.eclipse.bpmn2.Definitions; import org.eclipse.bpmn2.Documentation; import org.eclipse.bpmn2.Expression; import org.eclipse.bpmn2.ExtensionAttributeValue; import org.eclipse.bpmn2.FormalExpression; import org.eclipse.bpmn2.Import; import org.eclipse.bpmn2.ItemDefinition; import org.eclipse.bpmn2.Lane; import org.eclipse.bpmn2.Participant; import org.eclipse.bpmn2.Process; import org.eclipse.bpmn2.RootElement; import org.eclipse.bpmn2.SubProcess; import org.eclipse.bpmn2.di.BPMNDiagram; import org.eclipse.bpmn2.di.BPMNEdge; import org.eclipse.bpmn2.di.BPMNLabel; import org.eclipse.bpmn2.di.BPMNLabelStyle; import org.eclipse.bpmn2.di.BPMNPlane; import org.eclipse.bpmn2.di.BPMNShape; import org.eclipse.bpmn2.di.BpmnDiPackage; import org.eclipse.bpmn2.modeler.core.Activator; import org.eclipse.bpmn2.modeler.core.LifecycleEvent; import org.eclipse.bpmn2.modeler.core.LifecycleEvent.EventType; import org.eclipse.bpmn2.modeler.core.adapters.ExtendedPropertiesAdapter; import org.eclipse.bpmn2.modeler.core.adapters.IExtensionValueAdapter; import org.eclipse.bpmn2.modeler.core.features.GraphitiConstants; import org.eclipse.bpmn2.modeler.core.model.Bpmn2ModelerFactory.Bpmn2ModelerDocumentRootImpl; import org.eclipse.bpmn2.modeler.core.model.Bpmn2ModelerFactory.KeyValue; import org.eclipse.bpmn2.modeler.core.preferences.Bpmn2Preferences; import org.eclipse.bpmn2.modeler.core.runtime.TargetRuntime; import org.eclipse.bpmn2.modeler.core.runtime.TargetRuntimeAdapter; import org.eclipse.bpmn2.modeler.core.runtime.TypeLanguageDescriptor; import org.eclipse.bpmn2.modeler.core.utils.FeatureSupport; import org.eclipse.bpmn2.modeler.core.utils.ImportUtil; import org.eclipse.bpmn2.modeler.core.utils.ModelUtil; import org.eclipse.bpmn2.modeler.core.utils.NamespaceUtil; import org.eclipse.bpmn2.modeler.core.validation.SyntaxCheckerUtils; import org.eclipse.bpmn2.util.Bpmn2ResourceImpl; import org.eclipse.bpmn2.util.ImportHelper; import org.eclipse.bpmn2.util.OnlyContainmentTypeInfo; import org.eclipse.bpmn2.util.QNameURIHandler; import org.eclipse.bpmn2.util.XmlExtendedMetadata; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.dd.dc.Bounds; import org.eclipse.dd.dc.DcFactory; import org.eclipse.dd.dc.DcPackage; import org.eclipse.dd.dc.Point; import org.eclipse.dd.di.DiPackage; import org.eclipse.dd.di.DiagramElement; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.common.util.ECollections; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.EMap; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EFactory; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.BasicFeatureMap; import org.eclipse.emf.ecore.util.EObjectWithInverseEList; import org.eclipse.emf.ecore.util.ExtendedMetaData; import org.eclipse.emf.ecore.util.FeatureMap; import org.eclipse.emf.ecore.util.FeatureMapUtil; 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.ecore.xmi.XMLSave; import org.eclipse.emf.ecore.xmi.impl.ElementHandlerImpl; import org.eclipse.emf.ecore.xmi.impl.XMLLoadImpl; import org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl; import org.eclipse.emf.ecore.xmi.impl.XMLString; import org.eclipse.emf.ecore.xml.type.XMLTypePackage; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Shell; import org.eclipse.wst.wsdl.Definition; import org.eclipse.wst.wsdl.PortType; import org.eclipse.wst.wsdl.util.WSDLResourceImpl; import org.xml.sax.helpers.DefaultHandler; /** * <!-- begin-user-doc --> The <b>Resource </b> associated with the package. * * @implements Bpmn2Resource <!-- end-user-doc --> * @see org.eclipse.bpmn2.util.Bpmn2ResourceFactoryImpl */ public class Bpmn2ModelerResourceImpl extends Bpmn2ResourceImpl { public static final String BPMN2_CONTENT_TYPE_ID = "org.eclipse.bpmn2.content-type.xml"; //$NON-NLS-1$ protected BpmnXmlHelper xmlHelper; protected QNameURIHandler uriHandler; public HashMap xmlNameToFeatureMap = new HashMap(); protected static HashSet<EStructuralFeature> qnameMap = new HashSet<EStructuralFeature>(); static { qnameMap.add(Bpmn2Package.eINSTANCE.getExtension_Definition()); qnameMap.add(Bpmn2Package.eINSTANCE.getRelationship_Sources()); qnameMap.add(Bpmn2Package.eINSTANCE.getRelationship_Targets()); qnameMap.add(Bpmn2Package.eINSTANCE.getAssociation_SourceRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getAssociation_TargetRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getGroup_CategoryValueRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getCorrelationKey_CorrelationPropertyRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getCorrelationProperty_Type()); qnameMap.add(Bpmn2Package.eINSTANCE.getCorrelationPropertyBinding_CorrelationPropertyRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getCorrelationPropertyRetrievalExpression_MessageRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getCorrelationSubscription_CorrelationPropertyBinding()); qnameMap.add(Bpmn2Package.eINSTANCE.getError_StructureRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getEscalation_StructureRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getFlowElement_CategoryValueRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getFlowNode_Incoming()); qnameMap.add(Bpmn2Package.eINSTANCE.getFlowNode_Outgoing()); qnameMap.add(Bpmn2Package.eINSTANCE.getFormalExpression_EvaluatesToTypeRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getInputOutputBinding_OperationRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getItemDefinition_StructureRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getMessage_ItemRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getResourceParameter_Type()); qnameMap.add(Bpmn2Package.eINSTANCE.getInterface_ImplementationRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getOperation_InMessageRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getOperation_OutMessageRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getOperation_ErrorRefs()); qnameMap.add(Bpmn2Package.eINSTANCE.getOperation_ImplementationRef()); // qnameMap.add(Bpmn2Package.eINSTANCE.getCallConversation_CalledCollaborationRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getConversationAssociation_InnerConversationNodeRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getConversationAssociation_OuterConversationNodeRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getConversationLink_SourceRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getConversationLink_TargetRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getConversationNode_MessageFlowRefs()); qnameMap.add(Bpmn2Package.eINSTANCE.getConversationNode_ParticipantRefs()); qnameMap.add(Bpmn2Package.eINSTANCE.getConversationNode_CorrelationKeys()); qnameMap.add(Bpmn2Package.eINSTANCE.getMessageFlow_SourceRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getMessageFlow_TargetRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getMessageFlow_MessageRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getMessageFlowAssociation_InnerMessageFlowRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getMessageFlowAssociation_OuterMessageFlowRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getParticipant_InterfaceRefs()); qnameMap.add(Bpmn2Package.eINSTANCE.getParticipant_EndPointRefs()); qnameMap.add(Bpmn2Package.eINSTANCE.getParticipant_ProcessRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getParticipantAssociation_InnerParticipantRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getParticipantAssociation_OuterParticipantRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getCallableElement_SupportedInterfaceRefs()); // qnameMap.add(Bpmn2Package.eINSTANCE.getCallActivity_CalledElementRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getMultiInstanceLoopCharacteristics_LoopDataInputRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getMultiInstanceLoopCharacteristics_LoopDataOutputRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getMultiInstanceLoopCharacteristics_OneBehaviorEventRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getMultiInstanceLoopCharacteristics_NoneBehaviorEventRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getReceiveTask_MessageRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getReceiveTask_OperationRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getResourceRole_ResourceRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getSendTask_MessageRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getSendTask_OperationRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getServiceTask_OperationRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getItemAwareElement_ItemSubjectRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getBoundaryEvent_AttachedToRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getCatchEvent_EventDefinitionRefs()); qnameMap.add(Bpmn2Package.eINSTANCE.getCompensateEventDefinition_ActivityRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getErrorEventDefinition_ErrorRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getEscalationEventDefinition_EscalationRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getLinkEventDefinition_Source()); qnameMap.add(Bpmn2Package.eINSTANCE.getLinkEventDefinition_Target()); qnameMap.add(Bpmn2Package.eINSTANCE.getMessageEventDefinition_OperationRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getMessageEventDefinition_MessageRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getSignal_StructureRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getSignalEventDefinition_SignalRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getThrowEvent_EventDefinitionRefs()); qnameMap.add(Bpmn2Package.eINSTANCE.getProcess_Supports()); qnameMap.add(Bpmn2Package.eINSTANCE.getProcess_DefinitionalCollaborationRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getLane_PartitionElementRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getGlobalChoreographyTask_InitiatingParticipantRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getChoreographyActivity_ParticipantRefs()); qnameMap.add(Bpmn2Package.eINSTANCE.getChoreographyActivity_InitiatingParticipantRef()); qnameMap.add(Bpmn2Package.eINSTANCE.getChoreographyTask_MessageFlowRef()); // qnameMap.add(Bpmn2Package.eINSTANCE.getCallChoreography_CalledChoreographyRef()); qnameMap.add(BpmnDiPackage.eINSTANCE.getBPMNPlane_BpmnElement()); qnameMap.add(BpmnDiPackage.eINSTANCE.getBPMNShape_BpmnElement()); qnameMap.add(BpmnDiPackage.eINSTANCE.getBPMNShape_ChoreographyActivityShape()); qnameMap.add(BpmnDiPackage.eINSTANCE.getBPMNEdge_BpmnElement()); qnameMap.add(BpmnDiPackage.eINSTANCE.getBPMNEdge_SourceElement()); qnameMap.add(BpmnDiPackage.eINSTANCE.getBPMNEdge_TargetElement()); qnameMap.add(BpmnDiPackage.eINSTANCE.getBPMNLabel_LabelStyle()); } public class OnlyNamedContainmentTypeInfo extends OnlyContainmentTypeInfo { public boolean shouldSaveType(EClass objectType, EClassifier featureType, EStructuralFeature feature) { String name = ExtendedMetaData.INSTANCE.getName(objectType); if (name==null || name.isEmpty()) return false; return super.shouldSaveType(objectType, featureType, feature); } public boolean shouldSaveType(EClass objectType, EClass featureType, EStructuralFeature feature) { String name = ExtendedMetaData.INSTANCE.getName(objectType); if (name==null || name.isEmpty()) return false; return super.shouldSaveType(objectType, featureType, feature); } } /** * Creates an instance of the resource. * * @param uri * the URI of the new resource. */ public Bpmn2ModelerResourceImpl(URI uri) { super(uri); // override helper and uri handler in options map this.xmlHelper = (BpmnXmlHelper)createXMLHelper(); this.uriHandler = new FragmentQNameURIHandler(xmlHelper); uriHandler.setBaseURI(uri); this.getDefaultLoadOptions().put(XMLResource.OPTION_URI_HANDLER, uriHandler); this.getDefaultLoadOptions().put(XMLResource.OPTION_DEFER_IDREF_RESOLUTION, true); this.getDefaultLoadOptions().put(XMLResource.OPTION_DISABLE_NOTIFY, true); this.getDefaultSaveOptions().put(XMLResource.OPTION_URI_HANDLER, uriHandler); ExtendedMetaData extendedMetadata = new XmlExtendedMetadata(); this.getDefaultSaveOptions().put(XMLResource.OPTION_EXTENDED_META_DATA, extendedMetadata); this.getDefaultLoadOptions().put(XMLResource.OPTION_EXTENDED_META_DATA, extendedMetadata); this.getDefaultSaveOptions().put(XMLResource.OPTION_SAVE_TYPE_INFORMATION, new OnlyNamedContainmentTypeInfo()); this.getDefaultSaveOptions().put(XMLResource.OPTION_USE_ENCODED_ATTRIBUTE_STYLE, Boolean.TRUE); this.getDefaultLoadOptions().put(XMLResource.OPTION_USE_LEXICAL_HANDLER, Boolean.TRUE); this.getDefaultSaveOptions().put(XMLResource.OPTION_ELEMENT_HANDLER, new ElementHandlerImpl(true)); this.getDefaultSaveOptions().put(XMLResource.OPTION_ENCODING, "UTF-8"); //$NON-NLS-1$ this.getDefaultSaveOptions().put(XMLResource.OPTION_USE_CACHED_LOOKUP_TABLE, new ArrayList<Object>()); // some interesting things to play with: // this.getDefaultLoadOptions().put(XMLResource.OPTION_LAX_FEATURE_PROCESSING, true); // this.getDefaultLoadOptions().put(XMLResource.OPTION_LAX_WILDCARD_PROCESSING, true); // this.getDefaultLoadOptions().put(XMLResource.OPTION_RECORD_UNKNOWN_FEATURE, true); // this.getDefaultLoadOptions().put(XMLResource.OPTION_ANY_TYPE, BpmnDiPackage.eINSTANCE.getBPMNPlane()); this.getDefaultLoadOptions().put(XMLResource.OPTION_USE_XML_NAME_TO_FEATURE_MAP, xmlNameToFeatureMap); // only necessary if this resource will not be added to a ResourceSet instantly this.eAdapters().add(oppositeReferenceAdapter); } @Override public void load(Map<?, ?> options) throws IOException { try { Bpmn2ModelerFactory.lock(); Bpmn2ModelerFactory.setEnableModelExtensions(false); super.load(options); } finally { Bpmn2ModelerFactory.setEnableModelExtensions(true); Bpmn2ModelerFactory.setResource(null); Bpmn2ModelerFactory.unlock(); } } public void save(Map<?, ?> options) throws IOException { uriHandler.setBaseURI(getURI()); xmlHelper.setResource(this); super.save(options); } @Override protected XMLHelper createXMLHelper() { if (xmlHelper!=null) return xmlHelper; return new Bpmn2ModelerXmlHelper(this); } /** * Override this method to hook in our own XmlHandler */ @Override protected XMLLoad createXMLLoad() { XMLLoad xmlLoad = new XMLLoadImpl(createXMLHelper()) { Bpmn2ModelerXmlHandler handler; @Override protected DefaultHandler makeDefaultHandler() { handler = new Bpmn2ModelerXmlHandler(resource, helper, options); return handler; } @Override public void load(XMLResource resource, InputStream inputStream, Map<?, ?> options) throws IOException { try { super.load(resource, inputStream, options); } catch (Exception e) { e.printStackTrace(); BPMNDiagnostic error = new BPMNDiagnostic(e.getMessage()); error.setLine(handler.getLineNumber()); error.setColumn(handler.getColumnNumber()); error.setLocation(handler.getLocation()); if (!resource.getErrors().contains(error)) resource.getErrors().add(error); throw new IOException(e); } } }; return xmlLoad; } static class BPMNDiagnostic implements Resource.Diagnostic { private String message; private String location; private int column; private int line; public BPMNDiagnostic(String message) { this.message = message; } public void setLocation(String location) { this.location = location; } public String getLocation() { return location; } public void setColumn(int column) { this.column = column;; } public int getColumn() { return column; } public void setLine(int line) { this.line = line; } public int getLine() { return line; } public String getMessage() { return message; } @Override public boolean equals(Object obj) { if (obj instanceof BPMNDiagnostic) { BPMNDiagnostic that = (BPMNDiagnostic)obj; return (this.message!=null && this.message.equals(that.message)) && this.line==that.line && this.column==that.column; } else if (obj instanceof Exception) { String message = ((Exception)obj).getMessage(); return message.equals(this.message); } return super.equals(obj); } } @Override protected XMLSave createXMLSave() { prepareSave(); return new Bpmn2ModelerXMLSave(createXMLHelper()) { }; } @Override protected void prepareSave() { EObject cur; // Definitions definitions = ImportHelper.getDefinitions(this); for (Iterator<EObject> iter = getAllContents(); iter.hasNext();) { cur = iter.next(); setDefaultId(cur); // for (EObject referenced : cur.eCrossReferences()) { // setDefaultId(referenced); // if (definitions != null) { // Resource refResource = referenced.eResource(); // if (refResource != null && refResource != this) { // createImportIfNecessary(definitions, refResource); // } // } // } } } /** * Generate an ID attribute for the given BPMN2 element if not already set. * * @param obj * The object whose ID should be set. */ private void setDefaultId(EObject obj) { if (obj.eClass() != null) { EStructuralFeature idAttr = obj.eClass().getEIDAttribute(); if (idAttr != null && !obj.eIsSet(idAttr)) { obj.eSetDeliver(false); ModelUtil.setID(obj); obj.eSetDeliver(true); } } } @Override public void setURI(URI uri) { super.setURI(uri); xmlHelper.setResource(this); uriHandler.setBaseURI(uri); } /** * We need to extend the standard SAXXMLHandler to hook into the handling of * attribute references which may be either simple ID Strings or QNames. * We'll search through all of the objects' IDs first to find the one we're * looking for. If not, we'll try a QName search. */ protected static class Bpmn2ModelerXmlHandler extends BpmnXmlHandler { protected Bpmn2Preferences preferences = null; protected ImportUtil importHandler = new ImportUtil(); protected String targetNamespace = null; public Bpmn2ModelerXmlHandler(XMLResource xmiResource, XMLHelper helper, Map<?, ?> options) { super(xmiResource, helper, options); } /** * Subclasses should override this to provide their own namespace URI. * Default is to use that "targetNamespace" attribute in the <definitions> element. * @param defs * @return */ protected String getTargetNamespace(Definitions defs) { return defs.getTargetNamespace(); } @Override public void startDocument() { Bpmn2ModelerFactory.setResource(xmlResource); preferences = Bpmn2Preferences.getInstance(xmlResource); super.startDocument(); } @Override public void endDocument() { super.endDocument(); // Make sure there's a namespace prefix definition for typeLanguage. // We'll need that prefix to qualify data types defined in the type language. Definitions definitions = ModelUtil.getDefinitions(xmlResource); String typeLanguage = definitions.getTypeLanguage(); if (typeLanguage!=null) { String prefix = NamespaceUtil.getPrefixForNamespace(helper.getResource(), typeLanguage); if (prefix==null) { TargetRuntime rt = TargetRuntime.getRuntime(xmlResource); TypeLanguageDescriptor tld = rt.getTypeLanguageDescriptor(typeLanguage); if (tld!=null) prefix = tld.getPrefix(); NamespaceUtil.addNamespace(helper.getResource(), prefix, typeLanguage); } } // Load all of the Imports and generate Interfaces, Operations, Messages, Faults and ItemDefinitions for (Import imp : definitions.getImports()) { try { Object importObject = importHandler.loadImport(imp); if (importObject!=null) { importHandler.addImportObjects(imp, importObject); } } catch (Exception e) { BPMNDiagnostic error = new BPMNDiagnostic(e.getMessage()); error.setLine(getLineNumber()); error.setColumn(getColumnNumber()); error.setLocation(getLocation()); xmlResource.getErrors().add(error); } } // Fix up the descriptions for BPMNDiagrams for (BPMNDiagram d : definitions.getDiagrams()) { // Set the diagram's description AFTER it has been completely populated ExtendedPropertiesAdapter epa = ExtendedPropertiesAdapter.adapt(d); if (epa!=null) { epa.setProperty(ExtendedPropertiesAdapter.LONG_DESCRIPTION, epa.getDescription(d)); } } } @Override protected void handleForwardReferences(boolean isEndDocument) { if (isEndDocument) { List<SingleReference> resolved = new ArrayList<SingleReference>(); for (SingleReference ref : forwardSingleReferences) { EObject referencedObject = null; try { referencedObject = xmlResource.getEObject((String) ref.getValue()); } catch (RuntimeException exception) {} if (referencedObject==null) { // The forward reference may be in an external document. // Check the reference type and its owner, then search // external documents contained in the same project. EObject referencingObject = ref.getObject(); EStructuralFeature referencingFeature = ref.getFeature(); String id = (String) ref.getValue(); referencedObject = importHandler.resolveExternalReference(referencingObject, referencingFeature, id); if (referencedObject != null) { resolved.add(ref); if (shouldResolveExternals(referencingObject, referencedObject, id)) { importHandler.addImport(xmlResource, ModelUtil.getDefinitions(referencedObject)); xmlResource.getResourceSet().getResources().add(referencedObject.eResource()); setFeatureValue(referencingObject, referencingFeature, referencedObject, ref.getPosition()); } } } } if (!resolved.isEmpty()) forwardSingleReferences.removeAll(resolved); } super.handleForwardReferences(isEndDocument); } /** * Check if the loader should allow external references to other files * in the project. This is controlled by a User Preference setting. * * @param referencingObject the object referencing an external object. * @param referencedObject the referenced object contained in another * file in the project. * @param id the ID string of the referenced object. * @return true if externals should be resolved, false to cause an error * for missing references. */ private boolean shouldResolveExternals(EObject referencingObject, EObject referencedObject, String id) { if (preferences.getResolveExternals()==0) return false; if (preferences.getResolveExternals()==1) return true; try { Resource resource = referencedObject.eResource(); IPath path = new Path(resource.getURI().toPlatformString(true)); boolean doit = MessageDialog.openQuestion(new Shell(), Messages.Bpmn2ModelerResourceSetImpl_External_Reference_Found_Title, NLS.bind( Messages.Bpmn2ModelerResourceSetImpl_External_Reference_Found_Message, new Object[] { referencedObject.eClass().getName(), id, ModelUtil.getLabel(referencingObject)+" \""+ModelUtil.getName((BaseElement)referencingObject)+"\"", //$NON-NLS-1$ //$NON-NLS-2$ path.toString()} ) ); return doit; } catch (Exception e) {} return true; } @Override protected EStructuralFeature getFeature(EObject object, String prefix, String name, boolean isElement) { EStructuralFeature feature = null; String nsURI = (prefix==null ? targetNamespace : helper.getURI(prefix)); ModelDecorator md = ModelDecorator.getModelDecorator(nsURI); if (md!=null) { feature = md.getEStructuralFeature(object, name); if (feature!=null) { // System.out.println("found feature "+object.eClass().getName()+"."+name+" in pkg "+pkg.getName()); return feature; } } feature = super.getFeature(object, prefix, name, isElement); // if (feature!=null) // System.out.println("found feature "+object.eClass().getName()+"."+name); // else // System.out.println("feature "+prefix+":"+name+" not found!!!"); return feature; } @Override protected EObject createObjectFromFeatureType(EObject peekObject, EStructuralFeature feature) { int lineNumber = getLineNumber(); EObject newObject = null; String nsURI = ExtendedMetaData.INSTANCE.getNamespace(feature); EPackage pkg = ModelDecorator.getEPackage(nsURI); if (pkg!=null) { EClassifier eType = feature.getEType(); newObject = pkg.getEFactoryInstance().create((EClass)eType); // ModelDecoratorAdapter mda = AdapterUtil.adapt(pkg, ModelDecoratorAdapter.class); // feature = mda.getEStructuralFeature(peekObject, feature); if (feature!=null) { ExtendedPropertiesAdapter epa = ExtendedPropertiesAdapter.adapt(peekObject); if (epa!=null) { epa.getFeatureDescriptor(feature).setValue(newObject); processObject(newObject); handleObjectAttribs(newObject); } } } else { newObject = super.createObjectFromFeatureType(peekObject, feature); } if (newObject!=null) { ExtendedPropertiesAdapter epa = ExtendedPropertiesAdapter.adapt(newObject); if (epa!=null) { epa.setProperty(ExtendedPropertiesAdapter.LINE_NUMBER, lineNumber); } } return newObject; } protected EObject validateCreateObjectFromFactory(EFactory factory, String typeName, EObject newObject, EStructuralFeature feature) { if (newObject != null) { TargetRuntime rt = TargetRuntime.getRuntime(xmlResource); String id = rt.getCustomTaskId(newObject); if (id!=null) { // if this is a CustomElement we need to discard this object and construct it // properly such that all extension attributes and elements are created and // initialized. newObject = Bpmn2ModelerFactory.create(xmlResource, newObject.eClass(), new KeyValue(GraphitiConstants.CUSTOM_ELEMENT_ID, id)); handleObjectAttribs(newObject); } } return super.validateCreateObjectFromFactory(factory, typeName, newObject, feature); } @Override protected void setFeatureValue(EObject object, EStructuralFeature feature, Object value, int position) { ModelDecorator md = ModelDecorator.getModelDecorator(feature); if (md!=null) { EStructuralFeature f = md.getEStructuralFeature(object, feature.getName()); if (f!=null) { ExtendedPropertiesAdapter epa = ExtendedPropertiesAdapter.adapt(object); epa.getFeatureDescriptor(f).setValue(value, position); return; } // ExtendedPropertiesAdapter epa = ExtendedPropertiesAdapter.adapt(object); // epa.getFeatureDescriptor(feature).setValue(value, position); } super.setFeatureValue(object, feature, value, position); } @Override protected void handleObjectAttribs(EObject obj) { if (obj instanceof BPMNShape) { // Enable immediate ID resolution because we need to link up // the BPMNShape's "bpmnElement" reference. This is required // by the Bpmn2Preferences so it can determine how to handle // certain DI attributes, e.g. isHorizontal, isExpanded, etc. deferIDREFResolution = false; } super.handleObjectAttribs(obj); String xmlns = null; String tns = null; if (attribs != null) { for (int i = 0, size = attribs.getLength(); i < size; ++i) { String name = attribs.getQName(i); if (name.equals(XMLResource.XML_NS)) { // create an ns prefix in the prefix map for this default namespace // and qualify any qnameFeatures contained in this object... xmlns = attribs.getValue(i); for (EStructuralFeature f : obj.eClass().getEAllStructuralFeatures()) { if (qnameMap.contains(f)) { Object value = obj.eGet(f); if (ModelUtil.isStringWrapper(value)) { String localpart = ModelUtil.getStringWrapperValue(value); if (localpart!=null && !localpart.isEmpty() && !localpart.contains(":")) { //$NON-NLS-1$ String prefix = helper.getPrefix(xmlns); if (prefix==null || prefix.isEmpty()) { for (int index = 0; true; ++index) { prefix = "ns" + index; //$NON-NLS-1$ String ns = helper.getPrefixToNamespaceMap().get(prefix); if (ns==null) break; } helper.addPrefix(prefix, xmlns); } ModelUtil.setStringWrapperValue(value, prefix + ":" + localpart); //$NON-NLS-1$ } } } } } else if (name.startsWith(XMLResource.XML_NS+":")) { //$NON-NLS-1$ String prefix = name.substring(name.indexOf(':')+1); String namespace = attribs.getValue(i); NamespaceUtil.addNamespace(xmlResource, prefix, namespace); } else if (name.equals("tns")) { tns = attribs.getValue(i); } } } if (obj instanceof BPMNShape) { // Defer ID resolution until after resource is loaded. // See above. deferIDREFResolution = true; BPMNShape bpmnShape = (BPMNShape)obj; Hashtable<String,String> map = new Hashtable<String,String>(); if (attribs != null) { for (int i = 0, size = attribs.getLength(); i < size; ++i) { String key = attribs.getQName(i); String value = attribs.getValue(i); map.put(key, value); } preferences.applyBPMNDIDefaults(bpmnShape, map); } } else if (obj instanceof ItemDefinition) { ItemDefinition itemDef = (ItemDefinition)obj; Definitions definitions = ModelUtil.getDefinitions(xmlResource); URI referencingURI = ImportHelper.makeURICanonical(resourceURI); String location = ModelUtil.getStringWrapperValue(itemDef.getStructureRef()); if (location!=null) { int i = location.indexOf("$"); //$NON-NLS-1$ if (i>0) location = location.substring(0,i); URI uri = URI.createURI(location).resolve(referencingURI); uri = uri.trimFragment(); Import imp = ImportHelper.findImportForLocation(definitions, uri); itemDef.setImport(imp); } } /* it's already too late at this point to identify targetNamespace - see createDocumentRoot() else if (obj instanceof Definitions) { // Fetch the targetNamespace from Definitions object: // this is where we allow the TargetRuntime extension to provide // its own targetNamespace if the file doesn't define one. // The targetNamespace determines the TargetRuntime and therefore // how the file is handled, since the TargetRuntime may have special // processing that needs to be done during loading. Therefore, // it's important that we have a targetNamespace after the <definitions> // element is parsed, or the remainder of the file may not be processed // correctly. targetNamespace = getTargetNamespace((Definitions)obj); // if targetNamespace is not provided, use "tns" from file if (targetNamespace==null && tns!=null && !tns.isEmpty()) targetNamespace = tns; // if that didn't work, try "xmlns" if (targetNamespace==null && xmlns!=null && !xmlns.isEmpty()) targetNamespace = xmlns; // if that still didn't work use the default namespace if (targetNamespace==null) targetNamespace = TargetRuntime.getDefaultRuntime().getRuntimeExtension().getTargetNamespace(null); ((Bpmn2ModelerXmlHelper)helper).initializeTargetRuntime(targetNamespace); } */ } protected EObject createDocumentRoot(String prefix, String uri, String name, EFactory eFactory, boolean top) { // Fetch the targetNamespace from DocumentRoot (<definitions>) object: // this is where we allow the TargetRuntime extension to provide // its own targetNamespace if the file doesn't define one, by overriding // the Bpmn2ModelerXmlHandler#getTargetNamespace(Definitions) method. // The targetNamespace determines the TargetRuntime and therefore // how the file is handled, since the TargetRuntime may have special // processing that needs to be done during loading. Therefore, // it's important that we have a targetNamespace after the <definitions> // element is parsed, or the remainder of the file may not be processed // correctly. // // TargetNamespace identification needs to happen very early in the resource // load process, sometime before the first EObject is created by Bpmn2ModelerFactory Definitions definitions = Bpmn2Factory.eINSTANCE.createDefinitions(); String tns = null; String xmlns = null; for (int i = 0, size = attribs.getLength(); i < size; ++i) { String attrib = attribs.getQName(i); if (attrib.equals("tns")) { tns = attribs.getValue(i); } else if (attrib.equals("targetNamespace")) { targetNamespace = attribs.getValue(i); definitions.setTargetNamespace(targetNamespace); } else if (attrib.equals(XMLResource.XML_NS)) { xmlns = attribs.getValue(i); } } targetNamespace = getTargetNamespace(definitions); // if targetNamespace is not provided, use "tns" from file if (targetNamespace==null && tns!=null && !tns.isEmpty()) targetNamespace = tns; // if that didn't work, try "xmlns" if (targetNamespace==null && xmlns!=null && !xmlns.isEmpty()) targetNamespace = xmlns; // if that still didn't work use the default namespace if (targetNamespace==null) targetNamespace = TargetRuntime.getDefaultRuntime().getRuntimeExtension().getTargetNamespace(null); ((Bpmn2ModelerXmlHelper)helper).initializeTargetRuntime(targetNamespace); return super.createDocumentRoot(prefix, uri, name, eFactory, top); } /** * Overridden to be able to convert ID references in attributes to URIs * during load. If the reference can't be found by its ID, we'll try a * QName search (done in the super class) * * @param ids * In our case the parameter will contain exactly one ID that * we resolve to URI. */ @Override protected void setValueFromId(EObject object, EReference eReference, String ids) { Object value = null; // Handle QNames and arbitrary strings in BPMN2 element references if ( qnameMap.contains(eReference) ) { // This reference might be a QName (according to the BPMN2 spec!) // or it might not, in the case of some jBPM Java type references. int i = ids.indexOf(":"); //$NON-NLS-1$ if (i>0) { // if the ID string is a QName, try to resolve and load the object String prefix = ids.substring(0,i); String localname = ids.substring(i+1); String namespace = helper.getNamespaceURI(prefix); if (namespace!=null && namespace.equals(targetNamespace)) { // namespace for prefix is the same as targetNamespace // so remove the prefix to avoid confusing the XMLHandler ids = localname; } else { // this thing is in another namespace, possibly in an external document Import imp = importHandler.findImportForNamespace(helper.getResource(), namespace); if (imp!=null) { value = importHandler.getObjectForId(imp, object, eReference, ids); } } } if (value==null) { // we can't find the object in any of our imports, // so create a string wrapper EObject for this thing value = ModelUtil.createStringWrapper(ids); } if (value!=null && eReference.getEType().isInstance(value)) { try { if (eReference.isMany()) { ((EList)object.eGet(eReference)).add(value); } else { object.eSet(eReference, value); } return; } catch (Exception e) { String msg = NLS.bind( Messages.Bpmn2ModelerResourceImpl_Invalid_Reference, new Object[] {object, eReference, value}); IStatus s = new Status(Status.ERROR, Activator.PLUGIN_ID, msg, e); Activator.getDefault().logStatus(s); } } } // And yet another hack to deal with files generated by Savara: // Savara creates some object references as QNames and, while the // "Official" BPMN 2.0 xsd says these SHOULD be QNames, the bpmn2 EMF // model has these defined as "resolveProxies=false" which means they // will NOT get resolved. if (/*!eReference.isResolveProxies() && */ids.contains(":")) { //$NON-NLS-1$ // Resolve QNames here: if they are internal objects, // simply replace the ID string with the URI fragment. // If they are not internal objects, then there's a // problem with the file! String resolvedId = ((QNameURIHandler) uriHandler).convertQNameToUri(ids); URI resolvedURI = URI.createURI(resolvedId); URI resourceURI = xmlResource.getURI(); if (resolvedURI.trimFragment().equals(resourceURI)) ids = resolvedURI.fragment(); } super.setValueFromId(object, eReference, ids); } public int getLineNumber() { return super.getLineNumber(); } public int getColumnNumber() { return super.getColumnNumber(); } public String getLocation() { return super.getLocation(); } @Override public void startElement(String uri, String localName, String name) { super.startElement(uri, localName, name); EObject peekObject = objects.peekEObject(); // if (peekObject instanceof Definitions) { // // create all model extensions for this target runtime so that all // // extension attributes and elements are available during loading. // TargetRuntime rt = TargetRuntime.getCurrentRuntime(); // rt.createModelExtensions(peekObject.eResource()); // } if (peekObject!=null && peekObject.getClass()==Expression.class) { // If the element is an Expression (not a FormalExpression) then use the CDATA // as the body of a Formal Expression (because Expression does not have a body) text = new StringBuffer(); } } @Override public void endElement(String uri, String localName, String name) { EObject peekObject = objects.peek(); if (peekObject!=null && peekObject.getClass()==Expression.class) { // if the element is an Expression, replace it with a FormalExpression and set // its body using the CDATA of the Expression element. FormalExpression fe = Bpmn2Factory.eINSTANCE.createFormalExpression(); EObject owner = peekObject.eContainer(); if (owner!=null) { for (EStructuralFeature f : owner.eClass().getEAllStructuralFeatures()) { if (owner.eGet(f) == peekObject) { owner.eSet(f, fe); break; } } objects.pop(); objects.push(fe); types.pop(); types.push(Bpmn2Package.eINSTANCE.getFormalExpression_Body()); EStructuralFeature mixedFeature = extendedMetaData.getMixedFeature(fe.eClass()); if (mixedFeature != null) { mixedTargets.push((FeatureMap)fe.eGet(mixedFeature)); } else { mixedTargets.push(null); } } } super.endElement(uri, localName, name); TargetRuntime rt = TargetRuntime.getRuntime(xmlResource); LifecycleEvent.notify(new LifecycleEvent(EventType.BUSINESSOBJECT_LOADED, peekObject, rt)); } } public class Bpmn2ModelerXMLSave extends XMLSaveImpl { protected float minX = Float.MAX_VALUE; protected float minY = Float.MAX_VALUE; protected int lineNum = 1; protected int lineOffset = 0; protected Bpmn2Preferences preferences; @SuppressWarnings("serial") protected class Bpmn2ModelerXMLString extends XMLString { public Bpmn2ModelerXMLString(String publicId, String systemId) { super(Integer.MAX_VALUE, publicId, systemId, null); } @Override public void addAttribute(String name, String value) { // This special little hack removes namespace declarations // and schemaLocation attributes for the XSI namespace if the prefix // is anything other than "xsi". The EMF serializers rely on the fact // that the XSI namespace prefix is ALWAYS "xsi" and they WILL create // a duplicate namespace declaration if one already existed under a // different prefix. This would result a nasty warning from the parser. if (XSI_URI.equals(value) && name.startsWith("xmlns:")) { //$NON-NLS-1$ int i = name.indexOf(":"); //$NON-NLS-1$ String prefix = name.substring(i+1); if (!ExtendedMetaData.XSI_PREFIX.equals(prefix)) return; } if (name.contains(":schemaLocation")) { //$NON-NLS-1$ if (!XSI_SCHEMA_LOCATION.equals(name)) return; } super.addAttribute(name, value); } @Override public void addAttributeNS(String prefix, String localName, String value) { // Same hack as above - see comments. if (XSI_URI.equals(value) && !ExtendedMetaData.XSI_PREFIX.equals(localName)) return; super.addAttributeNS(prefix, localName, value); } @Override public void addLine() { ++lineNum; super.addLine(); lineOffset = getLength(); } public int getLineNum() { return lineNum; } @Override public void addAttributeContent(String content) { // convert to XML string with entities super.addAttributeContent(SyntaxCheckerUtils.toXMLString(content)); } public int getColumnNum() { return getLength() - lineOffset + 1; } }; public Bpmn2ModelerXMLSave(XMLHelper helper) { super(helper); helper.getPrefixToNamespaceMap().clear(); preferences = Bpmn2Preferences.getInstance(helper.getResource()); } @Override protected void addNamespaceDeclarations() { EMap<String,String> map = helper.getPrefixToNamespaceMap(); Definitions definitions = ModelUtil.getDefinitions(helper.getResource()); String typeLanguage = definitions.getTypeLanguage(); if (!map.containsValue(typeLanguage)) { String prefix = "tl"; //$NON-NLS-1$ TargetRuntime rt = TargetRuntime.getRuntime(xmlResource); TypeLanguageDescriptor tld = rt.getTypeLanguageDescriptor(typeLanguage); if (tld!=null) prefix = tld.getPrefix(); doc.addAttributeNS(XMLResource.XML_NS, prefix, typeLanguage); } super.addNamespaceDeclarations(); } @Override protected void init(XMLResource resource, Map<?, ?> options) { super.init(resource, options); featureTable = new Bpmn2ModelerXMLSave.Bpmn2Lookup(map, extendedMetaData, elementHandler); final List<BPMNDiagram> diagrams = getAll(BPMNDiagram.class, resource); for (BPMNDiagram bpmnDiagram : diagrams) { findMinXY(bpmnDiagram); } doc = createXMLString(); if (contents.size()>0 && contents.get(0) instanceof Bpmn2ModelerDocumentRootImpl) { Bpmn2ModelerDocumentRootImpl documentRoot = (Bpmn2ModelerDocumentRootImpl)contents.get(0); Definitions definitions = ModelUtil.getDefinitions(helper.getResource()); try { documentRoot.setDeliver(false); documentRoot.eSetDeliver(false); FeatureMap featureMap = documentRoot.getMixed(); if (featureMap.size()>0) { // insert a comment before the Definitions node String comment = " origin at X="+(minX<0 ? minX : 0)+ //$NON-NLS-1$ " Y="+(minY<0 ? minY : 0)+" "; //$NON-NLS-1$ //$NON-NLS-2$ if (featureMap.get(0).getEStructuralFeature() != XMLTypePackage.Literals.XML_TYPE_DOCUMENT_ROOT__COMMENT) { featureMap.add(0,XMLTypePackage.Literals.XML_TYPE_DOCUMENT_ROOT__COMMENT, comment); } else { FeatureMap.Entry e = FeatureMapUtil.createEntry(XMLTypePackage.Literals.XML_TYPE_DOCUMENT_ROOT__COMMENT, comment); featureMap.set(0, e); } } definitions.eSetDeliver(false); definitions.setExporter(Activator.PLUGIN_ID); String version = Platform.getBundle(Activator.PLUGIN_ID).getHeaders().get("Bundle-Version"); //$NON-NLS-1$ version = version.replaceAll(".qualifier", ""); //$NON-NLS-1$ //$NON-NLS-2$ definitions.setExporterVersion(version); } finally { documentRoot.setDeliver(true); documentRoot.eSetDeliver(true); definitions.eSetDeliver(true); } } } protected XMLString createXMLString() { return new Bpmn2ModelerXMLString(publicId, systemId); } protected Bpmn2ModelerXMLString getXMLString() { if (doc==null) { createXMLString(); } return (Bpmn2ModelerXMLString)doc; } @Override protected void endSave(List<? extends EObject> contents) throws IOException { Bpmn2ModelerDocumentRootImpl documentRoot = null; if (contents.size()>0 && contents.get(0) instanceof Bpmn2ModelerDocumentRootImpl) { documentRoot = (Bpmn2ModelerDocumentRootImpl)contents.get(0); documentRoot.setDeliver(false); documentRoot.eSetDeliver(false); } try { super.endSave(contents); } finally { if (documentRoot!=null) { documentRoot.setDeliver(true); documentRoot.eSetDeliver(true); } } } @Override protected boolean shouldSaveFeature(EObject o, EStructuralFeature f) { if (o instanceof BPMNShape) { BPMNShape s = (BPMNShape)o; if (f==BpmnDiPackage.eINSTANCE.getBPMNShape_IsExpanded()) { if (FeatureSupport.isExpandableElement(s.getBpmnElement())) return true; } if (f==BpmnDiPackage.eINSTANCE.getBPMNShape_IsHorizontal()) { if (s.getBpmnElement() instanceof Lane || s.getBpmnElement() instanceof Participant) return true; } } // we also want to store x and y with value zero, would be skipped because of default value otherwise if (o instanceof Bounds || o instanceof Point) { return true; } if (o instanceof Process && f==Bpmn2Package.eINSTANCE.getProcess_IsExecutable()) return true; if (o instanceof ItemDefinition && f==Bpmn2Package.eINSTANCE.getItemDefinition_IsCollection()) return true; if (o instanceof CompensateEventDefinition && f==Bpmn2Package.eINSTANCE.getCompensateEventDefinition_WaitForCompletion()) return true; // empty Expressions should not be saved if (f!=null && (f.getEType() == Bpmn2Package.eINSTANCE.getExpression() || f.getEType() == Bpmn2Package.eINSTANCE.getFormalExpression())) { Expression expression = (Expression)o.eGet(f); if (expression==null) return false; if (expression instanceof FormalExpression) { FormalExpression formalExpression = (FormalExpression)expression; String body = ModelUtil.getExpressionBody(formalExpression); if (body==null) { return false; } } } if (o!=null && o instanceof Documentation) { Documentation doc = (Documentation)o; if (doc.getText()==null || doc.getText().isEmpty()) return false; } if (f!=null && f.getEType() == Bpmn2Package.eINSTANCE.getDocumentation()) { EList<Documentation> docList = (EList<Documentation>)o.eGet(f); if (docList.isEmpty()) return false; int empty = 0; for (Documentation doc : docList) { if (doc.getText()==null || doc.getText().isEmpty()) ++empty; } if (empty==docList.size()) return false; } // don't serialize the "body" attribute of FormalExpressions because the expression text // is already in the CDATA section of the <bpmn2:expression> element. This would cause // the expression text to be duplicated on deserialization. // Same goes for Documentation.text if (Bpmn2Package.eINSTANCE.getFormalExpression_Body().equals(f) || Bpmn2Package.eINSTANCE.getDocumentation_Text().equals(f)) return false; // don't save Assignments if they are invalid: Assignments must have // both a "from" and "to" expression and they may not be empty strings. if (o instanceof DataAssociation && "assignment".equals(f.getName())) { //$NON-NLS-1$ DataAssociation da = (DataAssociation)o; for (Assignment a : da.getAssignment()) { Expression from = a.getFrom(); if (from instanceof FormalExpression) { String body = ModelUtil.getExpressionBody(((FormalExpression)from)); if (body==null || body.isEmpty()) return false; } Expression to = a.getTo(); if (to instanceof FormalExpression) { String body = ModelUtil.getExpressionBody(((FormalExpression)to)); if (body==null || body.isEmpty()) return false; } } } if (f == Bpmn2Package.eINSTANCE.getBaseElement_ExtensionValues()) { // check if this element is (or should be) empty int entryCount = 0; for (ExtensionAttributeValue ev : (EList<ExtensionAttributeValue>)o.eGet(f)) { BasicFeatureMap map = (BasicFeatureMap) ev.getValue(); Iterator<FeatureMap.Entry> mi = map.iterator(); while (mi.hasNext()) { FeatureMap.Entry entry = mi.next(); Object v = entry.getValue(); boolean entryCounted = false; if (v instanceof EObject) { Iterator<Adapter> ai = ((EObject)v).eAdapters().iterator(); while (ai.hasNext()) { Adapter a = ai.next(); if (a instanceof IExtensionValueAdapter) { if (((IExtensionValueAdapter)a).shouldSaveElement((EObject)v)) { ++entryCount; } entryCounted = true; } } } if (!entryCounted) ++entryCount; } } return entryCount>0; } Iterator<Adapter> ai = o.eAdapters().iterator(); while (ai.hasNext()) { Adapter a = ai.next(); if (a instanceof IExtensionValueAdapter) { if (!((IExtensionValueAdapter)a).shouldSaveFeature(o,f)) return false; } } return super.shouldSaveFeature(o, f); } protected <T> List<T> getAll(Class<T> class1, Resource resource) { ArrayList<T> l = new ArrayList<T>(); TreeIterator<EObject> contents = resource.getAllContents(); for (; contents.hasNext();) { Object t = contents.next(); if (class1.isInstance(t)) { l.add((T) t); } } return l; } protected void findMinXY(BPMNDiagram bpmnDiagram) { EList<DiagramElement> elements = (EList<DiagramElement>) bpmnDiagram.getPlane().getPlaneElement(); for (DiagramElement e : elements) { if (e instanceof BPMNShape) { Bounds b = ((BPMNShape)e).getBounds(); minX = Math.min(minX, b.getX()); minY = Math.min(minY, b.getY()); } else if (e instanceof BPMNEdge) { List<Point> points = ((BPMNEdge)e).getWaypoint(); for (Point p : points) { minX = Math.min(minX, p.getX()); minY = Math.min(minY, p.getY()); } } else if (e instanceof BPMNLabel) { Bounds b = ((BPMNLabel)e).getBounds(); minX = Math.min(minX, b.getX()); minY = Math.min(minY, b.getY()); } } } @Override protected void saveContainedMany(EObject o, EStructuralFeature f) { if (o instanceof BPMNPlane && f==DiPackage.eINSTANCE.getPlane_PlaneElement()) { // Sort the Diagram Elements in ascending Z-order BPMNPlane plane = (BPMNPlane) o; EList<DiagramElement> originalList = new BasicEList<DiagramElement>(); originalList.addAll(plane.getPlaneElement()); plane.eSetDeliver(false); ECollections.sort((EList<DiagramElement>) plane.getPlaneElement(), new DIZorderComparator()); super.saveContainedMany(o, f); ECollections.setEList((EList)plane.getPlaneElement(),originalList); plane.eSetDeliver(true); } else if (o instanceof Definitions && f==Bpmn2Package.eINSTANCE.getDefinitions_RootElements()) { // Sort the Definitions Root Elements to avoid forward references Definitions definitions = (Definitions) o; EList<RootElement> originalList = new BasicEList<RootElement>(); originalList.addAll(definitions.getRootElements()); definitions.eSetDeliver(false); ECollections.sort((EList<RootElement>)definitions.getRootElements(), new RootElementComparator()); super.saveContainedMany(o, f); ECollections.setEList((EList)definitions.getRootElements(),originalList); definitions.eSetDeliver(true); } else { super.saveContainedMany(o, f); } } @Override protected void saveElement(EObject o, EStructuralFeature f) { Iterator<Adapter> ai = o.eAdapters().iterator(); while (ai.hasNext()) { Adapter a = ai.next(); if (a instanceof IExtensionValueAdapter) { if (!((IExtensionValueAdapter)a).shouldSaveElement(o)) return; } } if (o instanceof BPMNLabel) { if (!preferences.getSaveBPMNLabels()) return; } if (o instanceof BPMNLabelStyle) { // is anyone still using this BPMNLabelStyle? // if not, get rid of it. boolean save = false; if (preferences.getSaveBPMNLabels()) { Definitions definitions = ModelUtil.getDefinitions(o); TreeIterator<EObject> iter = definitions.eAllContents(); while (iter.hasNext()) { EObject eo = iter.next(); if (eo instanceof BPMNLabel) { if (((BPMNLabel)eo).getLabelStyle() == o) save = true; } } } if (!save) return; } float oldX = 0, oldY = 0; List<Point> oldPoints = null; if (minX<0 || minY<0) { if (o instanceof BPMNShape) { Bounds b = ((BPMNShape)o).getBounds(); b.eSetDeliver(false); if (minX<0) { oldX = b.getX(); b.setX(oldX - minX); } if (minY<0) { oldY = b.getY(); b.setY(oldY - minY); } } else if (o instanceof BPMNEdge) { List<Point> points = ((BPMNEdge)o).getWaypoint(); oldPoints = new ArrayList<Point>(); for (Point p : points) { p.eSetDeliver(false); Point oldPoint = DcFactory.eINSTANCE.createPoint(); oldPoint.setX(p.getX()); oldPoint.setY(p.getY()); oldPoints.add(oldPoint); if (minX<0) p.setX( p.getX() - minX); if (minY<0) p.setY( p.getY() - minY); } } else if (o instanceof BPMNLabel) { Bounds b = ((BPMNLabel)o).getBounds(); if (b!=null) { b.eSetDeliver(false); if (minX<0) { oldX = b.getX(); b.setX(oldX - minX); } if (minY<0) { oldY = b.getY(); b.setY(oldY - minY); } } } } super.saveElement(o, f); if (minX<0 || minY<0) { if (o instanceof BPMNShape) { Bounds b = ((BPMNShape)o).getBounds(); if (minX<0) { b.setX(oldX); } if (minY<0) { b.setY(oldY); } b.eSetDeliver(true); } else if (o instanceof BPMNEdge) { List<Point> points = ((BPMNEdge)o).getWaypoint(); int index = 0; for (Point p : points) { if (minX<0) p.setX(oldPoints.get(index).getX()); if (minY<0) p.setY(oldPoints.get(index).getY()); p.eSetDeliver(true); ++index; } } else if (o instanceof BPMNLabel) { Bounds b = ((BPMNLabel)o).getBounds(); if (b!=null) { if (minX<0) { b.setX(oldX); } if (minY<0) { b.setY(oldY); } b.eSetDeliver(true); } } } } @Override protected String convertURI(String s) { URI uri = helper.deresolve( URI.createURI(s) ); return uri.toString(); } @Override public void traverse(List<? extends EObject> contents) { for (EObject e : contents) { if (e instanceof Definitions) { List<RootElement> roots = ((Definitions) e).getRootElements(); Process p = null; for (RootElement root : roots) { if (root instanceof Process) { p = (Process) root; } } if (p != null) { ((Definitions) e).getRootElements().remove(p); ((Definitions) e).getRootElements().add((RootElement) p); } } } super.traverse(contents); } public class Bpmn2Lookup extends XMLSaveImpl.Lookup { public Bpmn2Lookup(XMLMap map, ExtendedMetaData extendedMetaData, ElementHandler elementHandler) { super(map, extendedMetaData, elementHandler); } @Override public EStructuralFeature[] getFeatures(EClass cls) { int index = getIndex(cls); EClass c = classes[index]; if (c == cls) { return features[index]; } EStructuralFeature[] featureList = listFeatures(cls); EStructuralFeature[] newFeatureList = featureList; if (c == null) { newFeatureList = reorderFeatureList(cls, featureList); classes[index] = cls; features[index] = newFeatureList; featureKinds[index] = listKinds(newFeatureList); } return newFeatureList; } /** * Specifies the serialization order of features for a given ECLass. * Subclasses should override this behavior. * The default implementation simply returns the original list. * * @param cls - EClass whose features need to be reordered * @param featureList - the original feature list as provided by the Bpmn2Package * @return a feature list that specifies the new ordering */ protected EStructuralFeature[] reorderFeatureList(EClass cls, EStructuralFeature[] featureList) { return featureList; } /** * Change the serialization order of features for a given EClass. The string array "featureNames" * specifies a new ordering for some or all of the features in the feature list; these names must * be contiguous in the original list. For example, given the following original feature list: * * "w" * "x" * "a" * "b" * "c" * "d" * "e" * "y" * "z" * * The featureNames list may not contain this: * * "b" * "a" * "e" * "d" * * because the two sets "b", "a" and "e", "d" are not contiguous. The correct way of specifying this is: * * "b" * "a" * "c" * "e" * "d" * * Alternatively, the client could call this method twice, the first time with the first set ("b" and "a") * and a second time with the second set ("e" and "d). * * @param cls * @param featureList * @param featureNames * @return */ protected EStructuralFeature[] reorderFeatureList(EClass cls, EStructuralFeature[] featureList, String[] featureNames) { // the reordered list of features EStructuralFeature[] newFeatureList = new EStructuralFeature[featureList.length]; // map of old to new array indexes int[] indexMap = new int[featureList.length]; for (int i=0; i<indexMap.length; ++i) indexMap[i] = -1; int startIndex = Integer.MAX_VALUE; for (int i=0; i<featureList.length; ++i) { for (int j=0; j<featureNames.length; ++j) { if (featureList[i].getName().equals(featureNames[j])) { if (i<startIndex) { startIndex = i; break; } } } } for (int newIndex=0; newIndex<featureNames.length; ++newIndex) { String fn = featureNames[newIndex]; for (int oldIndex=0; oldIndex<featureList.length; ++oldIndex) { EStructuralFeature f = featureList[oldIndex]; if (f.getName().equalsIgnoreCase(fn)) { indexMap[oldIndex] = newIndex + startIndex; break; } } } for (int oldIndex=0; oldIndex<featureList.length; ++oldIndex) { EStructuralFeature f = featureList[oldIndex]; int newIndex = indexMap[oldIndex]; if (newIndex>=0) { newFeatureList[newIndex] = featureList[oldIndex]; } else newFeatureList[oldIndex] = featureList[oldIndex]; } return newFeatureList; } } } // TODO check this, is this the correct way to deal with this ID prefixes /** * QName handler to make create URIs out of the fragment, which is the local part of the QName * * Most other tools dont understand QNames in referencing attributes * * @author drobisch * */ public static class FragmentQNameURIHandler extends QNameURIHandler { protected BpmnXmlHelper xmlHelper; public FragmentQNameURIHandler(BpmnXmlHelper xmlHelper) { super(xmlHelper); this.xmlHelper = xmlHelper; } @Override public URI resolve(URI uri) { URI resolvedUri = super.resolve(uri); if (resolvedUri.isRelative()) resolvedUri = resolvedUri.resolve(baseURI); return resolvedUri; } @Override public URI deresolve(URI uri) { String fragment = uri.fragment(); if (fragment != null && !fragment.startsWith("/")) { //$NON-NLS-1$ // return just fragment (i.e. without the '#') but only if local reference URI otherURI = uri.trimFragment(); if (baseURI.equals(otherURI)) return URI.createURI(fragment); } if ( (uri.isPlatformResource() && baseURI.isPlatformResource()) || (uri.isFile() && baseURI.isFile())) { uri = uri.deresolve(baseURI, false, true, true); return uri; } return super.deresolve(uri); } @Override public String convertQNameToUri(String qName) { if (qName.contains("#") || qName.contains("/")) { //$NON-NLS-1$ //$NON-NLS-2$ // We already have an URI and not QName, e.g. URL return qName; } // Split into prefix and local part (fragment) String[] parts = qName.split(":"); //$NON-NLS-1$ String prefix, fragment; if (parts.length == 1) { prefix = null; fragment = qName; } else if (parts.length == 2) { prefix = parts[0]; fragment = parts[1]; } else throw new IllegalArgumentException(Messages.Bpmn2ModelerResourceImpl_Illegal_QName + qName); if (fragment.contains(".")) { //$NON-NLS-1$ // HACK: officially IDs can contain ".", but unfortunately // XmlHandler calls resolve also for xsi:schemaLocation stuff // and similar, that are // NO URIs. We must not process them. return qName; } boolean isTargetNamespacePrefix = false; try { isTargetNamespacePrefix = xmlHelper.isTargetNamespace(prefix); } catch (Exception e) { } if (!isTargetNamespacePrefix) { String uriString = xmlHelper.getPathForPrefix(prefix).appendFragment(fragment).toString(); URI uri = URI.createURI(uriString); ResourceSet rs = ModelUtil.slightlyHackedResourceSet(xmlHelper.getResource().getResourceSet()); Resource r = ((Bpmn2ModelerResourceSetImpl)rs).getResource(uri, true, "wsdl"); // the only problem here... //$NON-NLS-1$ if (r instanceof WSDLResourceImpl) { EObject o = r.getContents().get(0); Definition def = (Definition)o; // if eReference -- operation.implementationref // search all of these: for (PortType pt : (List<PortType>)def.getEPortTypes()) { for (org.eclipse.wst.wsdl.Operation op : (List<org.eclipse.wst.wsdl.Operation>)pt.getEOperations()) { } } // and so on for other eReference bpmn2 types } return xmlHelper.getPathForPrefix(prefix).appendFragment(fragment).toString(); } else { return URI.createURI("").appendFragment(fragment).toString(); //$NON-NLS-1$ } } } public static class Bpmn2ModelerXmlHelper extends BpmnXmlHelper { // List of all EReferences that are defined as type="xsd:QName" in the BPMN 2.0 Schema // This information is not represented in the MDT BPMN2 project metamodel. boolean isQNameFeature = false; ImportUtil importHandler = new ImportUtil(); /** associated target runtime, only used in "load" scenario */ protected TargetRuntime runtime = null; public Bpmn2ModelerXmlHelper(Bpmn2ResourceImpl resource) { super(resource); } /** * initialize the target runtime according to the parsed namespace * * @param targetNamespace */ protected void initializeTargetRuntime(String targetNamespace) { runtime = TargetRuntime.getRuntimeByNamespace(targetNamespace); if (runtime == null) { runtime = TargetRuntime.getDefaultRuntime(); } TargetRuntimeAdapter.adapt(resource, runtime); } @Override public Object getValue(EObject eObject, EStructuralFeature eStructuralFeature) { Object o = super.getValue(eObject, eStructuralFeature); if (qnameMap.contains(eStructuralFeature)) { List<String> prefixes = urisToPrefixes.get(getTargetNamespace()); if (prefixes!=null && prefixes.contains("")) //$NON-NLS-1$ isQNameFeature = false; else isQNameFeature = true; } else isQNameFeature = false; return o; } @Override public String getHREF(EObject obj) { // convert the attribute ID references to a QName String s = null; if (ModelUtil.isStringWrapper(obj)) { s = ModelUtil.getStringWrapperValue(obj); } else s = super.getHREF(obj); if (isQNameFeature) { if (s != null && s.contains("#")) { //$NON-NLS-1$ // object is a reference possibly to another document Import imp = importHandler.findImportForObject(resource, obj); if (imp != null) { return importHandler.getQNameForObject(resource, obj); } } } return s; } public String getIDREF(EObject obj) { // convert the element ID references to a QName String s = null; if (ModelUtil.isStringWrapper(obj)) { s = ModelUtil.getStringWrapperValue(obj); } else { s = super.getIDREF(obj); if (isQNameFeature) { // Don't use targetNamespace prefixes for BPMN2 object ID references // See https://bugzilla.redhat.com/show_bug.cgi?id=1150060 EPackage ePackage = obj.eClass().getEPackage(); if (!Bpmn2Package.class.isInstance(ePackage) && !DiPackage.class.isInstance(ePackage) && !DcPackage.class.isInstance(ePackage)) s = convertToQName(s); } } return s; } /** * Returns the targetNamespace defined in the <definitions> root element. * * @return a namespace URI or null if targetNamespace is not defined. */ private String getTargetNamespace() { Definitions definitions = ModelUtil.getDefinitions(getResource()); if (definitions==null) return null; return definitions.getTargetNamespace(); } /** * Get the namespace prefix for the targetNamespace. * * @return null if the document does not define a targetNamespace, * an empty string if there is a targetNamespace, but no prefix has been * defined for it, or the prefix for the targetNamespace * * Examples: * * <bpmn2:definitions tns="http://eclipse.org/example" targetNamespace="http://eclipse.org/example"/> * return "tns" * * <bpmn2:definitions targetNamespace="http://eclipse.org/example"/> * returns "" * * <bpmn2:definitions tns="http://eclipse.org/example"/> * returns null */ private String getTargetNamespacePrefix() { String targetNamespace = getTargetNamespace(); if (targetNamespace!=null && !targetNamespace.isEmpty()) { String prefix = getPrefix(targetNamespace); if (prefix==null || prefix.isEmpty()) { for (Entry<String, String> e : this.getPrefixToNamespaceMap().entrySet()) { if (targetNamespace.equals(e.getValue()) && !e.getKey().isEmpty()) { return e.getKey(); } } } return ""; //$NON-NLS-1$ } return null; } /** * Converts the NCName "s" to a QName that maps to the targetNamespace * * @param s - a non-colonized name string * @return the string "s" prefixed with the NS prefix for the targetNamespace. */ private String convertToQName(String s) { if (s!=null && !s.contains(":")) { //$NON-NLS-1$ String prefix = getTargetNamespacePrefix(); if (prefix!=null && !prefix.isEmpty()) { s = prefix + ":" + s; //$NON-NLS-1$ } } return s; } public void setValue(EObject object, EStructuralFeature feature, Object value, int position) { // fix some kind of bug which causes duplicate entries in objects that have // mutual reference lists. if ( object!=null && feature!=null && object.eClass()!=null && feature == object.eClass().getEStructuralFeature(feature.getFeatureID()) ) { Object v = object.eGet(feature); if (v instanceof EObjectWithInverseEList) { EObjectWithInverseEList list = (EObjectWithInverseEList)v; if (list.contains(value)) { // it's already in there! return; } } } // // check if we need to change the attribute's data type: // if (feature instanceof EAttribute) { // // and only if the attribute's data type is "Object" // EClassifier t = feature.getEType(); // if (t!=null && t.getInstanceClass() == Object.class) { // // search for the attribute in the target runtime's Custom Task and // // Model Extension definitions by name // List<Property>properties = new ArrayList<Property>(); // TargetRuntime rt = TargetRuntime.getCurrentRuntime(); // String className = object.eClass().getName(); // String featureName = feature.getName(); // for (CustomTaskDescriptor ctd : rt.getCustomTasks()) { // if (className.equals(ctd.getType())) { // properties.addAll(ctd.getProperties()); // } // } // for (ModelExtensionDescriptor med : rt.getModelExtensions()) { // if (className.equals(med.getType())) { // properties.addAll(med.getProperties()); // } // } // for (Property p : properties) { // if (p.name.equals(featureName)) { // String type = p.type; // if (type==null) // type = "EString"; //$NON-NLS-1$ // EClassifier eClassifier = ModelDecorator.findEClassifier( // rt.getModelDescriptor().getEPackage(),type); // if (eClassifier instanceof EDataType) { // feature.setEType(eClassifier); // } // break; // } // } // } // } super.setValue(object, feature, value, position); } @Override public EStructuralFeature getFeature(EClass eClass, String nsURI, String name, boolean isElement) { // This fixes https://bugs.eclipse.org/bugs/show_bug.cgi?id=378296 // I'm still not convinced that getFeature() shouldn't simply return the feature // from the given EClass instead of searching the EPackage of the Resource being // loaded (if the EClass has a feature with that name of course). EStructuralFeature feature = null; EPackage pkg = eClass.getEPackage(); if (pkg != Bpmn2Package.eINSTANCE && pkg != BpmnDiPackage.eINSTANCE && pkg != DcPackage.eINSTANCE && pkg != DiPackage.eINSTANCE && pkg != TargetRuntime.getRuntime(resource).getModelDescriptor().getEPackage()) { feature = eClass.getEStructuralFeature(name); } if (feature==null) feature = super.getFeature(eClass, nsURI, name, isElement); return feature; } } }