Arcane  4.2.1.0
Developer documentation
Loading...
Searching...
No Matches
AxlOptionsBuilder.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* AxlOptionsBuilder.cc (C) 2000-2026 */
9/* */
10/* Classes to dynamically create data set options. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/core/AxlOptionsBuilder.h"
15
16#include "arcane/utils/FatalErrorException.h"
17#include "arcane/utils/Array.h"
18#include "arcane/utils/JSONWriter.h"
19#include "arcane/utils/ScopedPtr.h"
20
21#include "arcane/core/DomUtils.h"
22#include "arcane/core/XmlNode.h"
23#include "arcane/core/IXmlDocumentHolder.h"
24#include "arcane/core/CaseNodeNames.h"
25
26#include <fstream>
27#include <iostream>
28
29/*---------------------------------------------------------------------------*/
30/*---------------------------------------------------------------------------*/
31
32namespace Arcane::AxlOptionsBuilder
33{
34// TODO: handle in JSON the case where an option with the same name is present
35// multiple times.
36
37// TODO: use a union in option to keep values as
38// reals without immediately converting them to strings.
39
40/*---------------------------------------------------------------------------*/
41/*---------------------------------------------------------------------------*/
42
44{
45 friend DocumentXmlWriter;
46 friend DocumentJSONWriter;
47
48 public:
49
50 void add(const std::initializer_list<OneOption>& options)
51 {
52 for (const auto& o : options)
53 m_options.add(o);
54 }
55
56 public:
57
58 UniqueArray<OneOption> m_options;
59};
60
61/*---------------------------------------------------------------------------*/
62/*---------------------------------------------------------------------------*/
63
64OneOption::
65OneOption(Type type, const String& name, const OptionList& options)
66: m_type(type)
67, m_name(name)
68{
69 OptionList cloned_list = options.clone();
70 m_sub_option = cloned_list.m_p;
71}
72
73/*---------------------------------------------------------------------------*/
74/*---------------------------------------------------------------------------*/
75
76ServiceInstance::
77ServiceInstance(const String& option_name, const String& service_name,
78 const std::initializer_list<OneOption>& options)
79: OneOption(Type::CO_ServiceInstance, option_name, OptionList{})
80{
81 m_sub_option->add(options);
82 m_service_name = service_name;
83}
84
85/*---------------------------------------------------------------------------*/
86/*---------------------------------------------------------------------------*/
87
88ServiceInstance::
89ServiceInstance(const String& option_name, const String& service_name,
90 const OptionList& options)
91: OneOption(Type::CO_ServiceInstance, option_name, options)
92{
93 m_service_name = service_name;
94}
95
96/*---------------------------------------------------------------------------*/
97/*---------------------------------------------------------------------------*/
98
99ServiceInstance::
100ServiceInstance(const String& option_name, const String& service_name)
101: OneOption(Type::CO_ServiceInstance, option_name, String{})
102{
103 m_service_name = service_name;
104}
105
106/*---------------------------------------------------------------------------*/
107/*---------------------------------------------------------------------------*/
108
109Complex::
110Complex(const String& name, const std::initializer_list<OneOption>& options)
111: OneOption(Type::CO_Complex, name, OptionList{})
112{
113 m_sub_option->add(options);
114}
115
116/*---------------------------------------------------------------------------*/
117/*---------------------------------------------------------------------------*/
118
119Complex::
120Complex(const String& name, const OptionList& options)
121: OneOption(Type::CO_Complex, name, options)
122{
123}
124
125/*---------------------------------------------------------------------------*/
126/*---------------------------------------------------------------------------*/
127
128/*---------------------------------------------------------------------------*/
129/*---------------------------------------------------------------------------*/
130
133: m_p(std::make_shared<OneOptionImpl>())
134{
135}
136
137/*---------------------------------------------------------------------------*/
138/*---------------------------------------------------------------------------*/
139
141OptionList(const std::initializer_list<OneOption>& options)
142: m_p(std::make_shared<OneOptionImpl>())
143{
144 add(options);
145}
146
147/*---------------------------------------------------------------------------*/
148/*---------------------------------------------------------------------------*/
149
150OptionList& OptionList::
151add(const String& name, const OptionList& option)
152{
153 m_p->m_options.add(OneOption(OneOption::Type::CO_Complex, name, option));
154 return (*this);
155}
156
157/*---------------------------------------------------------------------------*/
158/*---------------------------------------------------------------------------*/
159
160OptionList& OptionList::
161add(const std::initializer_list<OneOption>& options)
162{
163 for (const auto& o : options)
164 m_p->m_options.add(o);
165 return (*this);
166}
167
168/*---------------------------------------------------------------------------*/
169/*---------------------------------------------------------------------------*/
170
171OptionList OptionList::
172clone() const
173{
174 OptionList new_opt;
175 new_opt.m_p->m_options = m_p->m_options;
176 return new_opt;
177}
178
179/*---------------------------------------------------------------------------*/
180/*---------------------------------------------------------------------------*/
181
182/*---------------------------------------------------------------------------*/
183/*---------------------------------------------------------------------------*/
184
186class DocumentXmlWriter
187{
188 public:
189
190 static IXmlDocumentHolder* toXml(const Document& d)
191 {
192 auto* doc = domutils::createXmlDocument();
193 XmlNode document_node = doc->documentNode();
194 DocumentXmlWriter writer(d.language());
195 CaseNodeNames& cnn = writer.m_case_node_names;
196 XmlNode root_element = document_node.createAndAppendElement("root", String());
197 root_element.setAttrValue(cnn.lang_attribute, d.language());
198 XmlNode opt_element = root_element.createAndAppendElement("dynamic-options", String());
199 writer._writeToXml(d.m_options.m_p.get(), opt_element);
200 return doc;
201 }
202
203 private:
204
205 void _writeToXml(OneOptionImpl* opt, XmlNode element)
206 {
207 for (OneOption& o : opt->m_options) {
208 XmlNode current_element = element.createAndAppendElement(o.m_name, o.m_value);
209 if (o.m_sub_option.get())
210 _writeToXml(o.m_sub_option.get(), current_element);
211
212 if (!o.m_service_name.null())
213 current_element.setAttrValue("name", o.m_service_name);
214
215 if (!o.m_function_name.null()) {
216 String funcname_attr = m_case_node_names.function_ref;
217 current_element.setAttrValue(funcname_attr, o.m_function_name);
218 }
219 }
220 }
221
222 private:
223
224 explicit DocumentXmlWriter(const String& lang)
225 : m_case_node_names(lang)
226 {}
227
228 private:
229
230 CaseNodeNames m_case_node_names;
231};
232
233/*---------------------------------------------------------------------------*/
234/*---------------------------------------------------------------------------*/
235
237class DocumentJSONWriter
238{
239 public:
240
241 static String toJSON(const Document& d)
242 {
243 DocumentJSONWriter writer(d.language());
244
245 writer._write(d);
246 return writer.m_json_writer.getBuffer();
247 }
248
249 private:
250
251 void _write(const Document& doc)
252 {
253 JSONWriter::Object o(m_json_writer);
254 m_json_writer.write("language", doc.language());
255 m_json_writer.write("version", "1");
256 {
257 JSONWriter::Object o2(m_json_writer, "options");
258 _write(doc.m_options.m_p.get());
259 }
260 }
261
262 void _write(OneOptionImpl* opt)
263 {
264 if (!opt)
265 return;
266 // TODO: handle the case where an option with the same name
267 // is present multiple times.
268 // In theory, you can have multiple with the same key, but
269 // this is not recommended. The best approach in this case is
270 // to use an array.
271 for (OneOption& o : opt->m_options) {
272 _write(o);
273 }
274 }
275
276 void _write(OneOption& o)
277 {
278 OneOptionImpl* sub_options = o.m_sub_option.get();
279 if (o.m_type == OneOption::Type::CO_ServiceInstance) {
280 JSONWriter::Object j1(m_json_writer, o.m_name);
281 if (!o.m_service_name.null())
282 m_json_writer.write("$name", o.m_service_name);
283 _write(sub_options);
284 return;
285 }
286
287 if (o.m_type == OneOption::Type::CO_Complex) {
288 JSONWriter::Object j1(m_json_writer, o.m_name);
289 _write(sub_options);
290 return;
291 }
292
293 // Simple or enumerated or extended value.
294 // If there is no table of march, we do directly { "key" : "value" }.
295 // Otherwise we use a sub-object:
296 // {
297 // "$function" : "function_name",
298 // "$value" : "value"
299 // }
300
301 if (!o.m_function_name.null()) {
302 JSONWriter::Object j1(m_json_writer, o.m_name);
303 m_json_writer.write(m_case_function_json_name, o.m_function_name);
304 m_json_writer.write("$value", o.m_value);
305 return;
306 }
307
308 m_json_writer.write(o.m_name, o.m_value);
309 }
310
311 private:
312
313 explicit DocumentJSONWriter(const String& lang)
314 : m_case_node_names(lang)
315 {
316 m_case_function_json_name = String("$") + m_case_node_names.function_ref;
317 }
318
319 private:
320
321 CaseNodeNames m_case_node_names;
322 JSONWriter m_json_writer;
323 String m_case_function_json_name;
324};
325
326/*---------------------------------------------------------------------------*/
327/*---------------------------------------------------------------------------*/
328
329extern "C++" IXmlDocumentHolder*
330documentToXml(const Document& d)
331{
332 return DocumentXmlWriter::toXml(d);
333}
334
335/*---------------------------------------------------------------------------*/
336/*---------------------------------------------------------------------------*/
337
338} // namespace Arcane::AxlOptionsBuilder
339
340/*---------------------------------------------------------------------------*/
341/*---------------------------------------------------------------------------*/
342
343namespace Arcane
344{
345
346/*---------------------------------------------------------------------------*/
347/*---------------------------------------------------------------------------*/
348
349extern "C++" ARCANE_CORE_EXPORT void
350_testAxlOptionsBuilder()
351{
352 using namespace AxlOptionsBuilder;
353 std::cout << "TestOptionList\n";
354 OptionList sub_opt({ Simple("max", 35),
355 Simple("min", 13.2),
356 Simple("test", 1) });
357
358 OptionList service_opt({ Simple("service-option1", 42),
359 Simple("service-option2", -1.5) });
360
361 OptionList dco({ Simple("toto", 3),
362 Simple("titi", 3.1).addFunction("func1"),
363 Simple("tutu", "Hello"),
364 Enumeration("a", "vx"),
365 Extended("extended1", "ext"),
366 ServiceInstance("my-service1", "TestService1",
367 { Simple("service-option1", 25),
368 Simple("service-option2", 3.2) }),
369 ServiceInstance("my-service2", "TestService2"),
370 // TODO add service with default name.
371 ServiceInstance("my-service3", "TestService3", service_opt),
372 Complex("sub-opt1",
373 { Simple("max", 25),
374 Simple("min", 23.2),
375 Simple("test", 4) }),
376 Complex("sub-opt2", sub_opt) });
377 Document doc("fr", dco);
378 ScopedPtrT<IXmlDocumentHolder> x(DocumentXmlWriter::toXml(doc));
379 String s = x->save();
380 std::cout << "VALUE:" << s << "\n";
381
382 String ref_str = "<?xml version=\"1.0\"?>\n"
383 "<root xml:lang=\"fr\"><dynamic-options><toto>3</toto><titi fonction=\"func1\">3.1</titi>"
384 "<tutu>Hello</tutu><a>vx</a><extended1>ext</extended1><my-service1 name=\"TestService1\">"
385 "<service-option1>25</service-option1><service-option2>3.2</service-option2></my-service1>"
386 "<my-service2 name=\"TestService2\"/><my-service3 name=\"TestService3\">"
387 "<service-option1>42</service-option1><service-option2>-1.5</service-option2></my-service3>"
388 "<sub-opt1><max>25</max><min>23.2</min><test>4</test></sub-opt1><sub-opt2><max>35</max>"
389 "<min>13.2</min><test>1</test></sub-opt2></dynamic-options></root>\n";
390 if (s != ref_str)
391 ARCANE_FATAL("BAD VALUE v={0} expected={1}", s, ref_str);
392
393 {
394 String json_string = DocumentJSONWriter::toJSON(doc);
395 {
396 std::ofstream ofile("test1.json");
397 ofile << json_string;
398 }
399 std::cout << "JSON=" << json_string << "\n";
400 }
401}
402
403/*---------------------------------------------------------------------------*/
404/*---------------------------------------------------------------------------*/
405
406} // namespace Arcane
407
408/*---------------------------------------------------------------------------*/
409/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
Data set 'ServiceInstance' option.
Base class for a dynamic option.
String m_service_name
Option value (if CO_Simple option).
OptionList()
Constructs an empty set of options.
XML node names of an Arcane dataset.
Manager of a DOM document.
Encapsulation of an automatically destructing pointer.
Definition ScopedPtr.h:44
bool null() const
Returns true if the string is null.
Definition String.cc:306
1D data vector with value semantics (STL style).
Node of a DOM tree.
Definition XmlNode.h:51
void setAttrValue(const String &name, const String &value)
Sets the attribute name to the value value.
Definition XmlNode.cc:248
GenericDocument< UTF8<> > Document
GenericDocument with UTF8 encoding.
Definition document.h:2891
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Type
Type of JSON value.
Definition rapidjson.h:730