smp2_A_mapper.py 18.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# (C) Semantix Information Technologies.
#
# Semantix Information Technologies is licensing the code of the
# Data Modelling Tools (DMT) in the following dual-license mode:
#
# Commercial Developer License:
#       The DMT Commercial Developer License is the suggested version
# to use for the development of proprietary and/or commercial software.
# This version is for developers/companies who do not want to comply
# with the terms of the GNU Lesser General Public License version 2.1.
#
# GNU LGPL v. 2.1:
#       This version of DMT is the one to use for the development of
# applications, when you are willing to comply with the terms of the
# GNU Lesser General Public License version 2.1.
#
# Note that in both cases, there are no charges (royalties) for the
# generated code.
#
'''This contains the implementation of model level mapping
of ASN.1 constructs to SMP2 ones. It is used as a backend of Semantix's
code generator A.'''

import os
import re
import random

from commonPy.asnAST import AsnMetaMember, AsnChoice, AsnSet, AsnSequence, AsnSequenceOf, AsnSetOf
from commonPy.asnParser import g_names, g_leafTypeDict, CleanNameForAST
from commonPy.utility import panic, warn
31
from commonPy.cleanupNodes import SetOfBadTypenames
32
33
34
35
36
37
38
39
40

g_catalogueXML = None
g_innerTypes = {}
g_uniqueStringOfASN1files = ""
g_outputDir = "."
g_asnFiles = None


def Version():
41
    print("Code generator: " + "$Id: smp2_A_mapper.py 1932 2010-06-15 13:41:15Z ttsiodras $")  # pragma: no cover
42
43


44
# noinspection PyDefaultArgument
45
def getUID(strIdentifier, idStore={}):  # pylint: disable=dangerous-default-value
46
47
    def h(digits):
        ret = ""
48
        for _ in range(0, digits):
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
            ret += random.choice('0123456789abcdef')
        return ret
    if strIdentifier not in idStore:
        idStore[strIdentifier] = h(8) + '-' + h(4) + '-' + h(4) + '-' + h(4) + '-' + h(12)
    return idStore[strIdentifier]


g_dependencyGraph = {}


def FixupAstForSMP2():
    '''
    Find all the SEQUENCE, CHOICE and SEQUENCE OFs
    and make sure the contained types are not "naked" (i.e. unnamed)
    '''
    internalNo = 1
65
66
67

    def neededToAddPseudoType():
        nonlocal internalNo
68
        addedNewPseudoType = False
69
        listOfTypenames = sorted(list(g_names.keys()) + list(g_innerTypes.keys()))
70
71
        for nodeTypename in listOfTypenames:
            node = g_names[nodeTypename]
72
            if isinstance(node, (AsnChoice, AsnSequence, AsnSet)):
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
                for child in node._members:
                    if not isinstance(child[1], AsnMetaMember):
                        internalName = newname = "TaStE_" + CleanNameForAST(child[0].capitalize() + "_type")
                        while internalName in g_names:
                            internalName = (newname + "_%d") % internalNo  # pragma: no cover
                            internalNo += 1                                # pragma: no cover
                        g_names[internalName] = child[1]
                        child[1]._isArtificial = True
                        g_leafTypeDict[internalName] = child[1]._leafType
                        child[1] = AsnMetaMember(asnFilename=child[1]._asnFilename, containedType=internalName)
                        addedNewPseudoType = True
                        g_innerTypes[internalName] = 1
                        g_dependencyGraph.setdefault(nodeTypename, {})
                        g_dependencyGraph[nodeTypename][internalName] = 1
                    else:
                        g_dependencyGraph.setdefault(nodeTypename, {})
                        g_dependencyGraph[nodeTypename][child[1]._containedType] = 1
90
            elif isinstance(node, (AsnSequenceOf, AsnSetOf)):
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
                if not isinstance(node._containedType, str):
                    internalName = newname = "TaStE_" + "internalSeqOf_type"
                    while internalName in g_names:
                        internalName = (newname + "_%d") % internalNo  # pragma: no cover
                        internalNo += 1                                # pragma: no cover
                    g_names[internalName] = node._containedType
                    node._containedType._isArtificial = True
                    g_leafTypeDict[internalName] = node._containedType._leafType
                    node._containedType = internalName
                    addedNewPseudoType = True
                    g_innerTypes[internalName] = 1
                    g_dependencyGraph.setdefault(nodeTypename, {})
                    g_dependencyGraph[nodeTypename][internalName] = 1
                else:
                    g_dependencyGraph.setdefault(nodeTypename, {})
                    g_dependencyGraph[nodeTypename][node._containedType] = 1
107
108
109
110
111
        return addedNewPseudoType

    while True:
        if not neededToAddPseudoType():
            break
112
113
114
115
116


g_bStartupRun = False


117
def OnStartup(unused_modelingLanguage, asnFiles, outputDir, unused_badTypes):
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
    '''
    Smp2 cannot represent constraint changes in unnamed inner types
    e.g. this...
        SEQUENCE (SIZE(20)) OF INTEGER (0 .. 12)
    ...would need the declaration of an "inner" type that would carry the constraint (0..12).

    We do this AST "fixup" with this function.
    '''
    global g_uniqueStringOfASN1files
    if isinstance(asnFiles, str):
        g_uniqueStringOfASN1files = CleanName(asnFiles.lower().replace(".asn1", "").replace(".asn", ""))  # pragma: nocover
    else:
        g_uniqueStringOfASN1files = "_".join(CleanName(x.lower().replace(".asn1", "").replace(".asn", "")) for x in asnFiles)
    global g_bStartupRun
    if g_bStartupRun:
        # Must run only once, no more.
        return  # pragma: no cover
    g_bStartupRun = True
    global g_outputDir
    g_outputDir = outputDir
    global g_asnFiles
    g_asnFiles = asnFiles
    FixupAstForSMP2()


def CleanName(fieldName):
    return re.sub(r'[^a-zA-Z0-9_]', '_', fieldName)


147
def OnBasic(unused_nodeTypename, unused_node, unused_leafTypeDict):
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
    pass  # pragma: no cover


def CreateBasic(nodeTypename, node, leafTypeDict):
    baseType = leafTypeDict[node._leafType]
    xsitype = {
        'INTEGER': 'Types:Integer',
        'REAL': 'Types:Float',
        'BOOLEAN': 'Types:Integer',
        'OCTET STRING': 'Types:Array'
    }[baseType]
    span = ""
    if baseType == 'BOOLEAN':
        span = 'Minimum="0" Maximum="1"'
    elif baseType in ['INTEGER', 'REAL'] and node._range:
        span = 'Minimum="%s" Maximum="%s"' % (node._range[0], node._range[-1])
    elif baseType == 'OCTET STRING':
        span = 'Size="%s"' % node._range[-1]
    uid = getUID(nodeTypename)
    d = {"uid": uid, "xsitype": xsitype, "name": CleanName(nodeTypename), "range": span}
    g_catalogueXML.write('    <Type xsi:type="%(xsitype)s" Id="ID_%(uid)s" Name="%(name)s" %(range)s>\n' % d)
    g_catalogueXML.write('      <Description>in file "%s", line: %s%s</Description>\n' %
                         (node._asnFilename, node._lineno, ", artificial" if node._isArtificial else ""))
    g_catalogueXML.write('      <Uuid>%s</Uuid>\n' % uid)
    nativeSMP2type = {
        'INTEGER': "Int64",
        'REAL': "Float64",
        'BOOLEAN': "UInt8",
        'OCTET STRING': 'UInt8'
    }[baseType]
    element = "ItemType" if baseType == "OCTET STRING" else "PrimitiveType"
    g_catalogueXML.write('      <%s xlink:title="PrimitiveType %s" xlink:href="http://www.esa.int/2005/10/Smp#%s" />\n' %
                         (element, nativeSMP2type, nativeSMP2type))
    g_catalogueXML.write('    </Type>\n')


184
def OnSequence(unused_nodeTypename, unused_node, unused_leafTypeDict):
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
    pass  # pragma: no cover


def CreateSequence(nodeTypename, node, unused_leafTypeDict):
    uid = getUID(nodeTypename)
    d = {"uid": uid, "name": CleanName(nodeTypename)}
    g_catalogueXML.write('    <Type xsi:type="Types:Structure" Id="ID_%(uid)s" Name="%(name)s">\n' % d)
    g_catalogueXML.write('      <Description>in file "%s", line: %s%s</Description>\n' %
                         (node._asnFilename, node._lineno, ", artificial" if node._isArtificial else ""))
    g_catalogueXML.write('      <Uuid>%s</Uuid>\n' % uid)
    for c in node._members:
        uidc = getUID(nodeTypename + c[0] + "_" + c[1]._containedType)
        g_catalogueXML.write('      <Field Id="ID_%s" Name="%s"  Visibility="public">\n' %
                             (uidc, CleanName(c[0]),))
        g_catalogueXML.write('        <Description></Description>\n')
        childNode = c[1]
        if isinstance(childNode, AsnMetaMember):
            g_catalogueXML.write('        <Type xlink:title="%s" xlink:href="#ID_%s"></Type>\n' %
                                 (CleanName(c[1]._containedType), getUID(c[1]._containedType)))
        else:  # pragma: no cover
            panic("Unexpected child node in SEQUENCE")  # pragma: no cover

        g_catalogueXML.write('      </Field>\n')
    g_catalogueXML.write('    </Type>\n')


211
def OnSet(unused_nodeTypename, unused_node, unused_leafTypeDict):
212
213
214
    pass  # pragma: no cover


215
def OnEnumerated(unused_nodeTypename, unused_node, unused_leafTypeDict):
216
217
218
219
220
221
222
223
224
225
226
227
228
229
    pass  # pragma: no cover


def CreateEnumerated(nodeTypename, node, unused_leafTypeDict):
    uid = getUID(nodeTypename)
    d = {"uid": uid, "name": CleanName(nodeTypename)}
    g_catalogueXML.write('    <Type xsi:type="Types:Enumeration" Id="ID_%(uid)s" Name="%(name)s">\n' % d)
    g_catalogueXML.write('      <Description>in file "%s", line: %s%s</Description>\n' %
                         (node._asnFilename, node._lineno, ", artificial" if node._isArtificial else ""))
    g_catalogueXML.write('      <Uuid>%s</Uuid>\n' % uid)
    for opt in node._members:
        uido = getUID(nodeTypename + "_option_" + opt[0])
        g_catalogueXML.write('      <Literal Name="%s" Value="%s" Id="ID_%s" />\n' %
                             (opt[0], opt[1], uido))
230
    # g_catalogueXML.write('      <NativeType xlink:href="http://www.esa.int/2005/10/Smp#%s" />\n' % nativeSMP2type)
231
232
233
    g_catalogueXML.write('    </Type>\n')


234
def OnSequenceOf(unused_nodeTypename, unused_node, unused_leafTypeDict):
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
    pass  # pragma: no cover


def CreateSequenceOf(nodeTypename, node, unused_leafTypeDict):
    uid = getUID(nodeTypename)
    d = {"uid": uid, "name": CleanName(nodeTypename), "sz": node._range[-1]}
    g_catalogueXML.write('    <Type xsi:type="Types:Array" Id="ID_%(uid)s" Name="%(name)s" Size="%(sz)s">\n' % d)
    g_catalogueXML.write('      <Description>in file "%s", line: %s%s</Description>\n' %
                         (node._asnFilename, node._lineno, ", artificial" if node._isArtificial else ""))
    g_catalogueXML.write('      <Uuid>%s</Uuid>\n' % uid)
    reftype = node._containedType
    if isinstance(reftype, str):
        g_catalogueXML.write('      <ItemType xlink:href="#ID_%s" />\n' % getUID(reftype))
    else:  # pragma: no cover
        panic("FixupAstForSMP2 failed to create a pseudoType for %s" % nodeTypename)  # pragma: no cover
#    elif isinstance(reftype, AsnBasicNode):
#        baseType = reftype._leafType
#        smpType = {
#            'INTEGER': 'Int64',
#            'REAL': 'Float64',
#            'BOOLEAN': 'Bool',
#            'OCTET STRING': 'String8'
#        }[baseType]
#        g_catalogueXML.write('      <ItemType xlink:title="PrimitiveType %s" xlink:href="http://www.esa.int/2005/10/Smp#%s" />\n' %
#            (smpType, smpType))
    g_catalogueXML.write('    </Type>\n')


263
def OnSetOf(unused_nodeTypename, unused_node, unused_leafTypeDict):
264
265
266
    pass  # pragma: no cover


267
def OnChoice(unused_nodeTypename, unused_unused_node, unused_unused_leafTypeDict):
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
    pass  # pragma: no cover


def CreateChoice(nodeTypename, node, _):
    uid = getUID(nodeTypename)
    d = {"uid": uid, "name": CleanName(nodeTypename)}
    g_catalogueXML.write('    <Type xsi:type="Types:Structure" Id="ID_%(uid)s" Name="%(name)s">\n' % d)
    g_catalogueXML.write('      <Description>in file "%s", line: %s%s</Description>\n' %
                         (node._asnFilename, node._lineno, ", artificial" if node._isArtificial else ""))
    g_catalogueXML.write('      <Uuid>%s</Uuid>\n' % uid)
    uidIdx = getUID(nodeTypename + "_choiceIndex")
    g_catalogueXML.write('      <Field Id="ID_%s" Name="choiceIdx" Visibility="public">\n' % uidIdx)
    g_catalogueXML.write('        <Description>CHOICE selector field</Description>\n')
    g_catalogueXML.write('        <Type xlink:title="PrimitiveType UInt8" xlink:href="http://www.esa.int/2005/10/Smp#UInt8"></Type>\n')
    g_catalogueXML.write('      </Field>\n')
    for c in node._members:
        uidc = getUID(nodeTypename + c[0] + "_" + c[1]._containedType)
        g_catalogueXML.write('      <Field Id="ID_%s" Name="%s"  Visibility="public">\n' %
                             (uidc, CleanName(c[0]),))
        g_catalogueXML.write('        <Description></Description>\n')
        childNode = c[1]
        if isinstance(childNode, AsnMetaMember):
            g_catalogueXML.write('        <Type xlink:title="%s" xlink:href="#ID_%s"></Type>\n' %
                                 (CleanName(c[1]._containedType), getUID(c[1]._containedType)))
        else:  # pragma: no cover
            panic("Unexpected child node in SEQUENCE")   # pragma: no cover
        g_catalogueXML.write('      </Field>\n')
    g_catalogueXML.write('    </Type>\n')


g_bShutdownRun = False


301
def OnShutdown(badTypes: SetOfBadTypenames):
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
    global g_bShutdownRun
    if g_bShutdownRun:
        return   # pragma: no cover
    g_bShutdownRun = True

    global g_catalogueXML
    g_catalogueXML = open(g_outputDir + os.sep + g_uniqueStringOfASN1files + ".cat", 'w')
    g_catalogueXML.write('''\
<?xml version="1.0" encoding="UTF-8"?>
<Catalogue:Catalogue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:Catalogue="http://www.esa.int/2005/10/Smdl/Catalogue" xmlns:Types="http://www.esa.int/2005/10/Core/Types" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="http://www.esa.int/2005/10/Smdl/Catalogue Catalogue.xsd" Id="%(uniqid)s" Name="%(uniqid)s" Creator="taste" Date="2012-02-02T09:26:40.909Z" Version="1.0">
''' % {'uniqid': g_uniqueStringOfASN1files})
    g_catalogueXML.write('  <Description>Catalogue with types used in "%s"</Description>\n' %
                         (g_asnFiles if isinstance(g_asnFiles, str) else '","'.join(g_asnFiles)))
    g_catalogueXML.write('  <Namespace Id="ID_%s" Name="DataTypes">\n' % getUID("DataTypes"))
    g_catalogueXML.write('    <Description>Types used in "%s"</Description>\n' %
                         (g_asnFiles if isinstance(g_asnFiles, str) else '","'.join(g_asnFiles)))
    typenameList = []
319
    for nodeTypename in sorted(list(g_innerTypes.keys()) + list(g_names.keys())):
320
        if nodeTypename in badTypes:
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
            continue
        if nodeTypename not in typenameList:
            typenameList.append(nodeTypename)
    typesDoneSoFar = {}

    workRemains = True
    while workRemains:
        workRemains = False
        for nodeTypename in typenameList:

            # only emit XML chunks for types with no more dependencies
            if nodeTypename in g_dependencyGraph and g_dependencyGraph[nodeTypename]:
                continue

            # only emit each type once
            if nodeTypename in typesDoneSoFar:
                continue
            typesDoneSoFar[nodeTypename] = 1

            # if we process even one type, deps may be removed, so scan again next time
            workRemains = True

            # make sure we know what leaf type this node is
            node = g_names[nodeTypename]
345
            assert nodeTypename in g_leafTypeDict
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
            leafType = g_leafTypeDict[nodeTypename]
            if leafType in ['BOOLEAN', 'INTEGER', 'REAL', 'OCTET STRING']:
                CreateBasic(nodeTypename, node, g_leafTypeDict)
            elif leafType in ['SEQUENCE', 'SET']:
                CreateSequence(nodeTypename, node, g_leafTypeDict)
            elif leafType == 'CHOICE':
                CreateChoice(nodeTypename, node, g_leafTypeDict)
            elif leafType in ['SEQUENCEOF', 'SETOF']:
                CreateSequenceOf(nodeTypename, node, g_leafTypeDict)
            elif leafType == 'ENUMERATED':
                CreateEnumerated(nodeTypename, node, g_leafTypeDict)
            else:  # pragma: no cover
                warn("Ignoring unsupported node type: %s (%s)" % (leafType, nodeTypename))  # pragma: no cover

            # eliminate nodeTypename from dependency lists
            for t in typenameList:
                if t in g_dependencyGraph and nodeTypename in g_dependencyGraph[t]:
                    del g_dependencyGraph[t][nodeTypename]

    g_catalogueXML.write("  </Namespace>\n")
    g_catalogueXML.write("</Catalogue:Catalogue>\n")
    g_catalogueXML.close()

    pkgFile = open(g_uniqueStringOfASN1files + '.pkg', 'w')
    pkgFile.write('''<?xml version="1.0" encoding="UTF-8"?>
<Package:Package xmlns:Package="http://www.esa.int/2005/10/Smdl/Package" xmlns:xlink="http://www.w3.org/1999/xlink" Id="%(uniqid)s" Name="%(uniqid)s" Creator="TASTE" Date="2012-06-27T00:00:00.000Z" Version="1.0">\n''' % {'uniqid': g_uniqueStringOfASN1files})

    for nodeTypename in typenameList:
        node = g_names[nodeTypename]
        uid = getUID(nodeTypename)
        leafType = g_leafTypeDict[nodeTypename]
        if leafType in ['BOOLEAN', 'INTEGER', 'REAL', 'OCTET STRING']:
            baseType = g_leafTypeDict[node._leafType]
            xsitype = {
                'INTEGER': 'Integer',
                'REAL': 'Float',
                'BOOLEAN': 'Integer',
                'OCTET STRING': 'Array'
            }[baseType]
        elif leafType in ['SEQUENCE', 'SET', 'CHOICE']:
            xsitype = 'Structure'
        elif leafType in ['SEQUENCEOF', 'SETOF']:
            xsitype = 'Array'
        elif leafType == 'ENUMERATED':
            xsitype = 'Enumeration'
        else:  # pragma: no cover
            warn("Ignoring unsupported node type: %s (%s)" % (leafType, nodeTypename))  # pragma: no cover
            continue
        d = {"uid": uid, "xsitype": xsitype, "name": CleanName(nodeTypename), 'uniqid': g_uniqueStringOfASN1files}
        pkgFile.write('    <Implementation Uuid="%(uid)s">\n' % d)
        pkgFile.write('       <Type xlink:title="%(xsitype)s %(name)s" xlink:href="%(uniqid)s.cat#ID_%(uid)s" />\n' % d)
        pkgFile.write('    </Implementation>\n')
    pkgFile.write('</Package:Package>')
    pkgFile.close()
400

401
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4