如何在eclipse EMF中以编程方式转换模式时保留属性类型?

时间:2018-02-25 13:33:38

标签: eclipse-emf

考虑使用eclipse生成的以下简单UML模型:

<?xml version="1.0" encoding="UTF-8"?>
<uml:Model xmi:version="20131001" xmlns:xmi="http://www.omg.org/spec/XMI/20131001" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_qXjkkBojEeilO4QGIDh1YA" name="NewModel">
  <packageImport xmi:id="_uR-ywBojEeilO4QGIDh1YA">
    <importedPackage xmi:type="uml:Model" href="pathmap://UML_LIBRARIES/XMLPrimitiveTypes.library.uml#_0"/>
  </packageImport>
  <packagedElement xmi:type="uml:Class" xmi:id="_9ue9ABojEeilO4QGIDh1YA" name="Foo">
    <ownedAttribute xmi:id="_DzKcQBokEeilO4QGIDh1YA" name="Bar">
      <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/XMLPrimitiveTypes.library.uml#String"/>
    </ownedAttribute>
  </packagedElement>
</uml:Model>

我能够将此模型转换为UML&gt;的XSD。 ecore&gt; genmodel&gt; XSD通过 eclipse UI 路由。各中间结果如下:

E字形芯

<?xml version="1.0" encoding="UTF-8"?>
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="newModel" nsURI="http:///newModel.ecore" nsPrefix="newModel">
  <eAnnotations source="http://www.eclipse.org/uml2/2.0.0/UML">
    <details key="originalName" value="NewModel"/>
  </eAnnotations>
  <eClassifiers xsi:type="ecore:EClass" name="Foo">
    <eStructuralFeatures xsi:type="ecore:EAttribute" name="bar" ordered="false" lowerBound="1"
        eType="ecore:EDataType http://www.eclipse.org/emf/2003/XMLType#//String">
      <eAnnotations source="http://www.eclipse.org/uml2/2.0.0/UML">
        <details key="originalName" value="Bar"/>
      </eAnnotations>
    </eStructuralFeatures>
  </eClassifiers>
</ecore:EPackage>

的genmodel

<?xml version="1.0" encoding="UTF-8"?>
<genmodel:GenModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
    xmlns:genmodel="http://www.eclipse.org/emf/2002/GenModel" modelDirectory="/test/src" modelPluginID="test" modelName="NewModel"
    rootExtendsClass="org.eclipse.emf.ecore.impl.MinimalEObjectImpl$Container" importerID="org.eclipse.emf.importer.ecore"
    complianceLevel="5.0" copyrightFields="false" operationReflection="true" importOrganizing="true">
  <foreignModel>../../foobar/newModel.ecore</foreignModel>
  <genPackages prefix="NewModel" disposableProviderFactory="true" ecorePackage="newModel.ecore#/">
    <genClasses ecoreClass="newModel.ecore#//Foo">
      <genFeatures createChild="false" ecoreFeature="ecore:EAttribute newModel.ecore#//Foo/bar"/>
    </genClasses>
  </genPackages>
</genmodel:GenModel>

最终结果 XSD

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:newModel="http:///newModel.ecore" xmlns:org.eclipse.uml2._2._0._0.uml="http://www.eclipse.org/uml2/2.0.0/UML" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ecore:nsPrefix="newModel" ecore:package="newModel" org.eclipse.uml2._2._0._0.uml:originalName="NewModel" targetNamespace="http:///newModel.ecore">
  <xsd:element ecore:ignore="true" name="Foo" type="newModel:Foo"/>
  <xsd:complexType name="Foo">
    <xsd:attribute name="bar" org.eclipse.uml2._2._0._0.uml:originalName="Bar" type="xsd:string" use="required"/>
  </xsd:complexType>
</xsd:schema>

此处,bar的属性类型已正确识别为http://www.w3.org/2001/XMLSchema:string,这是我的目标。

现在,我尝试使用Kotlin程序以编程方式生成相同的结果:

@file:JvmName("XsdGenerator")

package org.example.foo.bar

import org.eclipse.emf.codegen.ecore.genmodel.GenModelFactory
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl
import org.eclipse.emf.ecore.util.EcoreUtil
import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl
import org.eclipse.uml2.uml.Model
import org.eclipse.uml2.uml.Namespace
import org.eclipse.uml2.uml.UMLPackage
import org.eclipse.uml2.uml.internal.resource.CMOF202UMLResourceImpl
import org.eclipse.uml2.uml.resource.UMLResource
import org.eclipse.uml2.uml.util.UMLUtil
import org.eclipse.xsd.ecore.EcoreSchemaBuilder
import org.eclipse.xsd.util.XSDResourceFactoryImpl
import kotlin.system.exitProcess

object XsdGenerator {
  private val UML_LOAD_OPTIONS = emptyMap<String, String>()
  private val UML_TO_ECORE_CONVERSION_OPTIONS = mutableMapOf<String, String>()
  private val ECORE_RESOURCE_OPTIONS = mutableMapOf<String, String>()
  private val GENMODEL_RESOURCE_OPTIONS = mutableMapOf<String, String>()
  private val XSD_RESOURCE_OPTIONS = mutableMapOf<String, String>()

  private lateinit var schemaSource: URI
  private lateinit var buildDirectory: URI
  private lateinit var schemaTarget: URI
  private lateinit var schemaVersion: String

  @JvmStatic
  fun main(args: Array<String>) {
    try {
      schemaSource = URI.createFileURI(args[0])
      buildDirectory = URI.createURI(args[1])
      schemaTarget = URI.createURI(args[2])
      schemaVersion = args[3]
    } catch (e: Exception) {
      println("Can't parse input arguments")
      exitProcess(1)
    }

    val resources = ResourceSetImpl().apply {
      resourceFactoryRegistry.apply {
        extensionToFactoryMap["xsd"] = XSDResourceFactoryImpl()
        extensionToFactoryMap["genmodel"] = EcoreResourceFactoryImpl()
        extensionToFactoryMap["ecore"] = EcoreResourceFactoryImpl()
      }
    }

    val umlModel = CMOF202UMLResourceImpl(schemaSource)
      .apply {
        if (!isLoaded) load(UML_LOAD_OPTIONS)
      }

    val ecoreModel = (EcoreUtil.getObjectByType(umlModel.contents, UMLPackage.Literals.MODEL) as Model)
      .run {
        UMLUtil.convertToEcore(this, UML_TO_ECORE_CONVERSION_OPTIONS)
      }
      .first()
      .also {
        resources
          .createResource(buildDirectory.appendSegment("foobar.ecore"))
          .apply {
            contents.add(it)
            save(ECORE_RESOURCE_OPTIONS)
          }
      }

    val genPackages = GenModelFactory.eINSTANCE.createGenModel()
      .also {
        // genmodel settings can be set here
        it.initialize(listOf(ecoreModel))
        resources.createResource(buildDirectory.appendSegment("foobar.genmodel")).apply {
          contents.add(it)
          save(GENMODEL_RESOURCE_OPTIONS)
        }
      }

    val mapBuilder = EcoreSchemaBuilder(genPackages.extendedMetaData)

    val res = resources.resources.find { it.uri.toString().endsWith(".ecore") }
      ?: throw IllegalStateException("No ecore model present")

    genPackages.allGenPackagesWithClassifiers.forEach { genPackage ->

      val pack = genPackage.ecorePackage
      val fileName = genPackage.nsName + ".xsd"

      val xsdSchema = mapBuilder.getSchema(pack).apply {
        version = schemaVersion
      }

      val xsdSchemaURI = res.uri.trimFileExtension().appendSegment(fileName)
      resources.createResource(xsdSchemaURI).apply {
        contents.add(xsdSchema)
        save(XSD_RESOURCE_OPTIONS)
      }
    }
  }
}

这给出了以下中间结果:

E字形芯

<?xml version="1.0" encoding="UTF-8"?>
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="NewModel" nsURI="http:///NewModel.ecore" nsPrefix="NewModel">
  <eClassifiers xsi:type="ecore:EClass" name="Foo">
    <eStructuralFeatures xsi:type="ecore:EAttribute" name="Bar" ordered="false" lowerBound="1"
        eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EJavaObject"/>
  </eClassifiers>
</ecore:EPackage>

的genmodel

<?xml version="1.0" encoding="UTF-8"?>
<genmodel:GenModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
    xmlns:genmodel="http://www.eclipse.org/emf/2002/GenModel" copyrightFields="false">
  <genPackages prefix="NewModel" disposableProviderFactory="true" ecorePackage="/Users/mo/workspace/foobar/build/foobar.ecore#/">
    <genClasses ecoreClass="/Users/mo/workspace/foobar/build/foobar.ecore#//Foo">
      <genFeatures createChild="false" ecoreFeature="ecore:EAttribute /Users/mo/workspace/foobar/build/foobar.ecore#//Foo/Bar"/>
    </genClasses>
  </genPackages>
</genmodel:GenModel>

最后, XSD

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns:NewModel="http:///NewModel.ecore" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ecore:nsPrefix="NewModel" ecore:package="NewModel" targetNamespace="http:///NewModel.ecore" version="0.0.1">
  <xsd:import namespace="http://www.eclipse.org/emf/2002/Ecore" schemaLocation="http://www.eclipse.org/emf/2002/Ecore"/>
  <xsd:element ecore:ignore="true" name="Foo" type="NewModel:Foo"/>
  <xsd:complexType name="Foo">
    <xsd:attribute ecore:name="Bar" name="Bar" type="ecore:EJavaObject" use="required"/>
  </xsd:complexType>
</xsd:schema>

请注意,此处的类型信息会丢失,并且该属性的输入为ecore:EJavaObject,而不是xsd:string

如何让我的程序保留XML模式类型与eclipse UI一样?

0 个答案:

没有答案