Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "BPMN2-Modeler/DeveloperTutorials/AddingCDATA"

m
 
(2 intermediate revisions by the same user not shown)
Line 27: Line 27:
  
 
== Start With The Ecore ==
 
== Start With The Ecore ==
 +
 +
If you have not already done so, you will want to create an EMF model (ecore) and generator file. Note that while you could do this with Java code and dynamic EMF, using an ecore and generated Java classes will make maintaining your model so much easier.
 +
 +
For this tutorial we will use the MyModel.ecore from the CustomTask example.
 +
 +
=== Create the EClass ===
  
 
Open the MyModel.ecore with the EMF Ecore editor and create a new EClass called MetaData.
 
Open the MyModel.ecore with the EMF Ecore editor and create a new EClass called MetaData.
Line 41: Line 47:
  
 
This will define the "metaData" element as having [http://www.w3.org/TR/REC-xml/#sec-mixed-content mixed content]. Add another Details entry and set Key="namespace" and Value="##targetNamespace". This declares that the "metaData" element belongs to the same namespace as that defined by the model.
 
This will define the "metaData" element as having [http://www.w3.org/TR/REC-xml/#sec-mixed-content mixed content]. Add another Details entry and set Key="namespace" and Value="##targetNamespace". This declares that the "metaData" element belongs to the same namespace as that defined by the model.
 +
 +
=== Add the Mix ===
  
 
Add an EAttribute named "mixed" to the MetaData class. This will represent the mixed contents of the serialized element and will hold the content for the "value" attribute. Create an ExtendedMetaData entry for "mixed" and add Details entries as shown in the MyModel.ecore above. Set the EType for "mixed" to "FeatureMap$Entry" and set its upper bound to "unbounded" (-1). The Properties for "mixed" should look like this:
 
Add an EAttribute named "mixed" to the MetaData class. This will represent the mixed contents of the serialized element and will hold the content for the "value" attribute. Create an ExtendedMetaData entry for "mixed" and add Details entries as shown in the MyModel.ecore above. Set the EType for "mixed" to "FeatureMap$Entry" and set its upper bound to "unbounded" (-1). The Properties for "mixed" should look like this:
  
 
[[File:BPMN2-Modeler-AddingCData-ExtendedMetaData-mixed.png]]
 
[[File:BPMN2-Modeler-AddingCData-ExtendedMetaData-mixed.png]]
 +
 +
=== Add the Attributes ===
  
 
Add a EString attribute to MetaData and call it "name". Add another EString attribute called "value", but set the "Volatile" and "Transient" flags "true". The first will generate code that, when invoked from Java, will throw an UnsupportedOperationException. This will remind you that the bodies for MetaData#getValue() and setValue() must be implemented (more about this later.) The "Transient=true" condition will ensure that the "value" attribute itself will not be serialized as an attribute, but it will appear as CDATA. In other words, if we had "Transient=false" the generated XML would look like this:
 
Add a EString attribute to MetaData and call it "name". Add another EString attribute called "value", but set the "Volatile" and "Transient" flags "true". The first will generate code that, when invoked from Java, will throw an UnsupportedOperationException. This will remind you that the bodies for MetaData#getValue() and setValue() must be implemented (more about this later.) The "Transient=true" condition will ensure that the "value" attribute itself will not be serialized as an attribute, but it will appear as CDATA. In other words, if we had "Transient=false" the generated XML would look like this:
Line 58: Line 68:
 
[[File:BPMN2-Modeler-AddingCData-ExtendedMetaData-value.png]]
 
[[File:BPMN2-Modeler-AddingCData-ExtendedMetaData-value.png]]
  
Save the ecore file, open your genmodel and generate the model code.
+
Finally, if your model does not have a DocumentRoot EClass, you need to create one. This should contain references to all of your other EClasses and is required to (among other things) support mixed content. Set its ExtendedMetaData to "mixed" and add references to all your model types as shown here:
 +
 
 +
[[File:BPMN2-Modeler-AddingCData-ExtendedMetaData-docroot.png]]
 +
 
 +
Note that the "Upper Bound" of these references should be set to -2 indicating "undefined".
 +
 
 +
== Generate and Edit The Model Code ==
 +
 
 +
Save the ecore file, create or open your genmodel and generate the model code.
 +
 
 +
Now open the java implementation class for MetaData. You should see that the generated getValue() and setValue() methods look like this:
 +
 
 +
<pre>
 +
    /**
 +
    * <!-- begin-user-doc -->
 +
    * <!-- end-user-doc -->
 +
    * @generated
 +
    */
 +
    public String getValue() {
 +
        // TODO: implement this method to return the 'Value' attribute
 +
        // Ensure that you remove @generated or mark it @generated NOT
 +
        throw new UnsupportedOperationException();
 +
    }
 +
 
 +
    /**
 +
    * <!-- begin-user-doc -->
 +
    * <!-- end-user-doc -->
 +
    * @generated
 +
    */
 +
    public void setValue(String newValue) {
 +
        // TODO: implement this method to set the 'Value' attribute
 +
        // Ensure that you remove @generated or mark it @generated NOT
 +
        throw new UnsupportedOperationException();
 +
    }
 +
</pre>
 +
 
 +
Replace these with the following:
 +
 
 +
<pre>
 +
 
 +
    /**
 +
    * <!-- begin-user-doc -->
 +
    * <!-- end-user-doc -->
 +
    * @generated NOT
 +
    */
 +
    public String getValue() {
 +
        if (mixed != null) {
 +
            StringBuilder result = new StringBuilder();
 +
            for (FeatureMap.Entry cur : mixed) {
 +
                switch (cur.getEStructuralFeature().getFeatureID()) {
 +
                case XMLTypePackage.XML_TYPE_DOCUMENT_ROOT__CDATA:
 +
                case XMLTypePackage.XML_TYPE_DOCUMENT_ROOT__TEXT:
 +
                    result.append(cur.getValue());
 +
                    break;
 +
 
 +
                default:
 +
                    break;
 +
                }
 +
            }
 +
            return result.toString();
 +
        }
 +
 
 +
        return null;
 +
    }
 +
 
 +
    /**
 +
    * <!-- begin-user-doc -->
 +
    * <!-- end-user-doc -->
 +
    * @generated NOT
 +
    */
 +
    public void setValue(String newValue) {
 +
        getMixed().clear();
 +
        FeatureMap.Entry cdata = FeatureMapUtil.createCDATAEntry(newValue);
 +
        getMixed().add(cdata);
 +
    }
 +
</pre>
 +
 
 +
Don't forget to add "NOT" to the @generated annotation before each method to prevent having this code overwritten when you regenerate this file.
 +
 
 +
That's really all there is to it. To see how to handle creation, editing and deletion of a metaData element see the [https://git.eclipse.org/c/bpmn2-modeler/org.eclipse.bpmn2-modeler.git/tree/examples/plugins/org.eclipse.bpmn2.modeler.examples.customtask/src/org/eclipse/bpmn2/modeler/examples/customtask/MyDescriptionPropertySection.java?h=luna Property Detail Composite implementation] in the CustomTask sample plugin.

Latest revision as of 09:41, 11 March 2015

Introduction

There have been a lot questions on the BPMN2 Modeler Forum lately about serialization of BPMN2 extension elements, specifically concerning the serialization of text values in a CDATA block instead of an attribute value.

This tutorial deals more with the implementation details in EMF, rather than focusing on a specific BPMN2 Modeler extension API feature. This functionality has already been added to the Custom Task example plugin, so this tutorial will show how it was done.

For our CustomTask target runtime we wanted the ability to add an optional extension element, named "metaData" to any BaseElement type. Recall that the schema for BaseElement looks like this:

<xsd:element name="baseElement" type="tBaseElement"/>
<xsd:complexType name="tBaseElement" abstract="true">
  <xsd:sequence>
    <xsd:element ref="documentation" minOccurs="0" maxOccurs="unbounded"/>
    <xsd:element ref="extensionElements" minOccurs="0" maxOccurs="1"/>
  </xsd:sequence>
  <xsd:attribute name="id" type="xsd:ID" use="optional"/>
  <xsd:anyAttribute namespace="##other" processContents="lax"/>
</xsd:complexType>

Our requirement is to be able to add an element called "metaData" into the <extensionElements> list. This "metaData" element should have a "name" attribute and a text "value", which should be serialized as CDATA. Something like this for example:

    <bpmn2:extensionElements>
      <mm:metaData name="info"><![CDATA[Here's some information about this process.]]></mm:metaData>
    </bpmn2:extensionElements>

Start With The Ecore

If you have not already done so, you will want to create an EMF model (ecore) and generator file. Note that while you could do this with Java code and dynamic EMF, using an ecore and generated Java classes will make maintaining your model so much easier.

For this tutorial we will use the MyModel.ecore from the CustomTask example.

Create the EClass

Open the MyModel.ecore with the EMF Ecore editor and create a new EClass called MetaData.

BPMN2-Modeler-AddingCData-MyModel-ecore.jpg

Add an EAnnotation child to this EClass and set the "Source" to "http:///org/eclipse/emf/ecore/util/ExtendedMetaData".

BPMN2-Modeler-AddingCData-ExtendedMetaData-properties.jpg

Add a new Details Entry to this ExtendedMetaData element and set the "Key" to "kind" and "Value" to "mixed"

BPMN2-Modeler-AddingCData-ExtendedMetaData-details.png

This will define the "metaData" element as having mixed content. Add another Details entry and set Key="namespace" and Value="##targetNamespace". This declares that the "metaData" element belongs to the same namespace as that defined by the model.

Add the Mix

Add an EAttribute named "mixed" to the MetaData class. This will represent the mixed contents of the serialized element and will hold the content for the "value" attribute. Create an ExtendedMetaData entry for "mixed" and add Details entries as shown in the MyModel.ecore above. Set the EType for "mixed" to "FeatureMap$Entry" and set its upper bound to "unbounded" (-1). The Properties for "mixed" should look like this:

BPMN2-Modeler-AddingCData-ExtendedMetaData-mixed.png

Add the Attributes

Add a EString attribute to MetaData and call it "name". Add another EString attribute called "value", but set the "Volatile" and "Transient" flags "true". The first will generate code that, when invoked from Java, will throw an UnsupportedOperationException. This will remind you that the bodies for MetaData#getValue() and setValue() must be implemented (more about this later.) The "Transient=true" condition will ensure that the "value" attribute itself will not be serialized as an attribute, but it will appear as CDATA. In other words, if we had "Transient=false" the generated XML would look like this:

    <bpmn2:extensionElements>
      <mm:metaData name="info" value="Here's some information about this process."><![CDATA[Here's some information about this process.]]></mm:metaData>
    </bpmn2:extensionElements>

The "value" properties should look like this:

BPMN2-Modeler-AddingCData-ExtendedMetaData-value.png

Finally, if your model does not have a DocumentRoot EClass, you need to create one. This should contain references to all of your other EClasses and is required to (among other things) support mixed content. Set its ExtendedMetaData to "mixed" and add references to all your model types as shown here:

BPMN2-Modeler-AddingCData-ExtendedMetaData-docroot.png

Note that the "Upper Bound" of these references should be set to -2 indicating "undefined".

Generate and Edit The Model Code

Save the ecore file, create or open your genmodel and generate the model code.

Now open the java implementation class for MetaData. You should see that the generated getValue() and setValue() methods look like this:

    /**
     * <!-- begin-user-doc -->
     * <!-- end-user-doc -->
     * @generated
     */
    public String getValue() {
        // TODO: implement this method to return the 'Value' attribute
        // Ensure that you remove @generated or mark it @generated NOT
        throw new UnsupportedOperationException();
    }

    /**
     * <!-- begin-user-doc -->
     * <!-- end-user-doc -->
     * @generated
     */
    public void setValue(String newValue) {
        // TODO: implement this method to set the 'Value' attribute
        // Ensure that you remove @generated or mark it @generated NOT
        throw new UnsupportedOperationException();
    }

Replace these with the following:


    /**
     * <!-- begin-user-doc -->
     * <!-- end-user-doc -->
     * @generated NOT
     */
    public String getValue() {
        if (mixed != null) {
            StringBuilder result = new StringBuilder();
            for (FeatureMap.Entry cur : mixed) {
                switch (cur.getEStructuralFeature().getFeatureID()) {
                case XMLTypePackage.XML_TYPE_DOCUMENT_ROOT__CDATA:
                case XMLTypePackage.XML_TYPE_DOCUMENT_ROOT__TEXT:
                    result.append(cur.getValue());
                    break;

                default:
                    break;
                }
            }
            return result.toString();
        }

        return null;
    }

    /**
     * <!-- begin-user-doc -->
     * <!-- end-user-doc -->
     * @generated NOT
     */
    public void setValue(String newValue) {
        getMixed().clear();
        FeatureMap.Entry cdata = FeatureMapUtil.createCDATAEntry(newValue);
        getMixed().add(cdata);
    }

Don't forget to add "NOT" to the @generated annotation before each method to prevent having this code overwritten when you regenerate this file.

That's really all there is to it. To see how to handle creation, editing and deletion of a metaData element see the Property Detail Composite implementation in the CustomTask sample plugin.

Back to the top