view CodeGeneration/JavaCodeGeneration.py @ 74:0acad1d4eb98

back to mainline
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 22 Aug 2025 10:25:30 +0200
parents 156e942b136e
children 1a1ee5a8424c
line wrap: on
line source

#!/usr/bin/env python3

# SPDX-FileCopyrightText: 2023-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
# SPDX-License-Identifier: GPL-3.0-or-later

# Java plugin for Orthanc
# Copyright (C) 2023-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
#
# This program 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 3 of the
# License, or (at your option) any later version.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.


import CodeModel

import argparse
import json
import os
import pystache
import sys


ROOT = os.path.dirname(os.path.realpath(__file__))


##
## Parse the command-line arguments
##

ORTHANC_SDK_DEFAULT_VERSION = CodeModel.ReadOrthancSdkDefaultVersion(os.path.join(ROOT, '..', 'OrthancSDKVersion.cmake'))

parser = argparse.ArgumentParser(description = 'Generate C++ native functions to wrap the Orthanc SDK in Java.')
parser.add_argument('--sdk',
                    default = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
                                           '../Resources/Orthanc/Sdk-%s/orthanc/OrthancCPlugin.h' % ORTHANC_SDK_DEFAULT_VERSION),
                    help = 'Path to the Orthanc SDK')
parser.add_argument('--model',
                    default = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
                                           '../Resources/Orthanc/OrthancPluginCodeModel.json'),
                    help = 'Input code model, as generated by the orthanc project')
parser.add_argument('--target',
                    default = '/tmp/OrthancJavaSDK',
                    help = 'Target folder')
parser.add_argument('--custom-functions',
                    default = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'CustomFunctions.json'),
                    help = 'Input list of custom global functions')
parser.add_argument('--custom-methods',
                    default = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'CustomMethods.json'),
                    help = 'Input list of custom methods')

args = parser.parse_args()

TARGET = os.path.join(args.target, 'be', 'uclouvain', 'orthanc')

try:
    # "exist_ok = True" is not available on Python 2.7, which is still in use on our CIS for Ubuntu 16.04
    os.makedirs(TARGET)
except:
    pass


SDK_VERSION = CodeModel.ReadOrthancSdkVersion(args.sdk)

renderer = pystache.Renderer(
    escape = lambda u: u,  # No escaping
)

model = CodeModel.Load(args.model,
                       customFunctionsPath = args.custom_functions,
                       customMethodsPath = args.custom_methods)


print('** Generating the Java classes to wrap Orthanc SDK %d.%d.%d **' % (SDK_VERSION[0], SDK_VERSION[1], SDK_VERSION[2]))

with open(os.path.join(ROOT, 'JavaNativeSDK.mustache'), 'r') as f:
    template = f.read()

    with open(os.path.join(TARGET, 'NativeSDK.java'), 'w') as g:
        functions = []

        for f in model['native_functions']:
            if CodeModel.IsPrimitiveAvailable(SDK_VERSION, f):
                functions.append(f)

        g.write(renderer.render(template, {
            'functions' : functions
        }))


with open(os.path.join(ROOT, 'JavaFunctions.mustache'), 'r') as f:
    template = f.read()

    # Export the global functions
    with open(os.path.join(TARGET, 'Functions.java'), 'w') as g:
        functions = []

        for f in model['native_functions']:
            if (f['class_name'] == None and
                f['return'].get('is_object') != True and
                CodeModel.IsPrimitiveAvailable(SDK_VERSION, f)):
                functions.append(f)

        g.write(renderer.render(template, {
            'functions' : functions
        }))


for enum in model['enumerations']:
    if CodeModel.IsPrimitiveAvailable(SDK_VERSION, enum):
        enum['values'] = list(filter(lambda value: CodeModel.IsPrimitiveAvailable(SDK_VERSION, value, key_prefix = enum['name']),
                                     enum['values']))

        enum['values'][-1]['last'] = True

        with open(os.path.join(ROOT, 'JavaEnumeration.mustache'), 'r') as f:
            template = f.read()

            with open(os.path.join(TARGET, '%s.java' % enum['short_name']), 'w') as g:
                g.write(renderer.render(template, enum))


with open(os.path.join(ROOT, 'ClassDocumentation.json'), 'r') as f:
    classDocumentation = json.loads(f.read())


for cls in model['classes']:
    if CodeModel.IsPrimitiveAvailable(SDK_VERSION, cls):
        with open(os.path.join(ROOT, 'JavaClass.mustache'), 'r') as f:
            template = f.read()

            constructors = []
            for f in model['native_functions']:
                if (f['class_name'] == None and
                    f['return'].get('is_object') == True and
                    f['return']['class_name'] == cls['name'] and
                    CodeModel.IsPrimitiveAvailable(SDK_VERSION, f)):
                    constructors.append(f)

            with open(os.path.join(TARGET, '%s.java' % cls['short_name']), 'w') as g:
                if not cls['name'] in classDocumentation:
                    raise Exception('No global documentation for class: %s' % cls['name'])

                methods = []
                for method in cls['wrapped_methods']:
                    if CodeModel.IsPrimitiveAvailable(SDK_VERSION, method):
                        methods.append(method)

                g.write(renderer.render(template, {
                    'destructor' : cls.get('destructor'),
                    'class_name' : cls['short_name'],
                    'methods' : methods,
                    'constructors' : constructors,
                    'has_documentation' : True,
                    'documentation' : classDocumentation[cls['name']],
                }))