Skipper - The ORM Designer Blog
  • Skipper - The ORM Designer
  • VsBuilds - Parallel building
  • Pulpo - Free Skipper CLI

Create support for your own framework in ORM Designer, part VII – export script

Part VII: How to write an export script

In this part of our serie I want to show you how to create an export script for your ORM framework. I’m going to show you basic script commands and how to communicate with ORM Designer project from the script. For an advanced usage of script language it is recommended to study the original ORM Designer scripts for Doctrine, Doctrine2, Propel and other frameworks located in Scripts folder in ORM Designer installation folder. Basic syntax of ORM Designer script is based on Phing XML language.

Export script testing

Before we start to write our first export script, I’m going to show you how to test and debug your scripts.  Article with all informations about this topic is available here http://www.orm-designer.com/article/ormd-developers-testing-import-export. Currently we need following switches:

OrmDesignerCore.exe -unit-test-import-export -input-project c:\project.ormdes -output-orm-file c:\project.out.xml
OrmDesignerCore.exe -unit-test-import-export -input-project c:\project.ormdes -output-export

First statement runs ORM Designer and load project stored in file “c:\project.ormdes”. After the project has been loaded, the whole project is exported into a single file “c:\project.out.xml”.

Second statement also runs ORM Designer and load stored project. After that, project is exported in the same way as after user’s click on “Export” button in ORM Designer application.

Note: Project stored in “project.ormdes” mu</span>st have “AtomixORM” as ORM type.

Minimal script

Now let’s see our minimal export script and how it will be launched by the command from previous paragraph:

<function name='AtomixORM.export'>
  <check>
    <is-set variable='projectID'/>
    <default variable='exportToOneFile' value=''/>
  </check>
</function>

Script “AtomixORM.export” will be executed with following values:

  • projectID: This variable will contain internal ID of project to export.
  • exportToOneFile: This variable will contain “c:\project.out.xml” in first command-line case, and is empty in second case.

First simple script

Now let’s add few commands to our export script. As first step we tell ORM Designer to export the selected project to a single temporary XML file and then copy this file to the output file determined by command line param:

<function name='AtomixORM.export'>
  <check>
    <is-set variable='projectID'/>
    <default variable='exportToOneFile' value=''/>
  </check>
  <if>
    <not-equal value1='${exportToOneFile}' value2=''/>
    <then>
      <get-tmp-file-name variable='tmpOneFileExport' dir='ormd-export'/>
      <ormd-export-file output-xml='${tmpOneFileExport}' project='${projectID}'/>
      <copy-file from='${tmpOneFileExport}' to='${exportToOneFile}'/>
    </then>
  </if>
</function>
  • By using “if” and “not-equal” we test if variable “exportToOneFile” isn’t empty. If it is true, we execute export statements which saves the whole project to one file.

  • “get-tmp-file-name” command assigns a unique temporary file name to variable tmpOneFileExport. Attribute “dir” serves to setup usage of subdirectory in temp directory. Result temporary file in our case will look like “….\temp\ormd-export\tmpXXXXXX.tmp”.
  • By using a next command “ormd-export-file” ORM Designer exports project with a certain ID into output XML file determined by output XML attribute.
  • The last command will copy file from the temporary location to a destination path determined by exportToOneFile variable.

This script isn’t much useful yet, but helps us to demonstrate how ORM Designer proceeds data export and allows you to examine how XML file exported from ORM Designer looks like. With this file we will do the next transformations and operations in next parts of this tutorial.

XSLT transformation

The next important step in import and export scripts are  the XSLT transformations. Now I’m going to show you how to transform an exported ORM Designer XML file to another XML file required by your ORM framework. Let’s replace our previous script by following new one:

<function name='AtomixORM.export'>
  <check>
    <is-set variable='projectID'/>
    <default variable='exportToOneFile' value=''/>
  </check>
  <if>
    <not-equal value1='${exportToOneFile}' value2=''/>
    <then>
      <get-tmp-file-name variable='tmpOneFileExport' dir='ormd-export'/>
      <ormd-export-file output-xml='${tmpOneFileExport}' project='${projectID}'/>
      <xsl-transformation xslt='AtomixOrm/Xml2AtomixOrmAbstractXml.xslt' input-xml='${tmpOneFileExport}' output-xml='${tmpOneFileExport}_2'/>
      <copy-file from='${tmpOneFileExport}_2' to='${exportToOneFile}'/>
      <delete-file file='${tmpOneFileExport},${tmpOneFileExport}_2'/>
      </then>
  </if>
</function>

By using the command “xsl-transformation” we will perform XSLT transformation by using XSL template passed by “xsl” attribute (path is relative to script location). Other two attributes determine input and output XML file. Next new command which I use in this example is “delete-file”. After we procees all required steps, it’s a good practice to clean up all temporary files. So we delete both of them.

Hint:

If you want to store results from your script for debug purposes to some folder, you can use following statement:

<!-- debug -->
<copy-file from='${tmpOneFileExport}' to='c:\ORM\US\AtomixOrm.export-1-proj-onefile.xml' only-debug='true'/>

This statement will be executed only if script runs only on debug  mode (export launched by using command line statement -unit-test-import-export).

XSLT

Now let’s look to a simple XSL template used for transformation:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:saxon="http://icl.com/saxon">
  <xsl:output indent="yes"/>
	<xsl:template match="*">
    <import-export-document>
      <xsl:apply-templates select="/orm-designer/module"/>
    </import-export-document>
  </xsl:template>
	<xsl:template match="/orm-designer/module">
    <import-export-file import-path="{@import-path}" import-format="{@import-format}">
      <atomix-orm>
        <xsl:copy-of select="./*"/>
      </atomix-orm>
    </import-export-file>
  </xsl:template>
</xsl:stylesheet>

This template creates XML with root element “import-export-document” which contains several “import-export-file” elements. These elements will contain copy of exported model from ORM Designer. We will use this prepared XML later in this tutorial to split file to several output XML files.

Example project files

Here is all files used in this tutorial:

Debug output

When you run ORM Designer with a command line params from the beginning of this article, you should see output like this:

(0019)Running function [AtomixORM.export] from file [p:/OrmDesignerCore/SetupCreator/Installed/config/AtomixOrm/AtomixOrm.uscript.xml]
(0020)  check
(0021)  - exist-variable [projectID]=TRUE (11)
(0022)  - default-variable-value [exportToOneFile] Exists=TRUE
(0020)  result-check = [TRUE]
(0026)  if
(0027)  - equal ['c:\project.out.xml'!='']=>TRUE
(0026)  - RESULT = [TRUE]
(0029)    get-tmp-file-name[tmpOneFileExport,ormd-export]=C:\Users\...\Temp\ormd-export\tmp7E69.tmp
(0030)    ormd-export-file [P:11] C:\Users\...\Temp\ormd-export\tmp7E69.tmp,
(0033)    copy-file[C:\Users\...\Temp\ormd-export\tmp7E69.tmp,c:\ORM\US\AtomixOrm.export-1-proj-onefile.xml]
(0036)    xsl-transform[p:\OrmDesignerCore\SetupCreator\Installed\config\AtomixOrm\Xml2AtomixOrmAbstractXml.xsl,C:\Users\...\Temp\ormd-export\tmp7E69.tmp,C:\Users\...\Temp\ormd-export\tmp7E69.tmp_2]
(0037)    copy-file[C:\Users\...\Temp\ormd-export\tmp7E69.tmp_2,c:\project.out.xml]
(0040)    delete-file[C:\Users\...\Temp\ormd-export\tmp7E69.tmp,C:\Users\...\Temp\ormd-export\tmp7E69.tmp_2]
(0040)    - deleting C:\Users\...\Temp\ormd-export\tmp7E69.tmp
(0040)    - deleting C:\Users\...\Temp\ormd-export\tmp7E69.tmp_2
Press any key to close the log.

Advanced export script

In previous paragraphs I showed you few simple examples of export script. Now let’s look at more complicated export which exports data to several xml files. In this example I want to show you next new commands and scripting technique. First I’m going to show you a full script listing and then explain every single step.

<function name='AtomixORM.export'>
  <check>
    <is-set variable='projectID'/>
    <default variable='exportToOneFile' value=''/>
  </check>
  <!-- (1) export data from ORM Designer -->
  <get-tmp-file-name variable='tmpOneFileExport' dir='ormd-export'/>
  <ormd-export-file output-xml='${tmpOneFileExport}' project='${projectID}'/>
  <!-- (2) debug -->
  <copy-file from='${tmpOneFileExport}' to='c:\ORM\US\AtomixOrm.export-1-proj-onefile.xml' only-debug='true'/>
  <!-- (3) xslt transformation -->
  <xsl-transformation xslt='Xml2AtomixOrmAbstractXml.xsl' input-xml='${tmpOneFileExport}' output-xml='${tmpOneFileExport}_2'/>
  <!-- (4) debug -->
  <copy-file from='${tmpOneFileExport}_2' to='c:\ORM\US\AtomixOrm.export-2-after-first-transform.xml' only-debug='true'/>
  <!-- (5) split xml file to separated files -->
  <get-tmp-path variable='tmpSplitOutput' dir='ormd-export'/>
  <xml-split-file input-xml='${tmpOneFileExport}_2' output-xml-path='${tmpSplitOutput}' variable='splitFiles' />
  <!-- (6) do export for each file -->
  <for-each list='${splitFiles}' variable='file'>
    <!-- (7) debug -->
    <copy-file from='${file}' to='c:\ORM\US\AtomixOrm.export-3-splited-file.xml' only-debug='true'/>
    <!-- (8) read destination file from file -->
    <xml-read-values input-xml='${file}'>
      <read-value xpath='/import-export-file/@import-path' variable='destination_path'/>
    </xml-read-values>
    <!-- (9) if there is no destination-path for this module, skip it-->
    <if>
      <equal value1='${destination_path}' value2=''/>
      <then>
        <continue/>
      </then>
    </if>
    <!-- (10) do transform for output file -->
    <xsl-transformation xslt='AtomixOrmAbstract2AtomixOrmXml.xsl' input-xml='${file}' output-xml='${file}_2'/>
    <!-- (11) debug -->
    <copy-file from='${file}_2' to='c:\ORM\US\AtomixOrm.export-4-converted-xml-yml.xml' only-debug='true'/>
    <!-- (12) Save generated result. If exportOneFile is set, all result is stored to this file, otherwise file is stored to export location -->
    <if>
      <equal value1='${exportToOneFile}' value2=''/>
      <then>
        <copy-file from='${file}_2' to='${destination_path}'/>
      </then>
      <else>
        <append-file from='${file}_2' to='${exportToOneFile}'/>
      </else>
    </if>
    <!-- (13) clean-up -->
    <delete-file file='${file},${file}_2'/>
  </for-each>
  <!-- (14) clean-up -->
  <delete-file file='${tmpOneFileExport}_2,${tmpOneFileExport}'/>
</function>

Here is the explain of each step of this script:

  1. Generate temporary file name and call export from ORM Designer project.
  2. Copy generated file to our debug location.
  3. Call XSLT transformation on exported XML file.
  4. Copy transformed file to our debug location.
  5. Split transformed XML file by root children to separated files. These files will be stored in temp/ormd-export folder. List of the files will be stored in $splitFiles variable.
  6. Now enumerate all splitted files one by one.
  7. Copy splitted file to debug location.
  8. Read value from splitted file by using xpath expression “/import-export-file/@import-path” to variable $destination_path.
  9. If variable @import-path isn’t filled, skip next expressions and proceed next file.
  10. Call next XSLT transformation on splitted file. File is again stored in temp with postfix “_2”.
  11. Copy transformed file to our debug location.
  12. Save exported and transformed result to destination file or append it to passed one-file path.
  13. Delete temporary files from this cycle.
  14. Delete another temporary files from this script.

Pretty simple, right? ;-). There is a lot of things that can be done by ORM Designer scripting language. But this sample should be sufficient to learn you how to create the export scripts for your framework. This script with both XSLT templates could be downloaded here: Final export script example.

Script execution

Now you can run this script in one-file export mode and also in full export mode. When you run this script by using “-output-orm-file c:\project.out.xml” param, file project.out.xml should content this:

<?xml version="1.0"?>
<atomix-orm>
  <table name="Contact" visPositionX="28" visPositionY="36" visSizeX="0" visSizeX2="20" visSizeY="0" visSizeY2="20">
    <column auto-increment="true" name="ID" primary="true" required="true" type="integer"/>
    <column name="Name" type="string"/>
  </table>
  <table name="Address" visPositionX="17" visPositionY="136" visSizeX="0" visSizeX2="113" visSizeY="0" visSizeY2="73">
    <column auto-increment="true" name="ID" primary="true" required="true" type="integer"/>
    <column name="Street" type="string"/>
    <column name="City" type="string"/>
    <column name="Contact_ID" type="integer"/>
  </table>
  <foreign-key caption="Contact Address" from="Address" name="FK_Address_Contact_ID" to="Contact">
    <foreign-key-column from="Contact_ID" to="ID"/>
  </foreign-key>
</atomix-orm>
<?xml version="1.0"?>
<atomix-orm>
  <table name="BlogPost" visPositionX="56" visPositionY="100" visSizeX="0" visSizeX2="113" visSizeY="0" visSizeY2="59">
    <column auto-increment="true" name="ID" primary="true" required="true" type="integer"/>
    <column name="Text"/>
    <column name="Contact_ID" type="integer"/>
  </table>
  <foreign-key caption="Contact BlogPost" from="BlogPost" name="FK_BlogPost_Contact_ID" to="Contact">
    <foreign-key-column from="Contact_ID" to="ID"/>
  </foreign-key>
</atomix-orm>

As you can see, this is not valid XML file. This file is useful in unit-testing stage of your development process. You can simply compare result from export to some required pattern file. If you want to export data in the same format in which your users will see them, use second command line parameter “-output-export”. After that, two files “models\app.xml” and “models\plugin.xml” will be created.

Debug log from processing of this script will look like this:

(0019)Running function [AtomixORM.export] from file [p:/OrmDesignerCore/SetupCreator/Installed/config/AtomixOrm/AtomixOrm.uscript.xml]
(0020)  check
(0021)  - exist-variable [projectID]=TRUE (19)
(0022)  - default-variable-value [exportToOneFile] Exists=TRUE
(0020)  result-check = [TRUE]
(0026)  get-tmp-file-name[tmpOneFileExport,ormd-export]=C:\Users\...\Temp\ormd-export\tmp1DBD.tmp
(0027)  ormd-export-file [P:19] C:\Users\...\Temp\ormd-export\tmp1DBD.tmp,
(0030)  copy-file[...\Temp\ormd-export\tmp1DBD.tmp,c:\ORM\US\AtomixOrm.export-1-proj-onefile.xml]
(0033)  xsl-transform[p:\OrmDesignerCore\SetupCreator\Installed\config\AtomixOrm\Xml2AtomixOrmAbstractXml.xsl,...\Temp\ormd-export\tmp1DBD.tmp,...\Temp\ormd-export\tmp1DBD.tmp_2]
(0036)  copy-file[...\Temp\ormd-export\tmp1DBD.tmp_2,c:\ORM\US\AtomixOrm.export-2-after-first-transform.xml]
(0039)  get-tmp-path[tmpSplitOutput,ormd-export]=...\Temp\ormd-export
(0040)  xml-split-file [...\Temp\ormd-export\tmp1DBD.tmp_2,...\Temp\ormd-export,splitFiles]
(0040)  - splitting file [...\Temp\ormd-export\_tmp100830173657742.tmp]
(0040)  - splitting file [...\Temp\ormd-export\_tmp100830173657753.tmp]
(0043)  for-each[file<='...\Temp\ormd-export\_tmp100830173657742.tmp,...\Temp\ormd-export\_tmp100830173657753.tmp'](0043)  - iteration [file=...\Temp\ormd-export\_tmp100830173657742.tmp]
(0045)    copy-file[...\Temp\ormd-export\_tmp100830173657742.tmp,c:\ORM\US\AtomixOrm.export-3-splited-file.xml]
(0048)    xml-read-values [...\Temp\ormd-export\_tmp100830173657742.tmp]
(0049)    - read-values [/import-export-file/@import-path=>destination_path] value:'c:\Article\models\app.xml'
(0053)    if
(0054)    - equal ['c:\Article\models\app.xml'='']=>FALSE
(0053)    - RESULT = [FALSE]
(0061)    xsl-transform[p:\OrmDesignerCore\SetupCreator\Installed\config\AtomixOrm\AtomixOrmAbstract2AtomixOrmXml.xsl,...\Temp\ormd-export\_tmp100830173657742.tmp,...\Temp\ormd-export\_tmp100830173657742.tmp_2]
(0064)    copy-file[...\Temp\ormd-export\_tmp100830173657742.tmp_2,c:\ORM\US\AtomixOrm.export-4-converted-xml-yml.xml]
(0067)    if
(0068)    - equal ['c:\project.out.xml'='']=>FALSE
(0067)    - RESULT = [FALSE]
(0073)      append-file[...\Temp\ormd-export\_tmp100830173657742.tmp_2,c:\project.out.xml]
(0078)    delete-file[...\Temp\ormd-export\_tmp100830173657742.tmp,...\Temp\ormd-export\_tmp100830173657742.tmp_2]
(0078)    - deleting ...\Temp\ormd-export\_tmp100830173657742.tmp
(0078)    - deleting ...\Temp\ormd-export\_tmp100830173657742.tmp_2
(0043)  - iteration [file=...\Temp\ormd-export\_tmp100830173657753.tmp]
(0045)    copy-file[...\Temp\ormd-export\_tmp100830173657753.tmp,c:\ORM\US\AtomixOrm.export-3-splited-file.xml]
(0048)    xml-read-values [...\Temp\ormd-export\_tmp100830173657753.tmp]
(0049)    - read-values [/import-export-file/@import-path=>destination_path] value:'c:\Article\models\plugin.xml'
(0053)    if
(0054)    - equal ['c:\Article\models\plugin.xml'='']=>FALSE
(0053)    - RESULT = [FALSE]
(0061)    xsl-transform[p:\OrmDesignerCore\SetupCreator\Installed\config\AtomixOrm\AtomixOrmAbstract2AtomixOrmXml.xsl,...\Temp\ormd-export\_tmp100830173657753.tmp,...\Temp\ormd-export\_tmp100830173657753.tmp_2]
(0064)    copy-file[...\Temp\ormd-export\_tmp100830173657753.tmp_2,c:\ORM\US\AtomixOrm.export-4-converted-xml-yml.xml]
(0067)    if
(0068)    - equal ['c:\project.out.xml'='']=>FALSE
(0067)    - RESULT = [FALSE]
(0073)      append-file[...\Temp\ormd-export\_tmp100830173657753.tmp_2,c:\project.out.xml]
(0078)    delete-file[...\Temp\ormd-export\_tmp100830173657753.tmp,...\Temp\ormd-export\_tmp100830173657753.tmp_2]
(0078)    - deleting ...\Temp\ormd-export\_tmp100830173657753.tmp
(0078)    - deleting ...\Temp\ormd-export\_tmp100830173657753.tmp_2
(0082)  delete-file[...\Temp\ormd-export\tmp1DBD.tmp_2,...\Temp\ormd-export\tmp1DBD.tmp]
(0082)  - deleting ...\Temp\ormd-export\tmp1DBD.tmp_2
(0082)  - deleting ...\Temp\ormd-export\tmp1DBD.tmp
Press any key to close the log.

### Conclusion

In this example I showed you how to create the export script for ORM Designer. This topic might be a little complicated to understand, so if you find anything understandable, please leave us a comment bellow the post or contact me on [email protected]. If you are seriously interested in creating of support for your ORM Framework, please let us know. We will prepare the support for you for free if you have large enough community which can be interested in. If you have your own company framework and want to prepare support for your internal ORM, please let us know too, we also offer creating of an ORM support for company customers.

26 Aug 2010