summaryrefslogtreecommitdiff
path: root/org.genivi.commonapi.core/src/org/genivi/commonapi/core/generator/FrancaGeneratorExtensions.xtend
blob: 9e7c4f317db61591f8393ef07374d520b9c1357f (plain)
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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
147
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
184
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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
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
263
264
265
266
267
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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
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
400
401
402
403
/* Copyright (C) 2013 BMW Group
 * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
 * Author: Juergen Gehring (juergen.gehring@bmw.de)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.genivi.commonapi.core.generator

import com.google.common.primitives.Ints
import java.util.List
import org.eclipse.core.runtime.Path
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.plugin.EcorePlugin
import org.eclipse.emf.ecore.resource.Resource
import org.franca.core.franca.FBasicTypeId
import org.franca.core.franca.FInterface
import org.franca.core.franca.FMethod
import org.franca.core.franca.FModel
import org.franca.core.franca.FModelElement
import org.franca.core.franca.FTypeCollection
import org.franca.core.franca.FTypeRef
import org.franca.core.franca.FAttribute
import org.franca.core.franca.FBroadcast
import org.franca.core.franca.FEnumerationType

import static com.google.common.base.Preconditions.*

class FrancaGeneratorExtensions {
    def getFullyQualifiedName(FModelElement fModelElement) {
        if (fModelElement.eContainer instanceof FModel)
            return (fModelElement.eContainer as FModel).name + '.' + fModelElement.name
        return (fModelElement.eContainer as FModelElement).fullyQualifiedName + '.' + fModelElement.name
    }

    def splitCamelCase(String string) {
        string.split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])")
    }

    def FModel getModel(FModelElement fModelElement) {
        if (fModelElement.eContainer instanceof FModel)
            return (fModelElement.eContainer as FModel)
        return (fModelElement.eContainer as FModelElement).model
    }

    def getDirectoryPath(FModel fModel) {
        fModel.name.replace('.', '/')
    }
    
    def getDefineName(FModelElement fModelElement) {
        val defineSuffix = '_' + fModelElement.name.splitCamelCase.join('_').toUpperCase

        if (fModelElement.eContainer instanceof FModelElement)
            return (fModelElement.eContainer as FModelElement).defineName + defineSuffix

        return (fModelElement.eContainer as FModel).defineName + defineSuffix
    }

    def getDefineName(FModel fModel) {
        fModel.name.toUpperCase.replace('.', '_')
    }

    def private dispatch List<String> getNamespaceAsList(FModel fModel) {
        newArrayList(fModel.name.split("\\."))
    }

    def private dispatch List<String> getNamespaceAsList(FModelElement fModelElement) {
        val namespaceList = fModelElement.eContainer.namespaceAsList
        val isRootElement = fModelElement.eContainer instanceof FModel

        if (!isRootElement)
            namespaceList.add((fModelElement.eContainer as FModelElement).name)

        return namespaceList
    }

    def private getSubnamespaceList(FModelElement destination, EObject source) {
        val sourceNamespaceList = source.namespaceAsList
        val destinationNamespaceList = destination.namespaceAsList
        val maxCount = Ints::min(sourceNamespaceList.size, destinationNamespaceList.size)
        var dropCount = 0

        while (dropCount < maxCount && sourceNamespaceList.get(dropCount).equals(destinationNamespaceList.get(dropCount)))
            dropCount = dropCount + 1

        return destinationNamespaceList.drop(dropCount)
    }

    def getRelativeNameReference(FModelElement destination, EObject source) {
        var nameReference = destination.name

        if (!destination.eContainer.equals(source)) {
            val subnamespaceList = destination.getSubnamespaceList(source)
            if (!subnamespaceList.empty)
                nameReference = subnamespaceList.join('::') + '::' + nameReference
        }

        return nameReference
    }

    def getHeaderFile(FTypeCollection fTypeCollection) {
        fTypeCollection.name + ".h"
    }

    def getHeaderPath(FTypeCollection fTypeCollection) {
        fTypeCollection.model.directoryPath + '/' + fTypeCollection.headerFile
    }

    def getSourceFile(FTypeCollection fTypeCollection) {
        fTypeCollection.name + ".cpp"
    }

    def getSourcePath(FTypeCollection fTypeCollection) {
        fTypeCollection.model.directoryPath + '/' + fTypeCollection.sourceFile
    }

    def getProxyBaseHeaderFile(FInterface fInterface) {
        fInterface.name + "ProxyBase.h"
    }

    def getProxyBaseHeaderPath(FInterface fInterface) {
        fInterface.model.directoryPath + '/' + fInterface.proxyBaseHeaderFile
    }

    def getProxyBaseClassName(FInterface fInterface) {
        fInterface.name + 'ProxyBase'
    }

    def getProxyHeaderFile(FInterface fInterface) {
        fInterface.name + "Proxy.h"
    }

    def getProxyHeaderPath(FInterface fInterface) {
        fInterface.model.directoryPath + '/' + fInterface.proxyHeaderFile
    }

    def getStubDefaultHeaderFile(FInterface fInterface) {
        fInterface.name + "StubDefault.h"
    }

    def getStubDefaultHeaderPath(FInterface fInterface) {
        fInterface.model.directoryPath + '/' + fInterface.stubDefaultHeaderFile
    }

    def getStubDefaultClassName(FInterface fInterface) {
        fInterface.name + 'StubDefault'
    }
    
    def getStubDefaultSourceFile(FInterface fInterface) {
        fInterface.name + "StubDefault.cpp"
    }

    def getStubDefaultSourcePath(FInterface fInterface) {
        fInterface.model.directoryPath + '/' + fInterface.getStubDefaultSourceFile
    }
    
    def getStubRemoteEventClassName(FInterface fInterface) {
        fInterface.name + 'StubRemoteEvent'
    }

    def getStubAdapterClassName(FInterface fInterface) {
        fInterface.name + 'StubAdapter'
    }
    
    def getStubHeaderFile(FInterface fInterface) {
        fInterface.name + "Stub.h"
    }

    def getStubHeaderPath(FInterface fInterface) {
        fInterface.model.directoryPath + '/' + fInterface.stubHeaderFile
    }

    def getStubClassName(FInterface fInterface) {
        fInterface.name + 'Stub'
    }

    def hasAttributes(FInterface fInterface) {
        !fInterface.attributes.empty
    }

    def hasBroadcasts(FInterface fInterface) {
        !fInterface.broadcasts.empty
    }

    def generateDefinition(FMethod fMethod) {
        fMethod.generateDefinitionWithin(null)
    }

    def generateDefinitionWithin(FMethod fMethod, String parentClassName) {
        var definition = 'void '

        if (!parentClassName.nullOrEmpty)
            definition = definition + parentClassName + '::'

        definition = definition + fMethod.name + '(' + fMethod.generateDefinitionSignature + ')'

        return definition
    }

    def generateDefinitionSignature(FMethod fMethod) {
        var signature = fMethod.inArgs.map['const ' + type.getNameReference(fMethod.model) + '& ' + name].join(', ')

        if (!fMethod.inArgs.empty)
            signature = signature + ', '

        signature = signature + 'CommonAPI::CallStatus& callStatus'

        if (fMethod.hasError)
            signature = signature + ', ' + fMethod.getErrorNameReference(fMethod.eContainer) + '& methodError'

        if (!fMethod.outArgs.empty)
            signature = signature + ', ' + fMethod.outArgs.map[type.getNameReference(fMethod.model) + '& ' + name].join(', ')

        return signature
    }
    
    def generateStubSignature(FMethod fMethod) {
        var signature = fMethod.inArgs.map[type.getNameReference(fMethod.model) + ' ' + name].join(', ')

        if (fMethod.hasError)
            signature = signature + ', ' + fMethod.getErrorNameReference(fMethod.eContainer) + '& methodError'

        if (!fMethod.outArgs.empty)
            signature = signature + ', ' + fMethod.outArgs.map[type.getNameReference(fMethod.model) + '& ' + name].join(', ')

        return signature
    }

    def generateAsyncDefinition(FMethod fMethod) {
        fMethod.generateAsyncDefinitionWithin(null)
    }

    def generateAsyncDefinitionWithin(FMethod fMethod, String parentClassName) {
        var definition = 'std::future<CommonAPI::CallStatus> '

        if (!parentClassName.nullOrEmpty)
            definition = definition + parentClassName + '::'

        definition = definition + fMethod.name + 'Async(' + fMethod.generateAsyncDefinitionSignature + ')'

        return definition
    }

    def generateAsyncDefinitionSignature(FMethod fMethod) {
        var signature = fMethod.inArgs.map['const ' + type.getNameReference(fMethod.model) + '& ' + name].join(', ')
        if (!fMethod.inArgs.empty)
            signature = signature + ', '
        return signature + fMethod.asyncCallbackClassName + ' callback'
    }

    def getAsyncCallbackClassName(FMethod fMethod) {
        fMethod.name.toFirstUpper + 'AsyncCallback'
    }

    def hasError(FMethod fMethod) {
        fMethod.errorEnum != null || fMethod.errors != null
    }

    def getErrorNameReference(FMethod fMethod, EObject source) {
        checkArgument(fMethod.hasError, 'FMethod has no error: ' + fMethod)
        if (fMethod.errorEnum != null)
            return fMethod.errorEnum.getRelativeNameReference(source)

        var errorNameReference = fMethod.errors.errorName
        if (!fMethod.eContainer.equals(source)) 
            errorNameReference = (fMethod.eContainer as FInterface).getRelativeNameReference(source) + '::' + errorNameReference
        return errorNameReference
    }

    def getClassName(FAttribute fAttribute) {
        fAttribute.name.toFirstUpper + 'Attribute'
    }

    def generateGetMethodDefinition(FAttribute fAttribute) {
        fAttribute.generateGetMethodDefinitionWithin(null)
    }

    def generateGetMethodDefinitionWithin(FAttribute fAttribute, String parentClassName) {
        var definition = fAttribute.className + '& '

        if (!parentClassName.nullOrEmpty)
            definition = parentClassName + '::' + definition + parentClassName + '::'

        definition = definition + 'get' + fAttribute.className + '()'

        return definition
    }
   
    def isReadonly(FAttribute fAttribute) {
        fAttribute.readonly != null
    }

    def isObservable(FAttribute fAttribute) {
        fAttribute.noSubscriptions == null
    }

    def getStubAdapterClassFireChangedMethodName(FAttribute fAttribute) {
        'fire' + fAttribute.name.toFirstUpper + 'AttributeChanged'
    }

    def getStubRemoteEventClassSetMethodName(FAttribute fAttribute) {
        'onRemoteSet' + fAttribute.name.toFirstUpper + 'Attribute'
    }

    def getStubRemoteEventClassChangedMethodName(FAttribute fAttribute) {
        'onRemote' + fAttribute.name.toFirstUpper + 'AttributeChanged'
    }

    def getStubClassGetMethodName(FAttribute fAttribute) {
        'get' + fAttribute.name.toFirstUpper + 'Attribute'
    }

    def getClassName(FBroadcast fBroadcast) {
        fBroadcast.name.toFirstUpper + 'Event'
    }

    def generateGetMethodDefinition(FBroadcast fBroadcast) {
        fBroadcast.generateGetMethodDefinitionWithin(null)
    }

    def generateGetMethodDefinitionWithin(FBroadcast fBroadcast, String parentClassName) {
        var definition = fBroadcast.className + '& '

        if (!parentClassName.nullOrEmpty)
            definition = parentClassName + '::' + definition + parentClassName + '::'

        definition = definition + 'get' + fBroadcast.className + '()'

        return definition
    }

    def getStubAdapterClassFireEventMethodName(FBroadcast fBroadcast) {
        'fire' + fBroadcast.name.toFirstUpper + 'Event'
    }

    def getNameReference(FTypeRef destination, EObject source) {
        if (destination.derived != null)
            return destination.derived.getRelativeNameReference(source)
        return destination.predefined.primitiveTypeName
    }

    def getErrorName(FEnumerationType fMethodErrors) {
        checkArgument(fMethodErrors.eContainer instanceof FMethod, 'Not FMethod errors')
        (fMethodErrors.eContainer as FMethod).name + 'Error'
    }

    def getBackingType(FEnumerationType fEnumerationType) {
        return FBasicTypeId::INT32        
    }

    def getPrimitiveTypeName(FBasicTypeId fBasicTypeId) {
        switch fBasicTypeId {
            case FBasicTypeId::BOOLEAN: "bool"
            case FBasicTypeId::INT8: "int8_t"
            case FBasicTypeId::UINT8: "uint8_t"
            case FBasicTypeId::INT16: "int16_t"
            case FBasicTypeId::UINT16: "uint16_t"
            case FBasicTypeId::INT32: "int32_t"
            case FBasicTypeId::UINT32: "uint32_t"
            case FBasicTypeId::INT64: "int64_t"
            case FBasicTypeId::UINT64: "uint64_t"
            case FBasicTypeId::FLOAT: "float"
            case FBasicTypeId::DOUBLE: "double"
            case FBasicTypeId::STRING: "std::string"
            case FBasicTypeId::BYTE_BUFFER: "CommonAPI::ByteBuffer"
            default: throw new IllegalArgumentException("Unsupported basic type: " + fBasicTypeId.name)
        }
    }
    
    def generateCppNamespace(FModel fModel) '''
        «fModel.namespaceAsList.map[toString].join("::")»::'''

    def generateNamespaceBeginDeclaration(FModel fModel) '''
        «FOR subnamespace : fModel.namespaceAsList»
            namespace «subnamespace» {
        «ENDFOR»
    '''

    def generateNamespaceEndDeclaration(FModel fModel) '''
        «FOR subnamespace : fModel.namespaceAsList.reverse»
            } // namespace «subnamespace»
        «ENDFOR»
    '''

    def isFireAndForget(FMethod fMethod) {
        return !fMethod.fireAndForget.nullOrEmpty
    }

    def getFilePath(Resource resource) {
        if (resource.URI.file)
            return resource.URI.toFileString

        val platformPath = new Path(resource.URI.toPlatformString(true))
        val file = EcorePlugin::workspaceRoot.getFile(platformPath)

        return file.location.toString
    }

    def generateCommonApiLicenseHeader() '''
        /* This Source Code Form is subject to the terms of the Mozilla Public
         * License, v. 2.0. If a copy of the MPL was not distributed with this
         * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    '''
}