Re: [Modeling-users] Re: Implementing inheritance through vertical mapping
Status: Abandoned
Brought to you by:
sbigaret
|
From: Yannick G. <yan...@sa...> - 2003-10-01 14:19:36
|
=2D----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On September 29, 2003 06:20 pm, SoaF-BSD wrote:
> > As I said some time ago, we use Datadesigner [1] to make our XML model
> > and than we pass it to a DOM transformation tool to have a Modeling
> > friendly document.
>
> Perhaps you can provide this tool ? add this to the CVS .. this could be
> great. no ?
Sure !
I already posted it some time ago but it didn't seems to generate a
lot of interests.
Since it's more a convenient internal tool for us than a generic model
converter it's a bit tainted by our usage of the framework : public
keys are public (we serialize them to cross a XML-RPC bridge), only
to-many relations and their inverse are merged and a few points like
that. It might be better to swith to a XSLT transform instead.
Anyway, if it can help someone, I'm willing to make it more generic.
#!/usr/bin/python
# Copyright (C) 2003 Savoir-faire Linux <in...@sa...>
# by Yannick Gingras <yan...@sa...>
# This is dd2pm.
# dd2pm is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# dd2pm is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with Open Beat Box; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 U=
SA
"""dd2pm.py : Datadesigner to Python Modeling convertion tool. """
from xml.dom.minidom import parseString
from xml.dom.ext import PrettyPrint
from xml import xpath
from xml.xpath import Evaluate
import xml
import sys
DOC_SKEL =3D """<?xml version=3D'1.0' encoding=3D'iso-8859-1'?> <model name=
=3D'' />"""
DEF_DB =3D "MySQL"
DEF_NEEDED_ENTITIES =3D []
DEF_CONNECTION =3D {}
DD_PM_TYPE_MAP =3D {"bool": "int",
"integer": "int",
"int": "int",
"varchar": "string",
"numeric": "string",
"real": "float",
"float": "float",
"time": "DateTime",
"char": "string",
"text": "string",
"date": "DateTime"}
DD_PM_EXT_TYPE_MAP =3D {"tinyint": "int",
"smallint": "int",
"decimal": "numeric"}
def getSourceDom(filename):
return parseString(open(filename).read())
def createDoc():
return parseString(DOC_SKEL)
class ModelingDDImporter:
"""Convert a Data Designer shema to PyModeling Model"""
def __init__(self, srcDoc, targetDoc, onlyNeededEnts=3D0):
"""both srcDoc and targetDoc are DOM compliant instances"""
self.srcDoc =3D srcDoc
self.destDoc =3D targetDoc
self.onlyNeededEnts =3D onlyNeededEnts
def startConvertion(self):
self.model =3D self.destDoc.getElementsByTagName("model")[0]
self.project =3D self.srcDoc.getElementsByTagName("project")[0]
self.addModelDefAttrs()
self.convPrjAttrs()
self.extractPrjEntities()
self.mutateEntities()
self.extractRelations()
self.mutateRelations()
def addModelDefAttrs(self):
self.model.setAttribute("adaptorName", DEF_DB)
self.model.setAttribute("connectionDictionary", str(DEF_CONNECTION))
def convPrjAttrs(self):
name =3D self.project.getAttribute("name")
=20
self.model.setAttribute("name", name)
self.model.setAttribute("packageName", name)
def extractPrjEntities(self):
entitiesNode =3D self.srcDoc.getElementsByTagName("entities")[0]
self.prjEntities =3D entitiesNode.getElementsByTagName("entity")
def extractRelations(self):
relationsNode =3D self.srcDoc.getElementsByTagName("relations")[0]
self.relations =3D relationsNode.getElementsByTagName("relation")
def mutateEntities(self):
for entity in self.prjEntities:
needed =3D 1
if self.onlyNeededEnts:
if entity.getAttribute("name") not in DEF_NEEDED_ENTITIES:
needed =3D 0
if needed:
newEntity =3D self.destDoc.createElement('entity')
self.model.appendChild(newEntity)
self.mutateEntity(entity, newEntity)
def mutateRelations(self):
for relation in self.relations:
needed =3D 1
if self.onlyNeededEnts:
parent =3D Evaluate("./parent/text()", relation)[0].nodeVal=
ue
if parent not in DEF_NEEDED_ENTITIES:
needed =3D 0
if needed:
newRelation =3D self.destDoc.createElement('relation')
self.mutateRelation(relation, newRelation)
def getModelEntityWithName(self, name):
modelEntities =3D self.model.getElementsByTagName("entity")
=20
for modelEntity in modelEntities:
if modelEntity.getAttribute("name") =3D=3D name:
return modelEntity
=20
return None
=20
def mutateEntity(self, sourceEntity, destEntity):
# mutate names=20
mapping =3D {"name": ["moduleName", "className", "name"],
"pname": ["externalName"]}
for mapEntry in mapping.items():
val =3D sourceEntity.getAttribute(mapEntry[0])
for attrName in mapEntry[1]:
destEntity.setAttribute(attrName, val)
# set defaults
defaults =3D self.makeDefaults(isReadOnly =3D'0',
isAbstract =3D '0',
typeName =3D '',
parentEntity =3D '')
for attrName, val in defaults.items():
destEntity.setAttribute(attrName, val)
# mutate misc attrs
self.mutateEntAttrs(sourceEntity, destEntity)
def mutateEntAttrs(self, sourceEntity, destEntity):
entAttrsNode =3D sourceEntity.getElementsByTagName("attributes")[0]
entAttrs =3D entAttrsNode.getElementsByTagName("attribute")
for entAttr in entAttrs:
newEntAttr =3D self.destDoc.createElement("attribute")
destEntity.appendChild(newEntAttr)
self.mutateEntAttr(entAttr, newEntAttr)
def mutateEntAttr(self, sourceEntAttr, destEntAttr):
# mutate name
name =3D sourceEntAttr.getAttribute("name")
destEntAttr.setAttribute("name", name)
pname =3D sourceEntAttr.getAttribute("pname")
destEntAttr.setAttribute("columnName", pname)
# set defaults
defaults =3D self.makeDefaults( isClassProperty =3D '1',
width =3D '0',
isRequired =3D '0',
precision =3D '0',
defaultValue =3D 'None',
scale =3D '0',
displayLabel =3D "")
for attrName, val in defaults.items():
destEntAttr.setAttribute(attrName, val)
# mutate real values
mapping =3D {"type": "externalType",
"length": "width"}
for node in sourceEntAttr.childNodes:
if node.nodeType =3D=3D node.ELEMENT_NODE:
tagName =3D node.tagName
try:
value =3D node.firstChild.nodeValue
except AttributeError:
value =3D ""
self.mutateEntAttrType(tagName,
value,
sourceEntAttr,
destEntAttr)
def strToBool(self, str):
if str =3D=3D "true":
return 1
elif str =3D=3D "false":
return 0
else:
raise Exception("String '%s' cannot be converted to bool" % str)
def mutateEntAttrType(self, tagName, value, sourceEntAttr, destEntAttr):
if tagName =3D=3D "type":
if DD_PM_EXT_TYPE_MAP.has_key(value):
value =3D DD_PM_EXT_TYPE_MAP[value]
destEntAttr.setAttribute("externalType", value)
destEntAttr.setAttribute("type", DD_PM_TYPE_MAP[value])
elif tagName =3D=3D "nullable":
destEntAttr.setAttribute("isRequired",
str(not self.strToBool(value)))
elif tagName =3D=3D "unique":
pass
elif tagName =3D=3D "primarykey":
if self.strToBool(value):
newElem =3D self.destDoc.createElement("primaryKey")
newElem.setAttribute("attributeName",
destEntAttr.getAttribute("name"))
destEntAttr.parentNode.appendChild(newElem)
destEntAttr.setAttribute("isRequired", '1')
destEntAttr.setAttribute("defaultValue", '0')
destEntAttr.setAttribute("isClassProperty", '1')
elif tagName =3D=3D "foreignkey":
if self.strToBool(value):
destEntAttr.setAttribute("isClassProperty", '0')
elif tagName =3D=3D "isarray":
pass
elif tagName =3D=3D "imported":
pass
elif tagName =3D=3D "description":
pass
elif tagName =3D=3D "default":
destEntAttr.setAttribute("defaultValue", value)
elif tagName =3D=3D "length":
# tricky, converted to width only if there is no precision
if sourceEntAttr.getElementsByTagName("decimals"):
# use precision
destEntAttr.setAttribute("precision", value)
else:
# use width
destEntAttr.setAttribute("width", value)
elif tagName =3D=3D "decimals":
destEntAttr.setAttribute("scale", value)
else:
raise Exception("Unknowed node type : %s" % tagName)
def mutateRelation(self, sourceRelation, destRelation):
# set defaults
defaults =3D self.makeDefaults(deleteRule=3D'nullify',
isClassProperty=3D'1',
multiplicityUpperBound=3D'-1',
multiplicityLowerBound=3D'0',
displayLabel=3D'',
joinSemantic=3D'0')
for attrName, value in defaults.items():
destRelation.setAttribute(attrName, value)
type =3D xpath.Evaluate("./type/text()", sourceRelation)[0].nodeVal=
ue
if type =3D=3D "ident":
pass # keep the defaults
elif type =3D=3D "inform":
# reverse relation of a to-many
destRelation.setAttribute("multiplicityUpperBound", "1")
destRelation.setAttribute("multiplicityLowerBound", "0")
else:
raise Exception("Can't handle relation type : '%s'" % type)
=20
# append relation to master entity
parentElem =3D sourceRelation.getElementsByTagName("parent")[0]
fromEntity =3D parentElem.firstChild.nodeValue
self.getModelEntityWithName(fromEntity).appendChild(destRelation)
# set the receiver
childElem =3D sourceRelation.getElementsByTagName("child")[0]
toEntity =3D childElem.firstChild.nodeValue
destRelation.setAttribute("destinationEntity", toEntity)
destRelation.setAttribute("name", sourceRelation.getAttribute("name=
"))
relAttrsNode =3D sourceRelation.getElementsByTagName("relationattrs=
")[0]
bindingNode =3D relAttrsNode.getElementsByTagName("attributebind")[=
0]
fromAttr =3D bindingNode.getAttribute("parent")
toAttr =3D bindingNode.getAttribute("child")
joinElem =3D self.destDoc.createElement("join")
joinElem.setAttribute("sourceAttribute", fromAttr)
joinElem.setAttribute("destinationAttribute", toAttr)
destRelation.appendChild(joinElem) =20
=20
def makeDefaults(self, **kwargs):
return kwargs
if __name__ =3D=3D '__main__':
if len(sys.argv) not in [3, 4]:
print """usage : %s [-o] <SOURCE> <DESTINATION>
where <SOURCE> is the path of the source XML model
and <DESTINATION> is where to save the PyModeling XML model
Options :
-o : only convert needed entities""" % (sys.argv[0])
sys.exit(1)
onlyNeededEnts =3D "-o" in sys.argv
=20
srcDoc =3D getSourceDom(sys.argv[-2])
newDoc =3D createDoc()
importer =3D ModelingDDImporter(srcDoc, newDoc, onlyNeededEnts)
importer.startConvertion()
PrettyPrint(newDoc, stream=3Dopen(sys.argv[-1], "w"))
# The END !
=2D --=20
Yannick Gingras
Byte Gardener, Savoir-faire Linux inc.
http://www.savoirfairelinux.com/
=2D----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.2 (GNU/Linux)
iD8DBQE/euJzrhy5Fqn/MRARAt/jAJ0eABfJZURbCLulDyX5oFRaR7DQvgCggzaz
mNqOp1fTT8LkjVcH+KEnqIQ=3D
=3Ds43u
=2D----END PGP SIGNATURE-----
|