Tony CHEMIT pushed to branch develop at ultreiaio / ird-observe

Commits:

22 changed files:

Changes:

  • client/datasource/actions/src/main/i18n/templates/reportHtmlExport_en_GB.ftl
    ... ... @@ -199,95 +199,11 @@
    199 199
             }
    
    200 200
         </style>
    
    201 201
         <script type="application/javascript">
    
    202
    -
    
    203
    -        function searchValue() {
    
    204
    -            return searchOption.checked;
    
    205
    -        }
    
    206
    -
    
    207
    -        function resizableValue() {
    
    208
    -            return resizableOption.checked;
    
    209
    -        }
    
    210
    -
    
    211
    -        function sortValue() {
    
    212
    -            return sortOption.checked;
    
    213
    -        }
    
    214
    -
    
    215
    -        function paginationValue() {
    
    216
    -            return paginationOption.checked ? {limit: paginationSizeOption.value} : false;
    
    217
    -        }
    
    218
    -
    
    219
    -        function toggleSearch(config, source) {
    
    220
    -            let newValue = source.checked;
    
    221
    -            // console.info("Toggle search to: " + newValue);
    
    222
    -            updateGrid(config);
    
    223
    -        }
    
    224
    -
    
    225
    -        function toggleResizable(config, source) {
    
    226
    -            let newValue = source.checked;
    
    227
    -            // console.info("Toggle resizable to: " + newValue);
    
    228
    -            updateGrid(config);
    
    229
    -        }
    
    230
    -
    
    231
    -        function toggleSort(config, source) {
    
    232
    -            let newValue = source.checked;
    
    233
    -            // console.info("Toggle sort to: " + newValue);
    
    234
    -            updateGrid(config);
    
    235
    -        }
    
    236
    -
    
    237
    -        function togglePagination(config, source) {
    
    238
    -            let newValue = source.checked;
    
    239
    -            // console.info("Toggle pagination to: " + newValue);
    
    240
    -            if (newValue) {
    
    241
    -                paginationSizeOption["disabled"] = null;
    
    242
    -            } else {
    
    243
    -                paginationSizeOption.disabled = true;
    
    244
    -            }
    
    245
    -            updateGrid(config);
    
    246
    -        }
    
    247
    -
    
    248
    -        function changePaginationSize(config, source) {
    
    249
    -            let newValue = source.value;
    
    250
    -            // console.info("Change pagination size to: " + newValue);
    
    251
    -            updateGrid(config);
    
    252
    -        }
    
    253
    -
    
    254
    -        function updateGrid(config) {
    
    255
    -            let searchValue1 = searchValue();
    
    256
    -            let sortValue1 = sortValue();
    
    257
    -            let resizableValue1 = resizableValue();
    
    258
    -            // FIXME Need to update config.columns otherwise we will keep previous options (sort, resizable...)
    
    259
    -            let newConfig = {
    
    260
    -                language: config.language,
    
    261
    -                data: config.data,
    
    262
    -                columns: config.columns,
    
    263
    -                search: searchValue1,
    
    264
    -                resizable: resizableValue1,
    
    265
    -                sort: sortValue1,
    
    266
    -                pagination: paginationValue()
    
    267
    -            };
    
    268
    -            gridContainerParent.innerHTML = '<div id="wrapper"></div>';
    
    269
    -            // console.info(newConfig);
    
    270
    -            setTimeout(() => {
    
    271
    -                <#--noinspection JSUnresolvedReference-->
    
    272
    -                grid = new gridjs.Grid(newConfig).render(document.getElementById("wrapper"));
    
    273
    -            }, 50);
    
    274
    -        }
    
    202
    +        ${.data_model.script}
    
    275 203
     
    
    276 204
             <#list .data_model.columnRendererFunctions as key>
    
    277
    -${key}</#list>
    
    278
    -
    
    279
    -        function createColumns(json) {
    
    280
    -            let result = !!json["columnNames"] ? json["columnNames"] : [];
    
    281
    -            if (result.length === 0) {
    
    282
    -                return result;
    
    283
    -            }
    
    284
    -            let renderers = json["columnRendererDefinitions"];
    
    285
    -            let i = 0;
    
    286
    -            let data = json.data;
    
    287
    -            <#list .data_model.columnRendererInitCode as  value>
    
    288
    -            ${value}</#list>
    
    289
    -            return result;
    
    290
    -        }
    
    205
    +        ${key}
    
    206
    +        </#list>
    
    291 207
         </script>
    
    292 208
     </head>
    
    293 209
     <body>
    
    ... ... @@ -330,11 +246,12 @@ ${key}</#list>
    330 246
         <div class="widget-list" id="tab-result">
    
    331 247
     
    
    332 248
             <div class="config-panel">
    
    333
    -            <label><input id="search" type="checkbox" checked/> Search</label>|
    
    334
    -            <label><input id="resizable" type="checkbox" checked/> Resizable columns</label>|
    
    335
    -            <label><input id="sort" type="checkbox" checked/> Sort</label>|
    
    249
    +            <label><input id="search" type="checkbox"/> Search</label>|
    
    250
    +            <label><input id="resizable" type="checkbox"/> Resizable columns</label>|
    
    251
    +            <label><input id="sort" type="checkbox"/> Sort</label>|
    
    336 252
                 <label><input id="pagination" type="checkbox"/> Pagination</label>|
    
    337
    -            <label>Page size&nbsp;<input id="paginationSize" type="number" disabled value="20"/></label>
    
    253
    +            <label>Page size&nbsp;<input id="paginationSize" type="number"/></label>|
    
    254
    +            <label>Result count:&nbsp;<b><span id="rowCount" style="font-style: italic"></span></b></label>
    
    338 255
             </div>
    
    339 256
             <div id="wrapperParent">
    
    340 257
                 <div id="wrapper"></div>
    
    ... ... @@ -347,61 +264,20 @@ ${key}</#list>
    347 264
     </div>
    
    348 265
     
    
    349 266
     <script type="application/javascript">
    
    350
    -    const json = ${.data_model.json};
    
    351
    -
    
    352
    -    const gridContainerParent = document.getElementById("wrapperParent");
    
    353
    -    const searchOption = document.getElementById("search");
    
    354
    -    const resizableOption = document.getElementById("resizable");
    
    355
    -    const paginationOption = document.getElementById("pagination");
    
    356
    -    const paginationSizeOption = document.getElementById("paginationSize");
    
    357
    -    const sortOption = document.getElementById("sort");
    
    358
    -
    
    359
    -    let grid = new gridjs.Grid({
    
    360
    -        search: searchValue(),
    
    361
    -        resizable: resizableValue(),
    
    362
    -        sort: sortValue(),
    
    363
    -        pagination: paginationValue(),
    
    364
    -        <#--language: {-->
    
    365
    -        <#--    'search': {-->
    
    366
    -        <#--        'placeholder': '🔍 Recherche...'-->
    
    367
    -        <#--    },-->
    
    368
    -        <#--    sort: {-->
    
    369
    -        <#--        sortAsc: 'Tri ascendant',-->
    
    370
    -        <#--        sortDesc: 'Tri descendant',-->
    
    371
    -        <#--    },-->
    
    372
    -        <#--    pagination: {-->
    
    373
    -        <#--        previous: 'Précédent',-->
    
    374
    -        <#--        next: 'Suivant',-->
    
    375
    -        <#--        navigate: (page, pages) => `Page ${r"${page}"} sur ${r"${pages}"}`,-->
    
    376
    -        <#--        page: (page) => `Page ${r"${page}"}`,-->
    
    377
    -        <#--        showing: 'Affichage des lignes de',-->
    
    378
    -        <#--        of: 'sur',-->
    
    379
    -        <#--        to: 'à',-->
    
    380
    -        <#--        results: 'lignes.',-->
    
    381
    -        <#--    },-->
    
    382
    -        <#--    loading: 'Chargement...'-->
    
    383
    -        <#--},-->
    
    384
    -        columns: createColumns(json),
    
    385
    -        data: json.data.data
    
    386
    -    });
    
    387
    -    updateGrid(grid.config);
    
    388
    -
    
    389
    -    searchOption.addEventListener("change", function () {
    
    390
    -        toggleSearch(grid.config, this);
    
    391
    -    });
    
    392
    -    resizableOption.addEventListener("change", function () {
    
    393
    -        toggleResizable(grid.config, this);
    
    394
    -    });
    
    395
    -    sortOption.addEventListener("change", function () {
    
    396
    -        toggleSort(grid.config, this);
    
    397
    -    });
    
    398
    -    paginationOption.addEventListener("change", function () {
    
    399
    -        togglePagination(grid.config, this);
    
    400
    -    });
    
    401
    -
    
    402
    -    paginationSizeOption.addEventListener("change", function () {
    
    403
    -        changePaginationSize(grid.config, this);
    
    404
    -    });
    
    267
    +    new GridHandler(
    
    268
    +        document,
    
    269
    +        {},
    
    270
    +        function (json) {
    
    271
    +            let result = !!json["columnNames"] ? json["columnNames"] : [];
    
    272
    +            if (result.length === 0) {
    
    273
    +                return result;
    
    274
    +            }
    
    275
    +            let data = json.data;
    
    276
    +            <#list .data_model.columnRendererInitCode as  value>
    
    277
    +            ${value}</#list>
    
    278
    +            return result;
    
    279
    +        },
    
    280
    +        ${.data_model.json}).init();
    
    405 281
     </script>
    
    406 282
     </body>
    
    407 283
     </html>

  • client/datasource/actions/src/main/i18n/templates/reportHtmlExport_es_ES.ftl
    ... ... @@ -199,95 +199,11 @@
    199 199
             }
    
    200 200
         </style>
    
    201 201
         <script type="application/javascript">
    
    202
    -
    
    203
    -        function searchValue() {
    
    204
    -            return searchOption.checked;
    
    205
    -        }
    
    206
    -
    
    207
    -        function resizableValue() {
    
    208
    -            return resizableOption.checked;
    
    209
    -        }
    
    210
    -
    
    211
    -        function sortValue() {
    
    212
    -            return sortOption.checked;
    
    213
    -        }
    
    214
    -
    
    215
    -        function paginationValue() {
    
    216
    -            return paginationOption.checked ? {limit: paginationSizeOption.value} : false;
    
    217
    -        }
    
    218
    -
    
    219
    -        function toggleSearch(config, source) {
    
    220
    -            let newValue = source.checked;
    
    221
    -            // console.info("Toggle search to: " + newValue);
    
    222
    -            updateGrid(config);
    
    223
    -        }
    
    224
    -
    
    225
    -        function toggleResizable(config, source) {
    
    226
    -            let newValue = source.checked;
    
    227
    -            // console.info("Toggle resizable to: " + newValue);
    
    228
    -            updateGrid(config);
    
    229
    -        }
    
    230
    -
    
    231
    -        function toggleSort(config, source) {
    
    232
    -            let newValue = source.checked;
    
    233
    -            // console.info("Toggle sort to: " + newValue);
    
    234
    -            updateGrid(config);
    
    235
    -        }
    
    236
    -
    
    237
    -        function togglePagination(config, source) {
    
    238
    -            let newValue = source.checked;
    
    239
    -            // console.info("Toggle pagination to: " + newValue);
    
    240
    -            if (newValue) {
    
    241
    -                paginationSizeOption["disabled"] = null;
    
    242
    -            } else {
    
    243
    -                paginationSizeOption.disabled = true;
    
    244
    -            }
    
    245
    -            updateGrid(config);
    
    246
    -        }
    
    247
    -
    
    248
    -        function changePaginationSize(config, source) {
    
    249
    -            let newValue = source.value;
    
    250
    -            // console.info("Change pagination size to: " + newValue);
    
    251
    -            updateGrid(config);
    
    252
    -        }
    
    253
    -
    
    254
    -        function updateGrid(config) {
    
    255
    -            let searchValue1 = searchValue();
    
    256
    -            let sortValue1 = sortValue();
    
    257
    -            let resizableValue1 = resizableValue();
    
    258
    -            // FIXME Need to update config.columns otherwise we will keep previous options (sort, resizable...)
    
    259
    -            let newConfig = {
    
    260
    -                language: config.language,
    
    261
    -                data: config.data,
    
    262
    -                columns: config.columns,
    
    263
    -                search: searchValue1,
    
    264
    -                resizable: resizableValue1,
    
    265
    -                sort: sortValue1,
    
    266
    -                pagination: paginationValue()
    
    267
    -            };
    
    268
    -            gridContainerParent.innerHTML = '<div id="wrapper"></div>';
    
    269
    -            // console.info(newConfig);
    
    270
    -            setTimeout(() => {
    
    271
    -                <#--noinspection JSUnresolvedReference-->
    
    272
    -                grid = new gridjs.Grid(newConfig).render(document.getElementById("wrapper"));
    
    273
    -            }, 50);
    
    274
    -        }
    
    202
    +        ${.data_model.script}
    
    275 203
     
    
    276 204
             <#list .data_model.columnRendererFunctions as key>
    
    277
    -${key}</#list>
    
    278
    -
    
    279
    -        function createColumns(json) {
    
    280
    -            let result = !!json["columnNames"] ? json["columnNames"] : [];
    
    281
    -            if (result.length === 0) {
    
    282
    -                return result;
    
    283
    -            }
    
    284
    -            let renderers = json["columnRendererDefinitions"];
    
    285
    -            let i = 0;
    
    286
    -            let data = json.data;
    
    287
    -            <#list .data_model.columnRendererInitCode as  value>
    
    288
    -            ${value}</#list>
    
    289
    -            return result;
    
    290
    -        }
    
    205
    +        ${key}
    
    206
    +        </#list>
    
    291 207
         </script>
    
    292 208
     </head>
    
    293 209
     <body>
    
    ... ... @@ -329,11 +245,12 @@ ${key}</#list>
    329 245
         </div>
    
    330 246
         <div class="widget-list" id="tab-result">
    
    331 247
             <div class="config-panel">
    
    332
    -            <label><input id="search" type="checkbox" checked/> Search</label>|
    
    333
    -            <label><input id="resizable" type="checkbox" checked/> Resizable columns</label>|
    
    334
    -            <label><input id="sort" type="checkbox" checked/> Sort</label>|
    
    248
    +            <label><input id="search" type="checkbox"/> Search</label>|
    
    249
    +            <label><input id="resizable" type="checkbox"/> Resizable columns</label>|
    
    250
    +            <label><input id="sort" type="checkbox"/> Sort</label>|
    
    335 251
                 <label><input id="pagination" type="checkbox"/> Pagination</label>|
    
    336
    -            <label>Page size&nbsp;<input id="paginationSize" type="number" disabled value="20"/></label>
    
    252
    +            <label>Page size&nbsp;<input id="paginationSize" type="number"/></label>|
    
    253
    +            <label>Result count:&nbsp;<b><span id="rowCount" style="font-style: italic"></span></b></label>
    
    337 254
             </div>
    
    338 255
             <div id="wrapperParent">
    
    339 256
                 <div id="wrapper"></div>
    
    ... ... @@ -346,61 +263,20 @@ ${key}</#list>
    346 263
     </div>
    
    347 264
     
    
    348 265
     <script type="application/javascript">
    
    349
    -    const json = ${.data_model.json};
    
    350
    -
    
    351
    -    const gridContainerParent = document.getElementById("wrapperParent");
    
    352
    -    const searchOption = document.getElementById("search");
    
    353
    -    const resizableOption = document.getElementById("resizable");
    
    354
    -    const paginationOption = document.getElementById("pagination");
    
    355
    -    const paginationSizeOption = document.getElementById("paginationSize");
    
    356
    -    const sortOption = document.getElementById("sort");
    
    357
    -
    
    358
    -    let grid = new gridjs.Grid({
    
    359
    -        search: searchValue(),
    
    360
    -        resizable: resizableValue(),
    
    361
    -        sort: sortValue(),
    
    362
    -        pagination: paginationValue(),
    
    363
    -        <#--language: {-->
    
    364
    -        <#--    'search': {-->
    
    365
    -        <#--        'placeholder': '🔍 Recherche...'-->
    
    366
    -        <#--    },-->
    
    367
    -        <#--    sort: {-->
    
    368
    -        <#--        sortAsc: 'Tri ascendant',-->
    
    369
    -        <#--        sortDesc: 'Tri descendant',-->
    
    370
    -        <#--    },-->
    
    371
    -        <#--    pagination: {-->
    
    372
    -        <#--        previous: 'Précédent',-->
    
    373
    -        <#--        next: 'Suivant',-->
    
    374
    -        <#--        navigate: (page, pages) => `Page ${r"${page}"} sur ${r"${pages}"}`,-->
    
    375
    -        <#--        page: (page) => `Page ${r"${page}"}`,-->
    
    376
    -        <#--        showing: 'Affichage des lignes de',-->
    
    377
    -        <#--        of: 'sur',-->
    
    378
    -        <#--        to: 'à',-->
    
    379
    -        <#--        results: 'lignes.',-->
    
    380
    -        <#--    },-->
    
    381
    -        <#--    loading: 'Chargement...'-->
    
    382
    -        <#--},-->
    
    383
    -        columns: createColumns(json),
    
    384
    -        data: json.data.data
    
    385
    -    });
    
    386
    -    updateGrid(grid.config);
    
    387
    -
    
    388
    -    searchOption.addEventListener("change", function () {
    
    389
    -        toggleSearch(grid.config, this);
    
    390
    -    });
    
    391
    -    resizableOption.addEventListener("change", function () {
    
    392
    -        toggleResizable(grid.config, this);
    
    393
    -    });
    
    394
    -    sortOption.addEventListener("change", function () {
    
    395
    -        toggleSort(grid.config, this);
    
    396
    -    });
    
    397
    -    paginationOption.addEventListener("change", function () {
    
    398
    -        togglePagination(grid.config, this);
    
    399
    -    });
    
    400
    -
    
    401
    -    paginationSizeOption.addEventListener("change", function () {
    
    402
    -        changePaginationSize(grid.config, this);
    
    403
    -    });
    
    266
    +    new GridHandler(
    
    267
    +        document,
    
    268
    +        {},
    
    269
    +        function (json) {
    
    270
    +            let result = !!json["columnNames"] ? json["columnNames"] : [];
    
    271
    +            if (result.length === 0) {
    
    272
    +                return result;
    
    273
    +            }
    
    274
    +            let data = json.data;
    
    275
    +            <#list .data_model.columnRendererInitCode as  value>
    
    276
    +            ${value}</#list>
    
    277
    +            return result;
    
    278
    +        },
    
    279
    +        ${.data_model.json}).init();
    
    404 280
     </script>
    
    405 281
     </body>
    
    406 282
     </html>

  • client/datasource/actions/src/main/i18n/templates/reportHtmlExport_fr_FR.ftl
    ... ... @@ -200,95 +200,11 @@
    200 200
             }
    
    201 201
         </style>
    
    202 202
         <script type="application/javascript">
    
    203
    -
    
    204
    -        function searchValue() {
    
    205
    -            return searchOption.checked;
    
    206
    -        }
    
    207
    -
    
    208
    -        function resizableValue() {
    
    209
    -            return resizableOption.checked;
    
    210
    -        }
    
    211
    -
    
    212
    -        function sortValue() {
    
    213
    -            return sortOption.checked;
    
    214
    -        }
    
    215
    -
    
    216
    -        function paginationValue() {
    
    217
    -            return paginationOption.checked ? {limit: paginationSizeOption.value} : false;
    
    218
    -        }
    
    219
    -
    
    220
    -        function toggleSearch(config, source) {
    
    221
    -            let newValue = source.checked;
    
    222
    -            // console.info("Toggle search to: " + newValue);
    
    223
    -            updateGrid(config);
    
    224
    -        }
    
    225
    -
    
    226
    -        function toggleResizable(config, source) {
    
    227
    -            let newValue = source.checked;
    
    228
    -            // console.info("Toggle resizable to: " + newValue);
    
    229
    -            updateGrid(config);
    
    230
    -        }
    
    231
    -
    
    232
    -        function toggleSort(config, source) {
    
    233
    -            let newValue = source.checked;
    
    234
    -            // console.info("Toggle sort to: " + newValue);
    
    235
    -            updateGrid(config);
    
    236
    -        }
    
    237
    -
    
    238
    -        function togglePagination(config, source) {
    
    239
    -            let newValue = source.checked;
    
    240
    -            // console.info("Toggle pagination to: " + newValue);
    
    241
    -            if (newValue) {
    
    242
    -                paginationSizeOption["disabled"] = null;
    
    243
    -            } else {
    
    244
    -                paginationSizeOption.disabled = true;
    
    245
    -            }
    
    246
    -            updateGrid(config);
    
    247
    -        }
    
    248
    -
    
    249
    -        function changePaginationSize(config, source) {
    
    250
    -            let newValue = source.value;
    
    251
    -            // console.info("Change pagination size to: " + newValue);
    
    252
    -            updateGrid(config);
    
    253
    -        }
    
    254
    -
    
    255
    -        function updateGrid(config) {
    
    256
    -            let searchValue1 = searchValue();
    
    257
    -            let sortValue1 = sortValue();
    
    258
    -            let resizableValue1 = resizableValue();
    
    259
    -            // FIXME Need to update config.columns otherwise we will keep previous options (sort, resizable...)
    
    260
    -            let newConfig = {
    
    261
    -                language: config.language,
    
    262
    -                data: config.data,
    
    263
    -                columns: config.columns,
    
    264
    -                search: searchValue1,
    
    265
    -                resizable: resizableValue1,
    
    266
    -                sort: sortValue1,
    
    267
    -                pagination: paginationValue()
    
    268
    -            };
    
    269
    -            gridContainerParent.innerHTML = '<div id="wrapper"></div>';
    
    270
    -            // console.info(newConfig);
    
    271
    -            setTimeout(() => {
    
    272
    -                <#--noinspection JSUnresolvedReference-->
    
    273
    -                grid = new gridjs.Grid(newConfig).render(document.getElementById("wrapper"));
    
    274
    -            }, 50);
    
    275
    -        }
    
    203
    +        ${.data_model.script}
    
    276 204
     
    
    277 205
             <#list .data_model.columnRendererFunctions as key>
    
    278
    -${key}</#list>
    
    279
    -
    
    280
    -        function createColumns(json) {
    
    281
    -            let result = !!json["columnNames"] ? json["columnNames"] : [];
    
    282
    -            if (result.length === 0) {
    
    283
    -                return result;
    
    284
    -            }
    
    285
    -            let renderers = json["columnRendererDefinitions"];
    
    286
    -            let i = 0;
    
    287
    -            let data = json.data;
    
    288
    -            <#list .data_model.columnRendererInitCode as  value>
    
    289
    -            ${value}</#list>
    
    290
    -            return result;
    
    291
    -        }
    
    206
    +        ${key}
    
    207
    +        </#list>
    
    292 208
         </script>
    
    293 209
     </head>
    
    294 210
     <body>
    
    ... ... @@ -330,11 +246,13 @@ ${key}</#list>
    330 246
         </div>
    
    331 247
         <div class="widget-list" id="tab-result">
    
    332 248
             <div class="config-panel">
    
    333
    -            <label><input id="search" type="checkbox" checked/> Recherche</label>|
    
    334
    -            <label><input id="resizable" type="checkbox" checked/> Colonnes redimensionnables</label>|
    
    335
    -            <label><input id="sort" type="checkbox" checked/> Tri des colonnes</label>|
    
    249
    +            <label><input id="search" type="checkbox"/> Recherche</label>|
    
    250
    +            <label><input id="resizable" type="checkbox"/> Colonnes redimensionnables</label>|
    
    251
    +            <label><input id="sort" type="checkbox"/> Tri des colonnes</label>|
    
    336 252
                 <label><input id="pagination" type="checkbox"/> Pagination</label>|
    
    337
    -            <label>Nombre de lignes par page&nbsp;<input id="paginationSize" type="number" disabled value="20"/></label>
    
    253
    +            <label>Nombre de lignes par page&nbsp;<input id="paginationSize" type="number"/></label>|
    
    254
    +            <label>Nombre de résultat(s)&nbsp;:&nbsp;<b><span id="rowCount" style="font-style: italic"></span></b></label>
    
    255
    +
    
    338 256
             </div>
    
    339 257
             <div id="wrapperParent">
    
    340 258
                 <div id="wrapper"></div>
    
    ... ... @@ -347,21 +265,9 @@ ${key}</#list>
    347 265
     </div>
    
    348 266
     
    
    349 267
     <script type="application/javascript">
    
    350
    -    const json = ${.data_model.json};
    
    351
    -
    
    352
    -    const gridContainerParent = document.getElementById("wrapperParent");
    
    353
    -    const searchOption = document.getElementById("search");
    
    354
    -    const resizableOption = document.getElementById("resizable");
    
    355
    -    const paginationOption = document.getElementById("pagination");
    
    356
    -    const paginationSizeOption = document.getElementById("paginationSize");
    
    357
    -    const sortOption = document.getElementById("sort");
    
    358
    -
    
    359
    -    let grid = new gridjs.Grid({
    
    360
    -        search: searchValue(),
    
    361
    -        resizable: resizableValue(),
    
    362
    -        sort: sortValue(),
    
    363
    -        pagination: paginationValue(),
    
    364
    -        language: {
    
    268
    +    new GridHandler(
    
    269
    +        document,
    
    270
    +        {
    
    365 271
                 'search': {
    
    366 272
                     'placeholder': '🔍 Recherche...'
    
    367 273
                 },
    
    ... ... @@ -381,27 +287,17 @@ ${key}</#list>
    381 287
                 },
    
    382 288
                 loading: 'Chargement...'
    
    383 289
             },
    
    384
    -        columns: createColumns(json),
    
    385
    -        data: json.data.data
    
    386
    -    });
    
    387
    -    updateGrid(grid.config);
    
    388
    -
    
    389
    -    searchOption.addEventListener("change", function () {
    
    390
    -        toggleSearch(grid.config, this);
    
    391
    -    });
    
    392
    -    resizableOption.addEventListener("change", function () {
    
    393
    -        toggleResizable(grid.config, this);
    
    394
    -    });
    
    395
    -    sortOption.addEventListener("change", function () {
    
    396
    -        toggleSort(grid.config, this);
    
    397
    -    });
    
    398
    -    paginationOption.addEventListener("change", function () {
    
    399
    -        togglePagination(grid.config, this);
    
    400
    -    });
    
    401
    -
    
    402
    -    paginationSizeOption.addEventListener("change", function () {
    
    403
    -        changePaginationSize(grid.config, this);
    
    404
    -    });
    
    290
    +        function (json) {
    
    291
    +            let result = !!json["columnNames"] ? json["columnNames"] : [];
    
    292
    +            if (result.length === 0) {
    
    293
    +                return result;
    
    294
    +            }
    
    295
    +            let data = json.data;
    
    296
    +            <#list .data_model.columnRendererInitCode as  value>
    
    297
    +            ${value}</#list>
    
    298
    +            return result;
    
    299
    +        },
    
    300
    +        ${.data_model.json}).init();
    
    405 301
     </script>
    
    406 302
     </body>
    
    407 303
     </html>

  • client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/HtmlExportModel.java
    ... ... @@ -24,14 +24,17 @@ package fr.ird.observe.client.datasource.actions.report;
    24 24
     
    
    25 25
     import com.google.gson.Gson;
    
    26 26
     import fr.ird.observe.client.datasource.actions.config.SelectDataModel;
    
    27
    +import fr.ird.observe.dto.ObserveUtil;
    
    27 28
     import fr.ird.observe.report.ColumnRendererConsumers;
    
    28 29
     import fr.ird.observe.report.Report;
    
    29 30
     import fr.ird.observe.report.ReportColumnRenderersParameters;
    
    30
    -import fr.ird.observe.report.definition.ColumnRendererDefinition;
    
    31 31
     import io.ultreia.java4all.application.template.spi.GenerateTemplate;
    
    32
    +import io.ultreia.java4all.util.SingletonSupplier;
    
    32 33
     import io.ultreia.java4all.util.matrix.DataMatrix;
    
    33 34
     
    
    35
    +import java.net.URL;
    
    34 36
     import java.util.List;
    
    37
    +import java.util.Objects;
    
    35 38
     import java.util.Set;
    
    36 39
     
    
    37 40
     /**
    
    ... ... @@ -44,6 +47,7 @@ import java.util.Set;
    44 47
      */
    
    45 48
     @GenerateTemplate(template = "reportHtmlExport.ftl")
    
    46 49
     public class HtmlExportModel {
    
    50
    +    private static SingletonSupplier<String> SCRIPT_CONTENT;
    
    47 51
         /**
    
    48 52
          * Selected report.
    
    49 53
          */
    
    ... ... @@ -52,16 +56,14 @@ public class HtmlExportModel {
    52 56
          * Selected data model.
    
    53 57
          */
    
    54 58
         private final transient SelectDataModel selectDataModel;
    
    55
    -
    
    59
    +    private final transient Set<String> columnRendererFunctions;
    
    60
    +    private final transient List<String> columnRendererInitCode;
    
    56 61
         private final List<String> columnNames;
    
    57 62
         private final List<String> rowNames;
    
    58 63
         private final DataMatrix data;
    
    59 64
         private final boolean withColumnHeader;
    
    60 65
         private final boolean withRowHeader;
    
    61 66
         private final transient String json;
    
    62
    -    private final List<Object> columnRendererDefinitions;
    
    63
    -    private final transient Set<String> columnRendererFunctions;
    
    64
    -    private final transient List<String> columnRendererInitCode;
    
    65 67
     
    
    66 68
         public HtmlExportModel(Gson gson,
    
    67 69
                                Report selectedReport,
    
    ... ... @@ -80,24 +82,26 @@ public class HtmlExportModel {
    80 82
             this.withColumnHeader = withColumnHeader;
    
    81 83
             this.withRowHeader = withRowHeader;
    
    82 84
             if (reportColumnRenderersParameters != null) {
    
    83
    -            this.columnRendererDefinitions = reportColumnRenderersParameters.consumeColumnRenderersHtml();
    
    84
    -            ColumnRendererDefinition[] columnRenderers = selectedReport.definition().getColumnRenderers();
    
    85
    -            this.columnRendererFunctions = ColumnRendererConsumers.htmlFunctions(columnRenderers);
    
    86
    -            this.columnRendererInitCode = ColumnRendererConsumers.htmlInitCode(columnRenderers);
    
    85
    +            this.columnRendererFunctions = ColumnRendererConsumers.htmlFunctions(reportColumnRenderersParameters);
    
    86
    +            this.columnRendererInitCode = ColumnRendererConsumers.htmlInitCode(reportColumnRenderersParameters);
    
    87 87
             } else {
    
    88
    -            this.columnRendererDefinitions = List.of();
    
    89 88
                 this.columnRendererFunctions = Set.of();
    
    90 89
                 this.columnRendererInitCode = List.of();
    
    91 90
             }
    
    92 91
             this.json = gson.toJson(this);
    
    93 92
         }
    
    94 93
     
    
    95
    -    public String getJson() {
    
    96
    -        return json;
    
    94
    +    public String getScript() {
    
    95
    +        if (SCRIPT_CONTENT == null) {
    
    96
    +            String resourceName = getClass().getSimpleName() + ".js";
    
    97
    +            URL url = Objects.requireNonNull(getClass().getResource(resourceName), "Could not find resource: " + resourceName);
    
    98
    +            SCRIPT_CONTENT = ObserveUtil.loadResourceContentSupplier(url, ObserveUtil.removeJavaLicense());
    
    99
    +        }
    
    100
    +        return SCRIPT_CONTENT.get();
    
    97 101
         }
    
    98 102
     
    
    99
    -    public List<Object> getColumnRendererDefinitions() {
    
    100
    -        return columnRendererDefinitions;
    
    103
    +    public String getJson() {
    
    104
    +        return json;
    
    101 105
         }
    
    102 106
     
    
    103 107
         public Set<String> getColumnRendererFunctions() {
    

  • client/datasource/actions/src/main/resources/fr/ird/observe/client/datasource/actions/report/HtmlExportModel.js
    1
    +/*-
    
    2
    + * #%L
    
    3
    + * ObServe Client :: DataSource :: Actions
    
    4
    + * %%
    
    5
    + * Copyright (C) 2008 - 2023 IRD, Ultreia.io
    
    6
    + * %%
    
    7
    + * This program is free software: you can redistribute it and/or modify
    
    8
    + * it under the terms of the GNU General Public License as
    
    9
    + * published by the Free Software Foundation, either version 3 of the
    
    10
    + * License, or (at your option) any later version.
    
    11
    + *
    
    12
    + * This program is distributed in the hope that it will be useful,
    
    13
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    14
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    
    15
    + * GNU General Public License for more details.
    
    16
    + *
    
    17
    + * You should have received a copy of the GNU General Public
    
    18
    + * License along with this program.  If not, see
    
    19
    + * <http://www.gnu.org/licenses/gpl-3.0.html>.
    
    20
    + * #L%
    
    21
    + */
    
    22
    +class GridHandler {
    
    23
    +    /**
    
    24
    +     * Where to store configuration of the grid.
    
    25
    +     */
    
    26
    +    config;
    
    27
    +    rowCount;
    
    28
    +    searchOption;
    
    29
    +    resizableOption;
    
    30
    +    paginationOption;
    
    31
    +    paginationSizeOption;
    
    32
    +    sortOption;
    
    33
    +    gridContainerParent;
    
    34
    +
    
    35
    +    constructor(document, language, columnsSupplier, json) {
    
    36
    +        this.gridContainerParent = document.getElementById("wrapperParent");
    
    37
    +        this.searchOption = document.getElementById("search");
    
    38
    +        this.resizableOption = document.getElementById("resizable");
    
    39
    +        this.paginationOption = document.getElementById("pagination");
    
    40
    +        this.paginationSizeOption = document.getElementById("paginationSize");
    
    41
    +        this.sortOption = document.getElementById("sort");
    
    42
    +        json.data.data = this.deserializeJson(json.data);
    
    43
    +
    
    44
    +        this.rowCount = json.data['height'];
    
    45
    +        this.searchOption['checked'] = true;
    
    46
    +        document.getElementById("rowCount").innerHTML = this.rowCount;
    
    47
    +        this.resizableOption['checked'] = true;
    
    48
    +        this.sortOption['checked'] = false;
    
    49
    +        this.paginationSizeOption.value = 20;
    
    50
    +        if (this.rowCount > 200) {
    
    51
    +            this.paginationOption['checked'] = true;
    
    52
    +        } else {
    
    53
    +            this.paginationSizeOption.disabled = true;
    
    54
    +        }
    
    55
    +        this.config = {
    
    56
    +            language: language,
    
    57
    +            columns: columnsSupplier(json),
    
    58
    +            data: json.data.data,
    
    59
    +            search: this.searchValue(),
    
    60
    +            resizable: this.resizableValue(),
    
    61
    +            sort: this.sortValue(),
    
    62
    +            pagination: this.paginationValue()
    
    63
    +        }
    
    64
    +    }
    
    65
    +
    
    66
    +    updateGrid() {
    
    67
    +        this.gridContainerParent.innerHTML = '<div id="wrapper"></div>';
    
    68
    +        setTimeout(() => {
    
    69
    +            // noinspection JSUnresolvedReference
    
    70
    +            new gridjs.Grid(this.toGridConfig()).render(document.getElementById("wrapper"));
    
    71
    +        }, 50);
    
    72
    +    }
    
    73
    +
    
    74
    +    toGridConfig() {
    
    75
    +        return {
    
    76
    +            language: this.config.language,
    
    77
    +            data: this.config.data,
    
    78
    +            columns: this.deepCopyColumns(this.config.columns),
    
    79
    +            search: this.config.search,
    
    80
    +            resizable: this.config.resizable,
    
    81
    +            sort: this.config.sort,
    
    82
    +            pagination: this.config.pagination,
    
    83
    +        }
    
    84
    +    }
    
    85
    +
    
    86
    +    deepCopyColumns(columns) {
    
    87
    +        let result = [];
    
    88
    +        let index = 0;
    
    89
    +        for (const column of columns) {
    
    90
    +            if (column instanceof Object) {
    
    91
    +                let newColumn = {};
    
    92
    +                newColumn['name'] = column['name'];
    
    93
    +                if (!!!column['formatter'] != null) {
    
    94
    +                    newColumn['formatter'] = column['formatter'];
    
    95
    +                }
    
    96
    +                if (column['attributes'] != null) {
    
    97
    +                    newColumn['attributes'] = column['attributes'];
    
    98
    +                }
    
    99
    +                result[index++] = newColumn;
    
    100
    +            } else {
    
    101
    +                result[index++] = column;
    
    102
    +            }
    
    103
    +        }
    
    104
    +        return result;
    
    105
    +    }
    
    106
    +
    
    107
    +    deserializeJson(json) {
    
    108
    +        let height = json.height;
    
    109
    +        let width = json.width;
    
    110
    +        let data = json.rows;
    
    111
    +        let result = new Array(height);
    
    112
    +        for (let row = 0; row < height; row++) {
    
    113
    +            let cells = data[row].split('||');
    
    114
    +            let realRow = new Array(width);
    
    115
    +            result[row] = realRow;
    
    116
    +            for (let column = 0; column < width; column++) {
    
    117
    +                let rowElement = cells[column];
    
    118
    +                realRow[column] = rowElement === '$' ? null : rowElement;
    
    119
    +            }
    
    120
    +        }
    
    121
    +        return result;
    
    122
    +    }
    
    123
    +
    
    124
    +    searchValue() {
    
    125
    +        return this.searchOption.checked;
    
    126
    +    }
    
    127
    +
    
    128
    +    resizableValue() {
    
    129
    +        return this.resizableOption.checked;
    
    130
    +    }
    
    131
    +
    
    132
    +    sortValue() {
    
    133
    +        return this.sortOption.checked;
    
    134
    +    }
    
    135
    +
    
    136
    +    paginationValue() {
    
    137
    +        return this.paginationOption.checked ? {limit: this.paginationSizeOption.value} : false;
    
    138
    +    }
    
    139
    +
    
    140
    +    toggleSearch(source) {
    
    141
    +        this.config.search = this.searchValue();
    
    142
    +        this.updateGrid();
    
    143
    +    }
    
    144
    +
    
    145
    +    toggleResizable(source) {
    
    146
    +        this.config.resizable = this.resizableValue();
    
    147
    +        this.updateGrid();
    
    148
    +    }
    
    149
    +
    
    150
    +    toggleSort(source) {
    
    151
    +        this.config.sort = this.sortValue();
    
    152
    +        this.updateGrid();
    
    153
    +    }
    
    154
    +
    
    155
    +    togglePagination(source) {
    
    156
    +        let newValue = source.checked;
    
    157
    +        if (newValue) {
    
    158
    +            this.paginationSizeOption["disabled"] = null;
    
    159
    +        } else {
    
    160
    +            this.paginationSizeOption.disabled = true;
    
    161
    +        }
    
    162
    +        this.config.pagination = this.paginationValue();
    
    163
    +        this.updateGrid();
    
    164
    +    }
    
    165
    +
    
    166
    +    changePaginationSize(source) {
    
    167
    +        this.config.pagination = this.paginationValue();
    
    168
    +        this.updateGrid();
    
    169
    +    }
    
    170
    +
    
    171
    +    init() {
    
    172
    +
    
    173
    +        this.updateGrid();
    
    174
    +        let that = this;
    
    175
    +        this.searchOption.addEventListener("change", function () {
    
    176
    +            that.toggleSearch(this);
    
    177
    +        });
    
    178
    +        this.resizableOption.addEventListener("change", function () {
    
    179
    +            that.toggleResizable(this);
    
    180
    +        });
    
    181
    +        this.sortOption.addEventListener("change", function () {
    
    182
    +            that.toggleSort(this);
    
    183
    +        });
    
    184
    +        this.paginationOption.addEventListener("change", function () {
    
    185
    +            that.togglePagination(this);
    
    186
    +        });
    
    187
    +
    
    188
    +        this.paginationSizeOption.addEventListener("change", function () {
    
    189
    +            that.changePaginationSize(this);
    
    190
    +        });
    
    191
    +    }
    
    192
    +}

  • core/persistence/report/src/main/resources/observe-reports.properties
    ... ... @@ -888,8 +888,8 @@ Left Join t.localMarketSurveySamplingAcquisitionStatus localMarketSurveySampling
    888 888
     Left Join t.advancedSamplingAcquisitionStatus advancedSamplingAcquisitionStatus \
    
    889 889
     Where t.id In :tripId \
    
    890 890
     Order By vessel.code,t.startDate,t.endDate
    
    891
    -report.psLogbookTrip.columnRenderers.1.type=HighlightIfNotI18nReferentialValue
    
    892
    -report.psLogbookTrip.columnRenderers.1.parameters=14,15,16,17,18,19,20,21|fr.ird.referential.ps.common.AcquisitionStatus#1464000000000#001
    
    891
    +report.psLogbookTrip.columnRenderers.1.type=HighlightIfEquals18nReferentialValue
    
    892
    +report.psLogbookTrip.columnRenderers.1.parameters=14,15,16,17,18,19,20,21|fr.ird.referential.ps.common.AcquisitionStatus#1464000000000#999
    
    893 893
     report.psLogbookTrip.columnRenderers.2.type=HighlightIfAbsoluteDeltaIsPositive
    
    894 894
     report.psLogbookTrip.columnRenderers.2.parameters=10|11|0.0001|0.5
    
    895 895
     report.psLogbookTrip.columnRenderers.3.type=HighlightIfAbsoluteDeltaIsPositive
    

  • core/services/test/src/main/java/fr/ird/observe/services/service/ReportServiceFixtures.java
    ... ... @@ -127,11 +127,9 @@ public class ReportServiceFixtures extends GeneratedReportServiceFixtures {
    127 127
             if (definition.needInitColumnRendererParameters()) {
    
    128 128
                 ReportColumnRenderersParameters renderersParameters = service.initColumnRendererParameters(definition.definition());
    
    129 129
                 Assert.assertNotNull(renderersParameters);
    
    130
    -            List<Object> consumeColumnRenderersHtml = renderersParameters.consumeColumnRenderersHtml();
    
    131
    -            Assert.assertNotNull(consumeColumnRenderersHtml);
    
    132
    -            Set<String> columnRendererFunctions = ColumnRendererConsumers.htmlFunctions(definition.definition().getColumnRenderers());
    
    130
    +            Set<String> columnRendererFunctions = ColumnRendererConsumers.htmlFunctions(renderersParameters);
    
    133 131
                 Assert.assertNotNull(columnRendererFunctions);
    
    134
    -            List<String> columnRendererInitCode = ColumnRendererConsumers.htmlInitCode(definition.definition().getColumnRenderers());
    
    132
    +            List<String> columnRendererInitCode = ColumnRendererConsumers.htmlInitCode(renderersParameters);
    
    135 133
                 Assert.assertNotNull(columnRendererInitCode);
    
    136 134
             }
    
    137 135
             Report report = reportFixture.populateVariables(service, definition);
    

  • src/site/markdown/report/embedded-column-renderers.md
    1
    +# Documentation des règles de rendu de colonnes disponibles
    
    2
    +
    
    3
    +Ce document décrit toutes les règles de rendu de colonnes disponibles sur les rapports.
    
    4
    +
    
    5
    +### HighlightIfAbsoluteDeltaIsPositive
    
    6
    +
    
    7
    +Ce rendu permet de vérifier que les valeurs de chaque cellule de deux colonnes sont identiques.
    
    8
    +
    
    9
    +Si deux valeurs ne sont pas égales, on colorise la cellule en orange (avertissement) pour un premier seuil et en rouge
    
    10
    +(erreur)  selon un second seuil.
    
    11
    +
    
    12
    +Le rendu nécessite quatre paramètres :
    
    13
    +
    
    14
    +* la première colonne
    
    15
    +* la seconde colonne
    
    16
    +* le seuil pour afficher des avertissements
    
    17
    +* le seuil pour afficher des erreurs
    
    18
    +
    
    19
    +Exemple d'utilisation :
    
    20
    +
    
    21
    +```properties
    
    22
    +report.xxx.columnRenderers.1.type=HighlightIfAbsoluteDeltaIsPositive
    
    23
    +report.ccc.columnRenderers.1.parameters=10|11|0.0001|0.5
    
    24
    +```
    
    25
    +
    
    26
    +### HighlightIfNumericalValueIsPositive
    
    27
    +
    
    28
    +Ce rendu permet de vérifier que les valeurs de chaque celleule d'une colonne n'est pas zéro.
    
    29
    +
    
    30
    +Si la valeur n'est pas zéro, on colorise la cellule en orange (avertissement) pour un premier seuil et en rouge
    
    31
    +(erreur)  selon un second seuil.
    
    32
    +
    
    33
    +Le rendu nécessite quatre paramètres :
    
    34
    +
    
    35
    +* la colonne
    
    36
    +* le seuil pour afficher des avertissements
    
    37
    +* le seuil pour afficher des erreurs
    
    38
    +
    
    39
    +Exemple d'utilisation
    
    40
    +
    
    41
    +```properties
    
    42
    +report.xxx.columnRenderers.1.type=HighlightIfNumericalValueIsPositive
    
    43
    +report.xxx.columnRenderers.1.parameters=19|0.0001|0.5
    
    44
    +```
    
    45
    +
    
    46
    +### HighlightIfEquals18nReferentialValue
    
    47
    +
    
    48
    +Ce rendu permet d'afficher de coloriser en rouge toute cellule des colonnes sélectionnées dont la valeur (de type
    
    49
    +référentiel i18n) n'est pas celle spécifié (via son identifiant).
    
    50
    +
    
    51
    +Le rendu nécessite deux paramètres :
    
    52
    +
    
    53
    +* une liste de colonnes sépararées par des virgules
    
    54
    +* l'identifiant du référentiel à mettre en valeur
    
    55
    +
    
    56
    +Exemple d'utilisation :
    
    57
    +
    
    58
    +```properties
    
    59
    +report.xxx.columnRenderers.1.type=HighlightIfEquals18nReferentialValue
    
    60
    +report.xxx.columnRenderers.1.parameters=14,15,16,17,18,19,20,21|fr.ird.referential.ps.common.AcquisitionStatus#1464000000000#999
    
    61
    +```

  • src/site/markdown/report/syntax.md
    ... ... @@ -4,13 +4,14 @@ Ce document décrit comment écrire un rapport.
    4 4
     
    
    5 5
     ## Syntaxe d'un rapport
    
    6 6
     
    
    7
    -Comme écrit dans la page de présentation des rapports, ceux-ci sont décrits dans un fichier de type *properties*. 
    
    7
    +Comme écrit dans la page de présentation des rapports, ceux-ci sont décrits dans un fichier de type *properties*.
    
    8 8
     
    
    9 9
     La syntaxe a été uniformisée en version 9.
    
    10 10
     
    
    11 11
     ### Identifiant d'un rapport
    
    12 12
     
    
    13
    -Chaque rapport possède un identifiant unique; celui-ci est utilisé pour identifier toute sa description dans le fichier global.
    
    13
    +Chaque rapport possède un identifiant unique; celui-ci est utilisé pour identifier toute sa description dans le fichier
    
    14
    +global.
    
    14 15
     
    
    15 16
     Voici un premier exemple de rapport pour mieux comprendre comment cela s'organise :
    
    16 17
     
    
    ... ... @@ -114,7 +115,8 @@ Order By sg.code
    114 115
     ```
    
    115 116
     
    
    116 117
     ```report.xxx.variable.1.name``` définit l'*alias* de la variable que l'on pourra ensuite utiliser dans le
    
    117
    -reste de la définition du rapport (par exemple dans d'autres variables, variables de répétition, requêtes ou opérations).
    
    118
    +reste de la définition du rapport (par exemple dans d'autres variables, variables de répétition, requêtes ou
    
    119
    +opérations).
    
    118 120
     
    
    119 121
     ```report.xxx.variable.1.type``` définit le type de la variable. Ce type doit être un type de *dto* ou une
    
    120 122
     *référence de dto*. On ne peut pas ici utiliser un type d'*entité*, puisque l'interface graphique (selon le principe de
    
    ... ... @@ -175,9 +177,12 @@ Where t.id In :tripId \
    175 177
     Order By sg.code
    
    176 178
     ```
    
    177 179
     
    
    178
    -Cet exemple est intéressant car il nous permet de voir comment utiliser un type qui ne reflète pas exactement une entité.
    
    180
    +Cet exemple est intéressant car il nous permet de voir comment utiliser un type qui ne reflète pas exactement une
    
    181
    +entité.
    
    182
    +
    
    183
    +Ici **SpeciesFateDiscardModeDto** est un objet que l'on a ajouté dans le code de l'application pour refléter les
    
    184
    +différents modes de captures :
    
    179 185
     
    
    180
    -Ici **SpeciesFateDiscardModeDto** est un objet que l'on a ajouté dans le code de l'application pour refléter les différents modes de captures :
    
    181 186
     * conservé (sf.discard = ```false```)
    
    182 187
     * rejeté (sf.discard = ```true```)
    
    183 188
     * non connu (sf.discard = ```NULL```)
    
    ... ... @@ -200,26 +205,33 @@ and g in elements (m.gearUseFeatures) \
    200 205
     Order By g.gear.label2
    
    201 206
     ```
    
    202 207
     
    
    203
    -```report.xxx.repeatVariable.1.name``` définit l'*alias* de la variable de répétition à utiliser dans une requête, autre variable de répétition ou opération
    
    208
    +```report.xxx.repeatVariable.1.name``` définit l'*alias* de la variable de répétition à utiliser dans une requête, autre
    
    209
    +variable de répétition ou opération
    
    204 210
     
    
    205 211
     ```report.xxx.repeatVariable.1.type``` définit le type de la variable de répétition. Contrairement aux variables, les
    
    206
    -variables de répétition sont utilisées en interne pour exécuter le rapport, on peut donc utiliser directement des types d'*entité*,
    
    207
    -ou bien des types *simples* qui correspondent aux types d'une colonne en base (**java.lang.String**, **java.lang.Float**, ...)
    
    212
    +variables de répétition sont utilisées en interne pour exécuter le rapport, on peut donc utiliser directement des types
    
    213
    +d'*entité*,
    
    214
    +ou bien des types *simples* qui correspondent aux types d'une colonne en base (**java.lang.String**, **java.lang.Float
    
    215
    +**, ...)
    
    208 216
     
    
    209
    -**À noter que contrairement aux variables, ici le type de la donnée sera toujours utilisé tel quel dans les autres requêtes :
    
    210
    -si on définit une variable de type *entité*, c'est bien l'entité qui sera injectée dans la requête *hql*, et non pas juste son identifiant technique.**
    
    217
    +**À noter que contrairement aux variables, ici le type de la donnée sera toujours utilisé tel quel dans les autres
    
    218
    +requêtes :
    
    219
    +si on définit une variable de type *entité*, c'est bien l'entité qui sera injectée dans la requête *hql*, et non pas
    
    220
    +juste son identifiant technique.**
    
    211 221
     
    
    212
    -```report.xxx.repeatVariable.1.request``` définit la requête **hql** pour récupérer l'univers des valeurs de la variable de répétition
    
    222
    +```report.xxx.repeatVariable.1.request``` définit la requête **hql** pour récupérer l'univers des valeurs de la variable
    
    223
    +de répétition
    
    213 224
     
    
    214 225
     Il est possible d'utiliser dans une requête de variable de répétition, un alias sur toute variable du rapport (dont la
    
    215 226
     variable spéciale **tripId**), ainsi que tout autre alias de variable de répétition.
    
    216 227
     
    
    217 228
     **À noter que l'outil de rapport utilise uniquement l'ordre induit dans la description du rapport, si par exemple la
    
    218
    -première variable de répétition était dépendante de la seconde, alors le rapport ne pourra jamais être exécuté puisque la
    
    229
    +première variable de répétition était dépendante de la seconde, alors le rapport ne pourra jamais être exécuté puisque
    
    230
    +la
    
    219 231
     première variable attend que l'univers de la seconde soit calculée...**
    
    220 232
     
    
    221
    -On peut aussi ajouter une ligne supplémentaire pour indiquer que l'on veut rajouter la valeur **nulle** dans 
    
    222
    -l'univers de cette variable de répétion, ce qui peut etre utilise (et nous nous en servons), mais que nous ne pouvons 
    
    233
    +On peut aussi ajouter une ligne supplémentaire pour indiquer que l'on veut rajouter la valeur **nulle** dans
    
    234
    +l'univers de cette variable de répétion, ce qui peut etre utilise (et nous nous en servons), mais que nous ne pouvons
    
    223 235
     pas décrire via la requete *hql.
    
    224 236
     
    
    225 237
     ```properties
    
    ... ... @@ -237,7 +249,8 @@ report.xxx.repeatVariable.1.comment=Un commentaire optionnel pour documenter la
    237 249
     On distingue deux types de requêtes :
    
    238 250
     
    
    239 251
     * des requêtes *simples*
    
    240
    -* des requêtes utilisant une variable de répétition (pour celles-ci on doit décrire en plus alors la variable de répétition à utiliser)
    
    252
    +* des requêtes utilisant une variable de répétition (pour celles-ci on doit décrire en plus alors la variable de
    
    253
    +  répétition à utiliser)
    
    241 254
     
    
    242 255
     Les deux types de requêtes nécessitent les trois lignes, comme décrit dans l'exemple suivant :
    
    243 256
     
    
    ... ... @@ -252,14 +265,20 @@ From fr.ird.observe.entities.data.ll.common.GearUseFeaturesImpl g \
    252 265
     Where g.id = :gearUseFeaturesId
    
    253 266
     ```
    
    254 267
     
    
    255
    -```report.xxx.request.1.location``` définit la position dans le résultat final où positionner le résultat de cette requête.
    
    268
    +```report.xxx.request.1.location``` définit la position dans le résultat final où positionner le résultat de cette
    
    269
    +requête.
    
    256 270
     
    
    257
    -```report.xxx.request.1.layout``` définit la disposition à utiliser pour placer le résultat de cette requête dans le résultat final. Deux valeurs sont possibles :
    
    271
    +```report.xxx.request.1.layout``` définit la disposition à utiliser pour placer le résultat de cette requête dans le
    
    272
    +résultat final. Deux valeurs sont possibles :
    
    258 273
     
    
    259
    -* **row** pour signifier que le résultat de la requête sera positionné en ligne à partir de la position précedemment définie. Ce mode correspond exactement au resultat de la requête.
    
    260
    -* **column** pour signifier que le résultat de la requête sera positionné en colonne à partir de la position précedemment définie. Ce mode est une transposition du résultat de la requête : une ligne du résultat de la requête sera une colonne dans le résultat final
    
    274
    +* **row** pour signifier que le résultat de la requête sera positionné en ligne à partir de la position précedemment
    
    275
    +  définie. Ce mode correspond exactement au resultat de la requête.
    
    276
    +* **column** pour signifier que le résultat de la requête sera positionné en colonne à partir de la position
    
    277
    +  précedemment définie. Ce mode est une transposition du résultat de la requête : une ligne du résultat de la requête
    
    278
    +  sera une colonne dans le résultat final
    
    261 279
     
    
    262
    -```report.xxx.request.1.request``` définit le code **hql** qui permet de construire le résultat à placer ensuite dans le résultat final du rapport
    
    280
    +```report.xxx.request.1.request``` définit le code **hql** qui permet de construire le résultat à placer ensuite dans le
    
    281
    +résultat final du rapport
    
    263 282
     
    
    264 283
     Pour une requête avec variable de répétition, il faut alors ajouter les deux lignes suivantes :
    
    265 284
     
    
    ... ... @@ -268,18 +287,24 @@ report.xxx.request.1.repeat.name=gearUseFeaturesId
    268 287
     report.xxx.request.1.repeat.layout=column
    
    269 288
     ```
    
    270 289
     
    
    271
    -```report.xxx.request.1.repeat.name``` définit l'*alias* de la variable de répétition à utiliser. La requête sera exécutée
    
    290
    +```report.xxx.request.1.repeat.name``` définit l'*alias* de la variable de répétition à utiliser. La requête sera
    
    291
    +exécutée
    
    272 292
     autant de fois qu'il y a de valeurs dans l'univers calculé de la variable de répétition.
    
    273 293
     
    
    274
    -**À noter qu'il faut alors que le corps de cette requête doit utiliser l'alias de cette variable de répétition, même si dans les faits, rien ne l'oblige, mais le résultat sera alors toujours le même...**
    
    294
    +**À noter qu'il faut alors que le corps de cette requête doit utiliser l'alias de cette variable de répétition, même si
    
    295
    +dans les faits, rien ne l'oblige, mais le résultat sera alors toujours le même...**
    
    275 296
     
    
    276
    -```report.xxx.request.1.repeat.layout``` définit la disposition à utiliser pour constuire le résultat final de la requête appliqué à chaque valeur de la variable de répétition. Deux valeurs sont possibles :
    
    297
    +```report.xxx.request.1.repeat.layout``` définit la disposition à utiliser pour constuire le résultat final de la
    
    298
    +requête appliqué à chaque valeur de la variable de répétition. Deux valeurs sont possibles :
    
    277 299
     
    
    278
    -* **row** pour signifier que pour chaque valeur de l'univers de la variable de répétition, le résultat de la requête sera positionné sur la même ligne;
    
    279
    -* **column** pour signifier que pour chaque valeur de l'univers de la variable de répétition, le résultat de la requête sera positionné sur la même colonne.
    
    300
    +* **row** pour signifier que pour chaque valeur de l'univers de la variable de répétition, le résultat de la requête
    
    301
    +  sera positionné sur la même ligne;
    
    302
    +* **column** pour signifier que pour chaque valeur de l'univers de la variable de répétition, le résultat de la requête
    
    303
    +  sera positionné sur la même colonne.
    
    280 304
     
    
    281 305
     **À noter qu'aucune vérification n'est effectuée sur la cohérence entre la disposition de la requête et la disposition
    
    282
    -de la variable de répétition. On peut alors obtenir un résultat final de la requête incohérent si les dispositions ne sont pas compatibles.**
    
    306
    +de la variable de répétition. On peut alors obtenir un résultat final de la requête incohérent si les dispositions ne
    
    307
    +sont pas compatibles.**
    
    283 308
     
    
    284 309
     Il est possible de documenter cette requête en utilisant une ligne optionnelle :
    
    285 310
     
    
    ... ... @@ -317,7 +342,26 @@ Il est possible de documenter cette opération en utilisant une ligne optionnell
    317 342
     report.xxx.operations.1.comment=Un commentaire optionnel pour documenter l'opération
    
    318 343
     ```
    
    319 344
     
    
    320
    -Les opérations disponibles et leur documentation sont décrites dans le document [suivant](./embedded-operations.html). 
    
    345
    +Les opérations disponibles et leur documentation sont décrites dans le document [suivant](./embedded-operations.html).
    
    346
    +
    
    347
    +### Rendu de colonnes
    
    348
    +
    
    349
    +Depuis la version **9.3.0**, il est possible de définir des rendus de colonnes via le fichier de définition,
    
    350
    +ce rendu sera valable dans le client swing ainsi que dans les rapports html.
    
    351
    +
    
    352
    +Un rendu est défini par deux lignes :
    
    353
    +
    
    354
    +1. Une pour définir le type de rendu
    
    355
    +2. Une pour paramétrer ce rendu
    
    356
    +
    
    357
    +```properties
    
    358
    +report.xxx.columnRenderers.1.type=HighlightIfAbsoluteDeltaIsPositive
    
    359
    +report.ccc.columnRenderers.1.parameters=10|11|0.0001|0.5
    
    360
    +```
    
    361
    +
    
    362
    +Il est possible d'ajouter plusieurs rendus sur un même rapport.
    
    363
    +
    
    364
    +Les rendus disponibles et leur documentation sont décrits dans le document [suivant](./embedded-column-renderers.html).
    
    321 365
     
    
    322 366
     ## Pour aller plus loin
    
    323 367
     
    

  • toolkit/api-report/pom.xml
    ... ... @@ -38,10 +38,6 @@
    38 38
           <groupId>com.google.code.gson</groupId>
    
    39 39
           <artifactId>gson</artifactId>
    
    40 40
         </dependency>
    
    41
    -    <dependency>
    
    42
    -      <groupId>com.google.guava</groupId>
    
    43
    -      <artifactId>guava</artifactId>
    
    44
    -    </dependency>
    
    45 41
         <dependency>
    
    46 42
           <groupId>io.ultreia.java4all</groupId>
    
    47 43
           <artifactId>java-lang</artifactId>
    

  • toolkit/api-report/src/main/java/fr/ird/observe/report/ColumnRendererConsumer.java
    ... ... @@ -22,8 +22,7 @@ package fr.ird.observe.report;
    22 22
      * #L%
    
    23 23
      */
    
    24 24
     
    
    25
    -import com.google.common.io.Resources;
    
    26
    -import fr.ird.observe.report.renderers.HighlightIfAbsoluteDeltaIsPositive;
    
    25
    +import fr.ird.observe.dto.ObserveUtil;
    
    27 26
     import io.ultreia.java4all.util.SingletonSupplier;
    
    28 27
     import org.jdesktop.swingx.JXTable;
    
    29 28
     import org.jdesktop.swingx.decorator.ColorHighlighter;
    
    ... ... @@ -31,6 +30,8 @@ import org.jdesktop.swingx.decorator.HighlightPredicate;
    31 30
     
    
    32 31
     import java.awt.Color;
    
    33 32
     import java.io.IOException;
    
    33
    +import java.io.InputStream;
    
    34
    +import java.net.URL;
    
    34 35
     import java.nio.charset.StandardCharsets;
    
    35 36
     import java.util.Objects;
    
    36 37
     
    
    ... ... @@ -53,13 +54,18 @@ public interface ColumnRendererConsumer<P extends ColumnRendererParameters> {
    53 54
         }
    
    54 55
     
    
    55 56
         static SingletonSupplier<String> htmlFunctions(Class<?> type) {
    
    56
    -        return SingletonSupplier.of(() -> {
    
    57
    -            try {
    
    58
    -                return Resources.toString(Objects.requireNonNull(HighlightIfAbsoluteDeltaIsPositive.class.getResource(type.getSimpleName() + ".js")), StandardCharsets.UTF_8);
    
    59
    -            } catch (IOException e) {
    
    60
    -                throw new RuntimeException(e);
    
    61
    -            }
    
    62
    -        });
    
    57
    +        String resourceName = type.getSimpleName() + ".js";
    
    58
    +        URL url = Objects.requireNonNull(type.getResource(resourceName), "Could not find resource: " + resourceName);
    
    59
    +        return ObserveUtil.loadResourceContentSupplier(url, ObserveUtil.removeJavaLicense());
    
    60
    +    }
    
    61
    +
    
    62
    +    static String loadResourceContent(URL url) {
    
    63
    +        try (InputStream in = url.openStream()) {
    
    64
    +            String content = new String(in.readAllBytes(), StandardCharsets.UTF_8);
    
    65
    +            return content.substring(content.indexOf("*/") + 2).trim();
    
    66
    +        } catch (IOException e) {
    
    67
    +            throw new RuntimeException(e);
    
    68
    +        }
    
    63 69
         }
    
    64 70
     
    
    65 71
         /**
    
    ... ... @@ -90,13 +96,9 @@ public interface ColumnRendererConsumer<P extends ColumnRendererParameters> {
    90 96
             return parseParameters(parameters);
    
    91 97
         }
    
    92 98
     
    
    93
    -    Object consumeHtml(P parameters);
    
    94
    -
    
    95 99
         void consumeSwing(P parameters, JXTable table);
    
    96 100
     
    
    97 101
         String htmlFunctions();
    
    98 102
     
    
    99
    -    default String htmlInitCode() {
    
    100
    -        return String.format("init%s(renderers[i++], result, data);\n", getClass().getSimpleName());
    
    101
    -    }
    
    103
    +    String htmlInitCode(P parameters);
    
    102 104
     }

  • toolkit/api-report/src/main/java/fr/ird/observe/report/ColumnRendererConsumers.java
    ... ... @@ -23,12 +23,10 @@ package fr.ird.observe.report;
    23 23
      */
    
    24 24
     
    
    25 25
     import fr.ird.observe.report.definition.ColumnRendererDefinition;
    
    26
    -import io.ultreia.java4all.util.matrix.DataMatrix;
    
    27 26
     import org.apache.logging.log4j.LogManager;
    
    28 27
     import org.apache.logging.log4j.Logger;
    
    29 28
     import org.jdesktop.swingx.JXTable;
    
    30 29
     
    
    31
    -import java.util.Arrays;
    
    32 30
     import java.util.List;
    
    33 31
     import java.util.Map;
    
    34 32
     import java.util.Objects;
    
    ... ... @@ -71,25 +69,21 @@ public final class ColumnRendererConsumers {
    71 69
             return get().getConsumer(definition.getName()).createParameters(requestExecutor, definition.getParameters());
    
    72 70
         }
    
    73 71
     
    
    74
    -    public static <P extends ColumnRendererParameters, R extends ColumnRendererConsumer<P>> Object consumeHtml(P parameters) {
    
    75
    -        return get().getConsumer(parameters.name()).consumeHtml(parameters);
    
    76
    -    }
    
    77
    -
    
    78
    -    public static <P extends ColumnRendererParameters, R extends ColumnRendererConsumer<P>> void consumeSwing(P parameters, JXTable table) {
    
    72
    +    public static <P extends ColumnRendererParameters> void consumeSwing(P parameters, JXTable table) {
    
    79 73
             get().getConsumer(parameters.name()).consumeSwing(parameters, table);
    
    80 74
         }
    
    81 75
     
    
    82
    -    public static Set<String> htmlFunctions(ColumnRendererDefinition[] columnRenderers) {
    
    83
    -        return Arrays.stream(columnRenderers).map(columnRenderer -> get().getConsumer(columnRenderer.getName()).htmlFunctions()).collect(Collectors.toCollection(TreeSet::new));
    
    76
    +    public static Set<String> htmlFunctions(ReportColumnRenderersParameters columnRenderers) {
    
    77
    +        return columnRenderers.getColumnRendererParameters().stream().map(columnRenderer -> get().getConsumer(columnRenderer.name()).htmlFunctions()).collect(Collectors.toCollection(TreeSet::new));
    
    84 78
         }
    
    85 79
     
    
    86
    -    public static List<String> htmlInitCode(ColumnRendererDefinition[] columnRenderers) {
    
    87
    -        return Arrays.stream(columnRenderers).map(r -> get().getConsumer(r.getName()).htmlInitCode()).collect(Collectors.toList());
    
    80
    +    public static List<String> htmlInitCode(ReportColumnRenderersParameters columnRenderers) {
    
    81
    +        return columnRenderers.getColumnRendererParameters().stream().map(r -> get().getConsumer(r.name()).htmlInitCode(r)).collect(Collectors.toList());
    
    88 82
         }
    
    89 83
     
    
    90 84
         @SuppressWarnings("unchecked")
    
    91
    -    public <P extends ColumnRendererParameters, R extends ColumnRendererConsumer<P>> R getConsumer(String name) {
    
    92
    -        return (R) Objects.requireNonNull(consumers.get(name), "Could not find column renderer with name: " + name);
    
    85
    +    public <P extends ColumnRendererParameters> ColumnRendererConsumer<P> getConsumer(String name) {
    
    86
    +        return (ColumnRendererConsumer<P>) Objects.requireNonNull(consumers.get(name), "Could not find column renderer with name: " + name);
    
    93 87
         }
    
    94 88
     
    
    95 89
     }

  • toolkit/api-report/src/main/java/fr/ird/observe/report/ReportColumnRenderersParameters.java
    ... ... @@ -28,7 +28,6 @@ import org.jdesktop.swingx.JXTable;
    28 28
     import java.util.Collections;
    
    29 29
     import java.util.List;
    
    30 30
     import java.util.Objects;
    
    31
    -import java.util.stream.Collectors;
    
    32 31
     
    
    33 32
     /**
    
    34 33
      * Created at 29/11/2023.
    
    ... ... @@ -53,8 +52,4 @@ public class ReportColumnRenderersParameters implements JsonAware {
    53 52
         public void consumeColumnRenderersSwing(JXTable table) {
    
    54 53
             getColumnRendererParameters().forEach(r -> ColumnRendererConsumers.consumeSwing(r, table));
    
    55 54
         }
    
    56
    -
    
    57
    -    public List<Object> consumeColumnRenderersHtml() {
    
    58
    -        return getColumnRendererParameters().stream().map(ColumnRendererConsumers::consumeHtml).collect(Collectors.toList());
    
    59
    -    }
    
    60 55
     }

  • toolkit/api-report/src/main/java/fr/ird/observe/report/json/ReportColumnRenderersParametersAdapter.java
    1
    +package fr.ird.observe.report.json;
    
    2
    +
    
    3
    +/*-
    
    4
    + * #%L
    
    5
    + * ObServe Toolkit :: API :: Report
    
    6
    + * %%
    
    7
    + * Copyright (C) 2008 - 2023 IRD, Ultreia.io
    
    8
    + * %%
    
    9
    + * This program is free software: you can redistribute it and/or modify
    
    10
    + * it under the terms of the GNU General Public License as
    
    11
    + * published by the Free Software Foundation, either version 3 of the
    
    12
    + * License, or (at your option) any later version.
    
    13
    + *
    
    14
    + * This program is distributed in the hope that it will be useful,
    
    15
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    16
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    
    17
    + * GNU General Public License for more details.
    
    18
    + *
    
    19
    + * You should have received a copy of the GNU General Public
    
    20
    + * License along with this program.  If not, see
    
    21
    + * <http://www.gnu.org/licenses/gpl-3.0.html>.
    
    22
    + * #L%
    
    23
    + */
    
    24
    +
    
    25
    +import com.google.auto.service.AutoService;
    
    26
    +import com.google.gson.JsonArray;
    
    27
    +import com.google.gson.JsonDeserializationContext;
    
    28
    +import com.google.gson.JsonDeserializer;
    
    29
    +import com.google.gson.JsonElement;
    
    30
    +import com.google.gson.JsonObject;
    
    31
    +import com.google.gson.JsonParseException;
    
    32
    +import com.google.gson.JsonSerializationContext;
    
    33
    +import com.google.gson.JsonSerializer;
    
    34
    +import fr.ird.observe.report.ColumnRendererParameters;
    
    35
    +import fr.ird.observe.report.ReportColumnRenderersParameters;
    
    36
    +import io.ultreia.java4all.lang.Objects2;
    
    37
    +import io.ultreia.java4all.util.json.JsonAdapter;
    
    38
    +
    
    39
    +import java.lang.reflect.Type;
    
    40
    +import java.util.ArrayList;
    
    41
    +import java.util.List;
    
    42
    +
    
    43
    +/**
    
    44
    + * Created at 01/12/2023.
    
    45
    + *
    
    46
    + * @author Tony Chemit - dev@tchemit.fr
    
    47
    + * @since 9.3.0
    
    48
    + */
    
    49
    +@AutoService(JsonAdapter.class)
    
    50
    +public class ReportColumnRenderersParametersAdapter implements JsonDeserializer<ReportColumnRenderersParameters>, JsonSerializer<ReportColumnRenderersParameters>, JsonAdapter {
    
    51
    +
    
    52
    +    private static final String TYPE = "type";
    
    53
    +    private static final String VALUE = "value";
    
    54
    +
    
    55
    +    @Override
    
    56
    +    public Class<?> type() {
    
    57
    +        return ReportColumnRenderersParameters.class;
    
    58
    +    }
    
    59
    +
    
    60
    +    @Override
    
    61
    +    public ReportColumnRenderersParameters deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
    
    62
    +        JsonArray columnRendererParametersJson = json.getAsJsonArray();
    
    63
    +        List<ColumnRendererParameters> list = new ArrayList<>(columnRendererParametersJson.size());
    
    64
    +        for (JsonElement itemJson : columnRendererParametersJson) {
    
    65
    +            JsonObject asJsonObject = itemJson.getAsJsonObject();
    
    66
    +            String key = context.deserialize(asJsonObject.get(TYPE), String.class);
    
    67
    +            Class<ColumnRendererParameters> itemType = Objects2.forName(key);
    
    68
    +            list.add(context.deserialize(asJsonObject.get(VALUE), itemType));
    
    69
    +        }
    
    70
    +        return new ReportColumnRenderersParameters(list);
    
    71
    +    }
    
    72
    +
    
    73
    +    @Override
    
    74
    +    public JsonElement serialize(ReportColumnRenderersParameters src, Type typeOfSrc, JsonSerializationContext context) {
    
    75
    +        JsonArray result = new JsonArray();
    
    76
    +        for (ColumnRendererParameters columnRendererParameter : src.getColumnRendererParameters()) {
    
    77
    +            JsonObject item = new JsonObject();
    
    78
    +            item.addProperty(TYPE, columnRendererParameter.getClass().getName());
    
    79
    +            item.add(VALUE, context.serialize(columnRendererParameter));
    
    80
    +            result.add(item);
    
    81
    +        }
    
    82
    +        return result;
    
    83
    +    }
    
    84
    +}

  • toolkit/api-report/src/main/java/fr/ird/observe/report/renderers/HighlightIfAbsoluteDeltaIsPositive.java
    ... ... @@ -31,7 +31,6 @@ import org.jdesktop.swingx.decorator.ComponentAdapter;
    31 31
     import org.jdesktop.swingx.decorator.HighlightPredicate;
    
    32 32
     
    
    33 33
     import java.awt.Component;
    
    34
    -import java.util.Map;
    
    35 34
     
    
    36 35
     import static fr.ird.observe.report.renderers.HighlightIfAbsoluteDeltaIsPositive.Parameters;
    
    37 36
     
    
    ... ... @@ -66,15 +65,6 @@ public class HighlightIfAbsoluteDeltaIsPositive implements ColumnRendererConsume
    66 65
             return new Parameters(column1, column2, warningThreshHold, errorThreshHold);
    
    67 66
         }
    
    68 67
     
    
    69
    -    @Override
    
    70
    -    public Object consumeHtml(Parameters parameters) {
    
    71
    -        return Map.of("name", name(),
    
    72
    -                      "column1", parameters.getColumn1(),
    
    73
    -                      "column2", parameters.getColumn2(),
    
    74
    -                      "warningThreshHold", parameters.getWarningThreshHold(),
    
    75
    -                      "errorThreshHold", parameters.getErrorThreshHold());
    
    76
    -    }
    
    77
    -
    
    78 68
         @Override
    
    79 69
         public void consumeSwing(Parameters parameters, JXTable table) {
    
    80 70
             ColumnRendererConsumer.addHighLighters(table,
    
    ... ... @@ -88,6 +78,11 @@ public class HighlightIfAbsoluteDeltaIsPositive implements ColumnRendererConsume
    88 78
             return HTML_FUNCTION.get();
    
    89 79
         }
    
    90 80
     
    
    81
    +    @Override
    
    82
    +    public String htmlInitCode(Parameters parameters) {
    
    83
    +        return String.format("init%s(%d, %d, %s, %s, result, data);\n", parameters.name(), parameters.getColumn1(), parameters.getColumn2(), parameters.getWarningThreshHold(), parameters.getErrorThreshHold());
    
    84
    +    }
    
    85
    +
    
    91 86
         public static final class Parameters implements ColumnRendererParameters {
    
    92 87
             private final int column1;
    
    93 88
             private final int column2;
    

  • toolkit/api-report/src/main/java/fr/ird/observe/report/renderers/HighlightIfNotI18nReferentialValue.javatoolkit/api-report/src/main/java/fr/ird/observe/report/renderers/HighlightIfEquals18nReferentialValue.java
    ... ... @@ -31,23 +31,21 @@ import org.jdesktop.swingx.JXTable;
    31 31
     import org.jdesktop.swingx.decorator.ColorHighlighter;
    
    32 32
     
    
    33 33
     import java.awt.Color;
    
    34
    -import java.util.Map;
    
    35 34
     import java.util.Objects;
    
    36 35
     import java.util.Set;
    
    37 36
     import java.util.TreeSet;
    
    38 37
     
    
    39
    -import static fr.ird.observe.report.renderers.HighlightIfNotI18nReferentialValue.Parameters;
    
    38
    +import static fr.ird.observe.report.renderers.HighlightIfEquals18nReferentialValue.Parameters;
    
    40 39
     
    
    41 40
     /**
    
    42
    - * Created at 29/11/2023.
    
    41
    + * Created at 02/12/2023.
    
    43 42
      *
    
    44 43
      * @author Tony Chemit - dev@tchemit.fr
    
    45 44
      * @since 9.3.0
    
    46 45
      */
    
    47
    -@SuppressWarnings("rawtypes")
    
    48 46
     @AutoService(ColumnRendererConsumer.class)
    
    49
    -public class HighlightIfNotI18nReferentialValue implements ColumnRendererConsumer<Parameters> {
    
    50
    -    private final static SingletonSupplier<String> HTML_FUNCTION = ColumnRendererConsumer.htmlFunctions(HighlightIfNotI18nReferentialValue.class);
    
    47
    +public class HighlightIfEquals18nReferentialValue implements ColumnRendererConsumer<Parameters> {
    
    48
    +    private final static SingletonSupplier<String> HTML_FUNCTION = ColumnRendererConsumer.htmlFunctions(HighlightIfEquals18nReferentialValue.class);
    
    51 49
     
    
    52 50
         @Override
    
    53 51
         public int parametersCount() {
    
    ... ... @@ -78,19 +76,12 @@ public class HighlightIfNotI18nReferentialValue implements ColumnRendererConsume
    78 76
             return result.setLabel(label);
    
    79 77
         }
    
    80 78
     
    
    81
    -    @Override
    
    82
    -    public Object consumeHtml(Parameters parameters) {
    
    83
    -        return Map.of("name", name(),
    
    84
    -                      "columns", parameters.getColumns(),
    
    85
    -                      "label", parameters.getLabel());
    
    86
    -    }
    
    87
    -
    
    88 79
         @Override
    
    89 80
         public void consumeSwing(Parameters parameters, JXTable table) {
    
    90 81
             table.addHighlighter(new ColorHighlighter((renderer, adapter) -> {
    
    91 82
                 Object value = adapter.getValue();
    
    92 83
                 int column = adapter.convertColumnIndexToModel(adapter.column);
    
    93
    -            return  parameters.getColumns().contains(column) && !Objects.equals(value, parameters.getLabel());
    
    84
    +            return parameters.getColumns().contains(column) && Objects.equals(value, parameters.getLabel());
    
    94 85
             }, Color.RED, Color.BLACK));
    
    95 86
         }
    
    96 87
     
    
    ... ... @@ -99,6 +90,11 @@ public class HighlightIfNotI18nReferentialValue implements ColumnRendererConsume
    99 90
             return HTML_FUNCTION.get();
    
    100 91
         }
    
    101 92
     
    
    93
    +    @Override
    
    94
    +    public String htmlInitCode(Parameters parameters) {
    
    95
    +        return String.format("init%s(%s, '%s', result, data);\n", parameters.name(), parameters.getColumns(), parameters.getLabel());
    
    96
    +    }
    
    97
    +
    
    102 98
         public static final class Parameters implements ColumnRendererParameters {
    
    103 99
             private final Set<Integer> columns;
    
    104 100
             private final String id;
    
    ... ... @@ -118,7 +114,7 @@ public class HighlightIfNotI18nReferentialValue implements ColumnRendererConsume
    118 114
     
    
    119 115
             @Override
    
    120 116
             public String name() {
    
    121
    -            return HighlightIfNotI18nReferentialValue.class.getSimpleName();
    
    117
    +            return HighlightIfEquals18nReferentialValue.class.getSimpleName();
    
    122 118
             }
    
    123 119
     
    
    124 120
             public Set<Integer> getColumns() {
    

  • toolkit/api-report/src/main/java/fr/ird/observe/report/renderers/HighlightIfNumericalValueIsPositive.java
    ... ... @@ -31,7 +31,6 @@ import org.jdesktop.swingx.decorator.ComponentAdapter;
    31 31
     import org.jdesktop.swingx.decorator.HighlightPredicate;
    
    32 32
     
    
    33 33
     import java.awt.Component;
    
    34
    -import java.util.Map;
    
    35 34
     
    
    36 35
     import static fr.ird.observe.report.renderers.HighlightIfNumericalValueIsPositive.Parameters;
    
    37 36
     
    
    ... ... @@ -65,14 +64,6 @@ public class HighlightIfNumericalValueIsPositive implements ColumnRendererConsum
    65 64
         }
    
    66 65
     
    
    67 66
     
    
    68
    -    @Override
    
    69
    -    public Object consumeHtml(Parameters parameters) {
    
    70
    -        return Map.of("name", name(),
    
    71
    -                      "column", parameters.getColumn(),
    
    72
    -                      "warningThreshHold", parameters.getWarningThreshHold(),
    
    73
    -                      "errorThreshHold", parameters.getErrorThreshHold());
    
    74
    -    }
    
    75
    -
    
    76 67
         @Override
    
    77 68
         public void consumeSwing(Parameters parameters, JXTable table) {
    
    78 69
             ColumnRendererConsumer.addHighLighters(table,
    
    ... ... @@ -86,6 +77,11 @@ public class HighlightIfNumericalValueIsPositive implements ColumnRendererConsum
    86 77
             return HTML_FUNCTION.get();
    
    87 78
         }
    
    88 79
     
    
    80
    +    @Override
    
    81
    +    public String htmlInitCode(Parameters parameters) {
    
    82
    +        return String.format("init%s(%d, %f, %f, result, data);\n", parameters.name(), parameters.getColumn(), parameters.getWarningThreshHold(), parameters.getErrorThreshHold());
    
    83
    +    }
    
    84
    +
    
    89 85
         public static final class Parameters implements ColumnRendererParameters {
    
    90 86
             private final int column;
    
    91 87
             private final double warningThreshHold;
    

  • toolkit/api-report/src/main/resources/fr/ird/observe/report/renderers/HighlightIfAbsoluteDeltaIsPositive.js
    ... ... @@ -46,9 +46,7 @@ function HighlightIfAbsoluteDeltaIsPositive(cell, row, column2, warningThreshHol
    46 46
         };
    
    47 47
     }
    
    48 48
     
    
    49
    -function initHighlightIfAbsoluteDeltaIsPositive(renderer, result, json) {
    
    50
    -    let column1 = renderer["column1"];
    
    51
    -    let column2 = renderer["column2"];
    
    49
    +function initHighlightIfAbsoluteDeltaIsPositive(column1, column2, warningThreshHold, errorThreshHold, result, json) {
    
    52 50
         let height = json.height;
    
    53 51
         let data = json.data;
    
    54 52
         for (let row = 0; row < height; row++) {
    
    ... ... @@ -58,8 +56,6 @@ function initHighlightIfAbsoluteDeltaIsPositive(renderer, result, json) {
    58 56
                 }
    
    59 57
             }
    
    60 58
         }
    
    61
    -    let warningThreshHold = renderer["warningThreshHold"];
    
    62
    -    let errorThreshHold = renderer["errorThreshHold"];
    
    63 59
         result[column1] = {
    
    64 60
             name: result [column1],
    
    65 61
             formatter: cell => {
    

  • toolkit/api-report/src/main/resources/fr/ird/observe/report/renderers/HighlightIfNotI18nReferentialValue.jstoolkit/api-report/src/main/resources/fr/ird/observe/report/renderers/HighlightIfEquals18nReferentialValue.js
    ... ... @@ -19,27 +19,24 @@
    19 19
      * <http://www.gnu.org/licenses/gpl-3.0.html>.
    
    20 20
      * #L%
    
    21 21
      */
    
    22
    -function HighlightIfNotI18nReferentialValue(cell, label) {
    
    22
    +function HighlightIfEquals18nReferentialValue(cell, label) {
    
    23 23
         if (!!!cell) {
    
    24 24
             return;
    
    25 25
         }
    
    26 26
         if (cell === label) {
    
    27
    -        return;
    
    27
    +        return {
    
    28
    +            'data-cell-content': cell,
    
    29
    +            'class': 'gridjs-td cellError',
    
    30
    +        };
    
    28 31
         }
    
    29
    -    return {
    
    30
    -        'data-cell-content': cell,
    
    31
    -        'class': 'gridjs-td cellError',
    
    32
    -    };
    
    33 32
     }
    
    34 33
     
    
    35
    -function initHighlightIfNotI18nReferentialValue(renderer, result, json) {
    
    36
    -    let columns = renderer["columns"];
    
    37
    -    let label = renderer["label"];
    
    34
    +function initHighlightIfEquals18nReferentialValue(columns, label, result, json) {
    
    38 35
         for (let j = 0; j < columns.length; j++) {
    
    39 36
             let column = columns[j];
    
    40 37
             result [column] = {
    
    41 38
                 name: result [column],
    
    42
    -            attributes: cell => HighlightIfNotI18nReferentialValue(cell, label)
    
    39
    +            attributes: cell => HighlightIfEquals18nReferentialValue(cell, label)
    
    43 40
             };
    
    44 41
         }
    
    45 42
     }

  • toolkit/api-report/src/main/resources/fr/ird/observe/report/renderers/HighlightIfNumericalValueIsPositive.js
    ... ... @@ -39,8 +39,7 @@ function HighlightIfNumericalValueIsPositive(cell, warningThreshHold, errorThres
    39 39
         };
    
    40 40
     }
    
    41 41
     
    
    42
    -function initHighlightIfNumericalValueIsPositive(renderer, result, json) {
    
    43
    -    let column = renderer["column"];
    
    42
    +function initHighlightIfNumericalValueIsPositive(column, warningThreshHold, errorThreshHold, result, json) {
    
    44 43
         let height = json.height;
    
    45 44
         let data = json.data;
    
    46 45
         for (let row = 0; row < height; row++) {
    
    ... ... @@ -48,8 +47,6 @@ function initHighlightIfNumericalValueIsPositive(renderer, result, json) {
    48 47
                 data[row][column] = "-";
    
    49 48
             }
    
    50 49
         }
    
    51
    -    let warningThreshHold = renderer["warningThreshHold"];
    
    52
    -    let errorThreshHold = renderer["errorThreshHold"];
    
    53 50
         result[column] = {
    
    54 51
             name: result [column],
    
    55 52
             formatter: cell => {
    

  • toolkit/api/src/main/java/fr/ird/observe/dto/ObserveUtil.java
    ... ... @@ -31,15 +31,21 @@ import fr.ird.observe.spi.module.BusinessSubModule;
    31 31
     import io.ultreia.java4all.config.ApplicationConfig;
    
    32 32
     import io.ultreia.java4all.config.ConfigResource;
    
    33 33
     import io.ultreia.java4all.lang.Strings;
    
    34
    +import io.ultreia.java4all.util.SingletonSupplier;
    
    35
    +import org.apache.logging.log4j.LogManager;
    
    36
    +import org.apache.logging.log4j.Logger;
    
    34 37
     import org.apache.logging.log4j.core.config.Configurator;
    
    35 38
     import org.hashids.Hashids;
    
    36 39
     
    
    37 40
     import javax.script.ScriptEngine;
    
    38 41
     import javax.script.ScriptEngineManager;
    
    42
    +import java.io.IOException;
    
    43
    +import java.io.InputStream;
    
    39 44
     import java.lang.reflect.ParameterizedType;
    
    40 45
     import java.lang.reflect.Type;
    
    41 46
     import java.net.MalformedURLException;
    
    42 47
     import java.net.URL;
    
    48
    +import java.nio.charset.StandardCharsets;
    
    43 49
     import java.nio.file.Files;
    
    44 50
     import java.nio.file.Path;
    
    45 51
     import java.text.Collator;
    
    ... ... @@ -66,7 +72,6 @@ import java.util.function.Supplier;
    66 72
     public class ObserveUtil {
    
    67 73
     
    
    68 74
         public static final String SQL_GZ_EXTENSION_PATTERN = "^.+\\.sql\\.gz|.+\\.SQL\\.GZ$";
    
    69
    -    public static final String CSV_EXTENSION_PATTERN = "^.+\\.csv|.+\\.CSV$";
    
    70 75
         public static final String SQL_GZ_EXTENSION = ".sql.gz";
    
    71 76
         public static final String CSV_EXTENSION = ".csv";
    
    72 77
         public static final String PROPERTIES_EXTENSION_PATTERN = "^.+\\.properties|.+\\.PROPERTIES$";
    
    ... ... @@ -75,38 +80,7 @@ public class ObserveUtil {
    75 80
         public static final String PNG_EXTENSION = ".png";
    
    76 81
         public static final String JS_ENGINE_NAME = "rhino";
    
    77 82
         private static final Hashids ID_GENERATOR = new Hashids("ObServeHasSomeSalt", 8, "0123456789#abcdefghijklmnopqrestuvwxyz");
    
    78
    -
    
    79
    -    @SuppressWarnings("rawtypes")
    
    80
    -    private static class ClassComparator<C extends Class<?>> implements Comparator<C> {
    
    81
    -
    
    82
    -        private final Map<Class, String> cache;
    
    83
    -        private final Function<Class, String> function;
    
    84
    -
    
    85
    -        private final Collator collator;
    
    86
    -
    
    87
    -        private ClassComparator(Function<Class, String> function, Locale locale) {
    
    88
    -            this.cache = new HashMap<>();
    
    89
    -            this.function = function;
    
    90
    -            this.collator = Collator.getInstance(locale);
    
    91
    -            this.collator.setStrength(Collator.PRIMARY);
    
    92
    -        }
    
    93
    -
    
    94
    -        @Override
    
    95
    -        public int compare(Class o1, Class o2) {
    
    96
    -            String s1 = getValue(o1);
    
    97
    -            String s2 = getValue(o2);
    
    98
    -            return this.collator.compare(s1, s2);
    
    99
    -        }
    
    100
    -
    
    101
    -        String getValue(Class klass) {
    
    102
    -            return cache.computeIfAbsent(klass, k -> function.apply(klass));
    
    103
    -        }
    
    104
    -
    
    105
    -        public void sort(List<C> list) {
    
    106
    -            list.sort(this);
    
    107
    -            cache.clear();
    
    108
    -        }
    
    109
    -    }
    
    83
    +    private static final Logger log = LogManager.getLogger(ObserveUtil.class);
    
    110 84
     
    
    111 85
         public static String newUUID(Date now) {
    
    112 86
             return ID_GENERATOR.encode(now.getTime());
    
    ... ... @@ -253,15 +227,65 @@ public class ObserveUtil {
    253 227
         }
    
    254 228
     
    
    255 229
         /**
    
    256
    -     *
    
    257 230
          * @param jsonString the json string in compact mode
    
    258 231
          * @return the gson in a pretty mode
    
    259 232
          */
    
    260
    -    public static String toPrettyFormat(String jsonString)
    
    261
    -    {
    
    233
    +    public static String toPrettyFormat(String jsonString) {
    
    262 234
             JsonObject json = JsonParser.parseString(jsonString).getAsJsonObject();
    
    263 235
     
    
    264 236
             Gson gson = new GsonBuilder().setPrettyPrinting().create();
    
    265 237
             return gson.toJson(json);
    
    266 238
         }
    
    239
    +
    
    240
    +    public static SingletonSupplier<String> loadResourceContentSupplier(URL url, Function<String, String> contentTransformer) {
    
    241
    +        return SingletonSupplier.of(() -> {
    
    242
    +            String content = loadResourceContent(url);
    
    243
    +            return contentTransformer == null ? content : contentTransformer.apply(content);
    
    244
    +        });
    
    245
    +    }
    
    246
    +
    
    247
    +    public static String loadResourceContent(URL url) {
    
    248
    +        log.info("Loading resource content: {}", url);
    
    249
    +        try (InputStream in = url.openStream()) {
    
    250
    +            return new String(in.readAllBytes(), StandardCharsets.UTF_8);
    
    251
    +        } catch (IOException e) {
    
    252
    +            throw new RuntimeException(e);
    
    253
    +        }
    
    254
    +    }
    
    255
    +
    
    256
    +    public static Function<String, String> removeJavaLicense() {
    
    257
    +        return content -> content.substring(content.indexOf("*/") + 2).trim();
    
    258
    +    }
    
    259
    +
    
    260
    +    @SuppressWarnings("rawtypes")
    
    261
    +    private static class ClassComparator<C extends Class<?>> implements Comparator<C> {
    
    262
    +
    
    263
    +        private final Map<Class, String> cache;
    
    264
    +        private final Function<Class, String> function;
    
    265
    +
    
    266
    +        private final Collator collator;
    
    267
    +
    
    268
    +        private ClassComparator(Function<Class, String> function, Locale locale) {
    
    269
    +            this.cache = new HashMap<>();
    
    270
    +            this.function = function;
    
    271
    +            this.collator = Collator.getInstance(locale);
    
    272
    +            this.collator.setStrength(Collator.PRIMARY);
    
    273
    +        }
    
    274
    +
    
    275
    +        @Override
    
    276
    +        public int compare(Class o1, Class o2) {
    
    277
    +            String s1 = getValue(o1);
    
    278
    +            String s2 = getValue(o2);
    
    279
    +            return this.collator.compare(s1, s2);
    
    280
    +        }
    
    281
    +
    
    282
    +        String getValue(Class klass) {
    
    283
    +            return cache.computeIfAbsent(klass, k -> function.apply(klass));
    
    284
    +        }
    
    285
    +
    
    286
    +        public void sort(List<C> list) {
    
    287
    +            list.sort(this);
    
    288
    +            cache.clear();
    
    289
    +        }
    
    290
    +    }
    
    267 291
     }

  • toolkit/api/src/main/java/fr/ird/observe/spi/json/java4all/DataMatrixAdapter.java
    1
    +package fr.ird.observe.spi.json.java4all;
    
    2
    +
    
    3
    +/*-
    
    4
    + * #%L
    
    5
    + * ObServe Toolkit :: API
    
    6
    + * %%
    
    7
    + * Copyright (C) 2008 - 2023 IRD, Ultreia.io
    
    8
    + * %%
    
    9
    + * This program is free software: you can redistribute it and/or modify
    
    10
    + * it under the terms of the GNU General Public License as
    
    11
    + * published by the Free Software Foundation, either version 3 of the
    
    12
    + * License, or (at your option) any later version.
    
    13
    + *
    
    14
    + * This program is distributed in the hope that it will be useful,
    
    15
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    16
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    
    17
    + * GNU General Public License for more details.
    
    18
    + *
    
    19
    + * You should have received a copy of the GNU General Public
    
    20
    + * License along with this program.  If not, see
    
    21
    + * <http://www.gnu.org/licenses/gpl-3.0.html>.
    
    22
    + * #L%
    
    23
    + */
    
    24
    +
    
    25
    +import com.google.auto.service.AutoService;
    
    26
    +import com.google.gson.JsonArray;
    
    27
    +import com.google.gson.JsonDeserializationContext;
    
    28
    +import com.google.gson.JsonDeserializer;
    
    29
    +import com.google.gson.JsonElement;
    
    30
    +import com.google.gson.JsonObject;
    
    31
    +import com.google.gson.JsonParseException;
    
    32
    +import com.google.gson.JsonPrimitive;
    
    33
    +import com.google.gson.JsonSerializationContext;
    
    34
    +import com.google.gson.JsonSerializer;
    
    35
    +import io.ultreia.java4all.util.json.JsonAdapter;
    
    36
    +import io.ultreia.java4all.util.matrix.DataMatrix;
    
    37
    +
    
    38
    +import java.lang.reflect.Type;
    
    39
    +import java.util.Arrays;
    
    40
    +import java.util.stream.Collectors;
    
    41
    +
    
    42
    +/**
    
    43
    + * Created at 01/12/2023.
    
    44
    + *
    
    45
    + * @author Tony Chemit - dev@tchemit.fr
    
    46
    + * @since 9.3.0
    
    47
    + */
    
    48
    +@AutoService(JsonAdapter.class)
    
    49
    +public class DataMatrixAdapter implements JsonDeserializer<DataMatrix>, JsonSerializer<DataMatrix>, JsonAdapter {
    
    50
    +
    
    51
    +    private static final String WIDTH = "width";
    
    52
    +    private static final String HEIGHT = "height";
    
    53
    +    private static final String X = "x";
    
    54
    +    private static final String Y = "y";
    
    55
    +    private static final String ROWS = "rows";
    
    56
    +
    
    57
    +    @Override
    
    58
    +    public Class<?> type() {
    
    59
    +        return DataMatrix.class;
    
    60
    +    }
    
    61
    +
    
    62
    +    @Override
    
    63
    +    public DataMatrix deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
    
    64
    +        JsonObject dataMatrixAsJsonObject = json.getAsJsonObject();
    
    65
    +
    
    66
    +        DataMatrix result = new DataMatrix();
    
    67
    +        result.setWidth(context.deserialize(dataMatrixAsJsonObject.get(WIDTH), int.class));
    
    68
    +        result.setHeight(context.deserialize(dataMatrixAsJsonObject.get(HEIGHT), int.class));
    
    69
    +        result.setX(context.deserialize(dataMatrixAsJsonObject.get(X), int.class));
    
    70
    +        result.setY(context.deserialize(dataMatrixAsJsonObject.get(Y), int.class));
    
    71
    +        JsonArray rows = dataMatrixAsJsonObject.getAsJsonArray(ROWS);
    
    72
    +        Object[][] data = new Object[result.getHeight()][result.getWidth()];
    
    73
    +        int index = 0;
    
    74
    +        for (JsonElement rowElement : rows) {
    
    75
    +            String[] deserialize = rowElement.getAsString().split("\\|\\|");
    
    76
    +            Object[] row = new Object[deserialize.length];
    
    77
    +            for (int i = 0; i < deserialize.length; i++) {
    
    78
    +                String s = deserialize[i];
    
    79
    +                row[i] = s.equals("$") ? null : s;
    
    80
    +            }
    
    81
    +            data[index++] = row;
    
    82
    +        }
    
    83
    +        result.setData(data);
    
    84
    +        return result;
    
    85
    +    }
    
    86
    +
    
    87
    +    @Override
    
    88
    +    public JsonElement serialize(DataMatrix src, Type typeOfSrc, JsonSerializationContext context) {
    
    89
    +        JsonObject result = new JsonObject();
    
    90
    +        result.addProperty(WIDTH, src.getWidth());
    
    91
    +        result.addProperty(HEIGHT, src.getHeight());
    
    92
    +        result.addProperty(X, src.getX());
    
    93
    +        result.addProperty(Y, src.getY());
    
    94
    +        JsonArray rows = new JsonArray(src.getHeight());
    
    95
    +        result.add(ROWS, rows);
    
    96
    +        for (Object[] row : src.getData()) {
    
    97
    +            rows.add(new JsonPrimitive(Arrays.stream(row).map(d -> d == null ? "$" : d.toString()).collect(Collectors.joining("||"))));
    
    98
    +        }
    
    99
    +
    
    100
    +        return result;
    
    101
    +    }
    
    102
    +}
    
    103
    +