blob: 924f30e387582b14b227226ff10d7c622aef3e35 [file] [log] [blame]
/*******************************************************************************
* 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.Bpmn2Factory;
import org.eclipse.bpmn2.Bpmn2Package;
import org.eclipse.bpmn2.DataAssociation;
import org.eclipse.bpmn2.Definitions;
import org.eclipse.bpmn2.Documentation;
import org.eclipse.bpmn2.Expression;
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.RootElement;
import org.eclipse.bpmn2.di.BPMNDiagram;
import org.eclipse.bpmn2.di.BPMNEdge;
import org.eclipse.bpmn2.di.BPMNLabel;
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.adapters.ExtendedPropertiesAdapter;
import org.eclipse.bpmn2.modeler.core.model.Bpmn2ModelerFactory.Bpmn2ModelerDocumentRootImpl;
import org.eclipse.bpmn2.modeler.core.preferences.Bpmn2Preferences;
import org.eclipse.bpmn2.modeler.core.runtime.CustomTaskDescriptor;
import org.eclipse.bpmn2.modeler.core.runtime.ModelExtensionDescriptor;
import org.eclipse.bpmn2.modeler.core.runtime.ModelExtensionDescriptor.Property;
import org.eclipse.bpmn2.modeler.core.runtime.TargetRuntime;
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.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.IStatus;
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.util.BasicEList;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
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.InternalEObject;
import org.eclipse.emf.ecore.impl.BasicEObjectImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
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.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.osgi.util.NLS;
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());
}
/**
* 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 OnlyContainmentTypeInfo());
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);
}
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() {
return 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) {
DiagnosticWrappedException error = new DiagnosticWrappedException(e);
error.setLine(handler.getLineNumber());
error.setColumn(handler.getColumnNumber());
error.setLocation(handler.getLocation());
resource.getErrors().add(error);
throw new IOException(e);
}
}
};
}
class DiagnosticWrappedException extends WrappedException implements Resource.Diagnostic {
private static final long serialVersionUID = 1L;
private String location;
private int column;
private int line;
public DiagnosticWrappedException(Exception exception) {
super(exception);
}
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;
}
}
@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 {
Bpmn2Preferences prefs = null;
ImportUtil importHandler = new ImportUtil();
public Bpmn2ModelerXmlHandler(XMLResource xmiResource, XMLHelper helper, Map<?, ?> options) {
super(xmiResource, helper, options);
}
@Override
public void startDocument() {
super.startDocument();
Bpmn2ModelerFactory.setEnableModelExtensions(false);
}
@Override
public void endDocument() {
super.endDocument();
Bpmn2ModelerFactory.setEnableModelExtensions(true);
}
@Override
protected void createObject(EObject peekObject, EStructuralFeature feature) {
super.createObject(peekObject, feature);
EObject newObject = objects.peekEObject();
if (newObject!=null && newObject!=peekObject) {
ExtendedPropertiesAdapter adapter = ExtendedPropertiesAdapter.adapt(newObject);
if (adapter!=null) {
adapter.setProperty(ExtendedPropertiesAdapter.LINE_NUMBER, getLineNumber());
}
}
}
@Override
protected void handleObjectAttribs(EObject obj) {
super.handleObjectAttribs(obj);
if (attribs != null) {
InternalEObject internalEObject = (InternalEObject) obj;
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...
String namespaceURI = 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(namespaceURI);
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, namespaceURI);
}
ModelUtil.setStringWrapperValue(value, prefix + ":" + localpart); //$NON-NLS-1$
}
}
}
}
}
}
}
if (obj instanceof BPMNShape) {
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);
}
Bpmn2Preferences.getInstance(this.resourceURI).applyBPMNDIDefaults(bpmnShape, map);
}
}
else if (obj instanceof ItemDefinition) {
ItemDefinition itemDef = (ItemDefinition)obj;
Definitions definitions = ImportHelper.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);
}
}
}
/**
* 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);
Import imp = importHandler.findImportForNamespace(helper.getResource(), namespace);
if (imp!=null) {
value = importHandler.getObjectForLocalname(imp, object, eReference, localname);
}
}
if (value==null) {
// not a QName or can't find EObject: 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!=null && peekObject.eClass() == Bpmn2Package.eINSTANCE.getExpression()) {
// 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.eClass() == Bpmn2Package.eINSTANCE.getExpression()) {
// 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);
}
}
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;
@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;
}
public int getColumnNum() {
return getLength() - lineOffset + 1;
}
};
public Bpmn2ModelerXMLSave(XMLHelper helper) {
super(helper);
helper.getPrefixToNamespaceMap().clear();
}
@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();
}
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);
}
super.endSave(contents);
if (documentRoot!=null) {
documentRoot.setDeliver(true);
}
}
@Override
protected boolean shouldSaveFeature(EObject o, EStructuralFeature f) {
if (o instanceof BPMNShape && f==BpmnDiPackage.eINSTANCE.getBPMNShape_IsHorizontal()) {
BPMNShape s = (BPMNShape)o;
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;
}
// 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;
}
}
}
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) {
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
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];
}
// System.out.println("Reordered features for "+cls.getName());
// for (int newIndex=0; newIndex<newFeatureList.length; ++newIndex) {
// System.out.println(" "+newIndex+": "+newFeatureList[newIndex].getName()+" was "+featureList[newIndex].getName());
// }
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);
else
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) {
}
String uriString = xmlHelper.getPathForPrefix(prefix).appendFragment(fragment).toString();
if (!isTargetNamespacePrefix) {
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 uriString;
}
}
}
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();
public Bpmn2ModelerXmlHelper(Bpmn2ResourceImpl resource) {
super(resource);
}
@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 = super.getHREF(obj);
if (isQNameFeature) {
if (ModelUtil.isStringWrapper(obj)) {
s = ModelUtil.getStringWrapperValue(obj);
}
else 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) {
String localname = importHandler.getLocalnameForObject(obj);
if (localname!=null) {
String prefix = NamespaceUtil.getPrefixForNamespace(resource, imp.getNamespace());
if (prefix!=null) {
s = prefix + ":" + localname; //$NON-NLS-1$
return s;
}
}
}
}
}
return s;
}
public String getIDREF(EObject obj) {
// convert the element ID references to a QName
String s = super.getIDREF(obj);
if (isQNameFeature && !ModelUtil.isStringWrapper(obj))
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 = ImportHelper.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 = ModelUtil.getEClassifierFromString(
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 namespaceURI, 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 result = null;
EPackage pkg = eClass.getEPackage();
if (pkg != Bpmn2Package.eINSTANCE &&
pkg != BpmnDiPackage.eINSTANCE &&
pkg != DcPackage.eINSTANCE &&
pkg != DiPackage.eINSTANCE &&
pkg != TargetRuntime.getCurrentRuntime().getModelDescriptor().getEPackage()) {
result = eClass.getEStructuralFeature(name);
}
if (result==null)
result = super.getFeature(eClass, namespaceURI, name, isElement);
return result;
}
}
}