PyEmofUCSample project: CountyCityJ.M. Drake, P. López Martínez and C. CuevasSoftware
Engineering and Real-Time (ISTR) - University
of Cantabria
|
PyEmofUC => Root folder
that results from unpacking the PyEmofUC_0_0.zip
archive. It represents the starting point for the
inner relative paths. CountyCity => Root folder for the CountyCity sample project. DumpedFiles => Folder containing those files generated at runtime. Its contents can be removed. Meta-models => Folder containing the meta-models (M2) of the example. City.xmi => The City meta-model in XMI format. City.xsd => W3C-Schema that drives the formulation of models compliant to the City meta-model. County.xmi => The County meta-model in XMI format. County.xsd => W3C-Schema that drives the formulation of models compliant to the County meta-model. TaxCensus.xmi => The TaxCensus meta-model in XMI format. TaxCensus.xsd => W3C-Schema that drives the formulation of models compliant to the TaxCensus meta-model. Models => Folder containing the models (M1) of the example. EagleCity.xmi => Sample model instance compliant to the City meta-model. EagleCounty.xmi => Sample model instance compliant to the County meta-model. Scripts => Folder containing the scripts of the example. CountyCity_CountyToTaxCensus_Declarative_Script.py => Script implementing a sample M2M transformation using a declarative style. CountyCity_CountyToTaxCensus_Imperative_Script.py => Script implementing a sample M2M transformation using an imperative style. CountyCity_LoadDump_Script.py => Script implementing a model loading/processing/serialization process. schemas => Folder containing general purpose W3C-Schemas required by the PyEmofUC environment for auxiliary tasks. EMOF.xsd => Schema for EmofUC meta-models. It is referenced for XML validation of any meta-model in XMI format. XMI.xsd => Schema of the OMG XMI. It is imported by any meta-model in XMI format. src => Root folder containing the PyEmof Python code. Its path should be included in the PYTHONPATH environment variable. |
The County.xmi
file
formulates
the County
meta-model in XMI format, while the County.txt file formulates the same meta-model in
plain text.
Finally, the County.rtf
file formulates it in a enhanced (coloured) textual
format. When the County
meta-model is saved, it generates the
W3C-Schema County.xsd
(TODO) defining the XMI
files as well as the County.bnf BNF grammar (TODO) supporting the models
in the RTF format.
Each company has a workforce consisting of a set of jobs (Employ class). Each job may be assigned to an employee (worker) who must bte an adult (Adult) whose personal data are described in a model compliant to the County meta-model.
The City.xmi
file formulates the City
meta-model in XMI format, while the City.txt file formulates the same meta-model in plain
text.
Finally, the City.rtf
file formulates it in a enhanced (coloured) textual format.
Each debt has two data: the amount due (amount) and the date (date):
The table below shows the script with the Python code.
import
pyEmofUC.resource as resource import
pyEmofUC.xmlcore as xmlcore import
pyEmofUC.GLOBAL as GLOBAL xmlcore.PLATFORM
= 'C:\CountyCityWorkspace' # Set
VERBOSE MODE GLOBAL.IS_VERBOSE
= True # CountyCity
metamodel URIs countyURI = "http://unican.es/istr/PyEmofUC/CountyCity/County" cityURI = "http://unican.es/istr/PyEmofUC/CountyCity/Cyty" # The
application repository is built aRep =
resource.AppRepository() # Loads the
EagleCity model eagleCounty_URL
= "platform:CountyCity/models/EagleCounty.xmi" eagleCounty =
aRep.loadModel(eagleCounty_URL) # Example of
model managing: Displays the loaded models print '\n' + 'PRINTS
LIST
OF LOADED MODELS:' for
loadedModelURI in aRep.mdlRepositories.keys(): print 5 * ' ' +
loadedModelURI # Example of
model processing: List the name of adults without
job print '\n' + 'PRINT
LIST
OF ADULTS WITHOUT JOB:' adultWithoutJob
= (adult for home in eagleCounty.rootPackage.home
for adult in home.member
if adult.isInstanceOf('cnty.Adult') and not adult.job) for theAdult in
adultWithoutJob: print 5 * ' ' +
theAdult.name # Dumps
eagleCounty model with RTF format dumpedEagleCounty_URL
= 'platform:CountyCity/DumpedFiles/EagleCounty.rtf' eagleCounty.dump(dumpedEagleCounty_URL) # Delete all
models del aRep |
Since the input model references to a City model, loading it implies loading that second one.
# @nsURI
county_MM=http://unican.es/istr/PyEmofUC/CountyCity/County # @nsURI taxCensus_MM=
http://unican.es/istr/PyEmofUC/CountyCity/TaxCensus module County_To_TaxCensus; create outTaxCensusModel
: taxCensus_MM from inCountyModel
: county_MM; helper def :
rootContainer() : county_MM!County = . . .; helper def :
currentDate() : String = . . .; helper def :
accumulatedSalary(theAdult: county_MM!Adult) : Real= . . .; lazy rule createTax
{ from theAdult
: county_MM!Adult to theTax
: TaxCensus!Tax ( amount <- (0.2 * accumulatedSalary(theAdult)) (100.0
* len(theAdult.offspring)), date <- thisModule.currentDate() ) } entrypoint rule County_To_TaxCensus
() { to theCountyTaxCensus
: taxCensus_MM!CountyTaxCensus( countyName <- thisModule.rootContainer().countyName, taxPayer <- thisModule.rootContainer().home -> collect(theHome |
theHome.member -> select(theMember |
theMember.isInstanceOf(County_MM!Adult)) ) ) } rule Adult_To_TaxPayer
{ from theAdult
: county_MM!Adult to theTaxPayer
: taxCensus_MM!TaxPayer ( payerName <- theAdult.name, address <- theAdult.home.address+
+ thisModule.rootContainer().countyAddress, currentTax <- if
not theAdult.job
-> isEmpty() then thisModule.createTax(theAdult), else OclUndefined
endif, holder <- theAdult } |
county_MM represents the URI of the
County meta-model taxCensus_MM represents the URI of
the TaxCensus
meta-model The module County_To_TaxCensus
represents the generation of the outTaxCensusModel
model from the inCountyModel model Returns the input model main
container. Returns the
date of the invocation instant Returns the
accumulated salary of the Adult element as
the sum of the salaries corresponding to
the jobs assigned to him/her. Callable rule which generates an
element of Tax type in the output model using the data
of the referenced Adult element Initial rule which generates the output
model main container using the
data in the input model Rule which generates a TaxPayer
element in the output model for each Adult element
in the input model |
The County_To_TaxCensus_Declarative.py file contains the transformation Python code in a declarative style.
Python code |
ATL code |
||
import emofUC.resource as resource import emofUC.xmlcore as xmlcore import emofUC.GLOBAL as GLOBAL aRep
= resource.AppRepository() xmlcore.PLATFORM = "C:\Users\. .
.\PyEMOF_UC\Workspace" |
|
||
|
@path
county_MM_URL= platform:CountyCity/County.xmi @ path
taxCensus_MM_URL= platform:CountyCity/TaxCensus.xmi |
||
county_MM_URL = 'platform:CountyCity/County.xmi' taxCensus_MM_URL
= 'platform:CountyCity/TaxCensus.xmi' aRep.loadModel(taxCensus_MM_URL) |
|
||
|
module CountyCity_To_TaxCensus; create outTaxCensusModel : taxCensus_MM from inCountyModel : county_MM; |
||
outTaxCensusModelURI = "http://unican.es/istr/PyEmofUC/CountyCity/EagleTaxCensus" outTaxCensusModelNsPrefix
= 'etxc' outTaxCensusModel
= aRep.createMdlRepository(uri=outTaxCensusModelURI,
mmUrl=taxCensus_MM_URL,
nsPrefix=outTaxCensusModelNsPrefix) inCountyModelURL
= "platform:CountyCity/EagleCounty.xmi" inCountyModel = aRep.loadModel(inCountyModelURL) |
|
||
|
helper def :
rootContainer() : county_MM!County = . . .; |
||
def rootContainer(): return inCountyModel.rootPackage |
|
||
|
helper def :
currentDate() : String = . . .; |
||
def currentDate(): return GLOBAL.currentDate() |
|
||
|
helper def :
accumulatedSalary(theAdult: county_MM!Adult) : Real= .
. .; |
||
def accumulatedSalary(theAdult): return reduce(lambda x, y:x +
y, [job.salary for job in theAdult.job]) |
|
||
|
lazy rule createTax
{ from theAdult
: county_MM!Adult to theTax
: TaxCensus!Tax ( amount <- (0.2 * accumulatedSalary(theAdult)) (100.0
* len(theAdult.offspring)), date <- thisModule.currentDate() ) } |
||
def createTax(theAdult): if len(theAdult.job)
> 0: theTax
= outTaxCensusModel.createElement('txc.Tax',
theAdult.xmi_id.replace('cnty', 'etxc') + '.theTax') theTax.amount
= (0.2 *
accumulatedSalary(theAdult)) - (100.0 *
len(theAdult.offspring)) theTax.date
= currentDate() return theTax else: return None |
|
||
|
entrypoint rule County_To_TaxCensus
() { to theCountyTaxCensus
: taxCensus_MM!CountyTaxCensus( countyName <- thisModule.rootContainer().countyName, taxPayer <- thisModule.rootContainer().home -> collect(theHome |
theHome.member -> select(theMember |
theMember.isInstanceOf(County_MM!Adult)) ) ) } |
||
theCounty
= rootContainer() theCountyTaxCensus
= outTaxCensusModel.createElement('txc.CountyTaxCensus',
rootContainer().xmi_id.replace('cnty', 'etxc')) theCountyTaxCensus.countyName
= rootContainer().countyName theCountyTaxCensus.taxPayer = [theMember.xmi_id.replace('cnty', 'etxc') + '.theTaxPayer' for theHome in rootContainer().home for theMember intheHome.member if theMember.isInstanceOf('cnty.Adult')] |
|
||
|
rule Adult_To_TaxPayer
{ from theAdult :
county_MM!Adult to theTaxPayer :
taxCensus_MM!TaxPayer ( payerName <- theAdult.name, address <- theAdult.home.address+
+ thisModule.rootContainer().countyAddress, currentTax <- thisModule.createTax(theAdult), holder <- theAdult } |
||
for theAdult in (theAdult for theAdult in inCountyModel.getElements() if theAdult.isInstanceOf('cnty.Adult')): theTaxPayer
= outTaxCensusModel.createElement('txc.TaxPayer',
theAdult.xmi_id.replace('cnty', 'etxc') + '.theTaxPayer') theTaxPayer.payerName
= theAdult.name theTaxPayer.address
= theAdult.home.address + ' ' +
inCountyModel.rootPackage.countyAddress theTaxPayer.holder
= theAdult theTaxPayer.currentTax = createTax(theAdult) |
|
||
|
#Updating of references
between objects created in the output model
|
||
outTaxCensusModel.rootPackage
= theCountyTaxCensus theCountyTaxCensus.taxPayer = [outTaxCensusModel.getElement(xmiid)
for xmiid in theCountyTaxCensus.taxPayer] |
|
The output model generated by the County_To_TaxCensus
transformation
can be found in the EagleTaxCensus_Declarative.xmi
file.
Transformation
formulation (imperative style):
The table below shows the
County_To_TaxCensus M2M transformation in an
imperative style.
The imperative algorithm is based on an iteration over the input model elements, using conditional sentences to evaluate the guard conditions.
For the appropriate elements, their attributes are processed and the corresponding output model elements are created.
1. Creates an
application repository to store and process the
models. 2. Opens the input file
and loads the in the repository the model data. 3. Load the metamodel
of the output model 4. Creates an empty
output model 5. Creates the root
container element of the output model. 5.1. Assigns the
attributes of the output model head. 5.2. FOR each element Adult
in the input model, creates a TaxPayer element in
the output model. 5.2.1. Assigns value to the
attributes of the created TaxPayer element. 5.2.2. IF the Adult element
has referenced jobs: 5.2.2.1. Creates a Tax
element and assigns the values to the attributes. 5.2.2.2. Aggregates the created element (or
None if is not created) to the TaxPayer element. |
Python code |
Pseudocode |
||
|
1. Creates an application
repository to store and process the models. |
||
import emofUC.resource as resource import emofUC.xmlcore as xmlcore import emofUC.GLOBAL as GLOBAL aRep
= resource.AppRepository() xmlcore.PLATFORM = "C:\Users\. .
.\PyEMOF_UC\Workspace" |
|
||
|
2. Opens the input file and loads
the in the repository the model data. |
||
inCountyModelURL = "platform:CountyCity/EagleCounty.xmi" inCountyModel = aRep.loadModel(inCountyModelURL) |
|
||
|
3. Load the metamodel of the
output model. |
||
taxCensus_MM_URL = 'platform:CountyCity/TaxCensus.xmi' |
|
||
|
4. Creates an
empty output model |
||
aRep.loadModel(taxCensus_MM_URL) outTaxCensusModelURI
= "http://unican.es/istr/PyEmofUC/CountyCity/EagleTaxCensus" outTaxCensusModelNsPrefix
= 'etxc' outTaxCensusModel
= aRep.createMdlRepository( uri=outTaxCensusModelURI, mmUrl=taxCensus_MM_URL, nsPrefix=outTaxCensusModelNsPrefix) |
|
||
|
5. Creates
the root container element of the output model: |
||
rootContainer_xmiid = 'etxc.Id_' +
str(outTaxCensusModel.nextXmiId()) theCountyTaxCensus
= outTaxCensusModel.createElement( metaclass='txc.CountyTaxCensus', xmi_id=rootContainer_xmiid) outTaxCensusModel.rootPackage
= theCountyTaxCensus |
|
||
|
5.1 Assigns
the attributes of the output model head. |
||
theCountyTaxCensus.countyName =
inCountyModel.rootPackage.countyName |
|
||
|
5.2 FOR each element Adult in the
input model, creates a TaxPayer element in the
output model. |
||
for theAdult in (theAdult for theAdult in inCountyModel.getElements() if theAdult.isInstanceOf('cnty.Adult')): |
|
||
|
5.2.1.
Assigns value to the attributes of the created
TaxPayer element. |
||
theTaxPayer
= outTaxCensusModel.createElement('txc.TaxPayer', 'etxc.Id_' + str(outTaxCensusModel.nextXmiId())) theTaxPayer.payerName
= theAdult.name theTaxPayer.address
= theAdult.home.address + ' ' +
inCountyModel.rootPackage.countyAddress theTaxPayer.holder = theAdult |
|
||
|
5.2.2. IF the
Adult element has referenced jobs: |
||
theTax
= None if len(theAdult.job)
> 0: |
|
||
|
5.2.2.1. Creates
a Tax element and assigns the values to the
attributes. |
||
theTax
= outTaxCensusModel.createElement('txc.Tax', 'etxc.Id_' + str(outTaxCensusModel.nextXmiId())) theTax.amount
= -100.0 *
len(theAdult.offspring) for theJob in theAdult.job: theTax.amount
+= 0.2 *
theJob.salary theTax.date = GLOBAL.currentDate() |
|
||
|
5.2.2.2. Aggregates
the created element (or None if is not created) to
the TaxPayer element. |
||
theTaxPayer.currentTax
= theTax theCountyTaxCensus.taxPayer.append(theTaxPayer) |
|