Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
CaseOptionService.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/* CaseOptionsService.cc (C) 2000-2025 */
9/* */
10/* Management of dataset options. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/core/CaseOptionService.h"
15
16#include "arcane/utils/Collection.h"
17#include "arcane/utils/Enumerator.h"
18#include "arcane/utils/NotImplementedException.h"
19#include "arcane/utils/FatalErrorException.h"
20#include "arcane/utils/StringBuilder.h"
21#include "arcane/utils/internal/ParameterCaseOption.h"
22
23#include "arcane/core/IApplication.h"
24#include "arcane/core/IServiceFactory.h"
25#include "arcane/core/CaseOptionBuildInfo.h"
26#include "arcane/core/CaseOptionException.h"
27#include "arcane/core/CaseOptionError.h"
28#include "arcane/core/XmlNodeList.h"
29#include "arcane/core/ICaseDocumentVisitor.h"
30#include "arcane/core/ICaseDocument.h"
31#include "arcane/core/ICaseMng.h"
32#include "arcane/core/internal/ICaseOptionListInternal.h"
33#include "arcane/core/internal/StringVariableReplace.h"
34#include "arcane/core/internal/ICaseMngInternal.h"
35
36#include <typeinfo>
37
38/*---------------------------------------------------------------------------*/
39/*---------------------------------------------------------------------------*/
40
41namespace Arcane
42{
43
44namespace
45{
46
47 void
48 _getAvailableServiceNames(ICaseOptionServiceContainer* container, IApplication* app,
49 StringArray& names)
50 {
51 for (ServiceFactory2Collection::Enumerator i(app->serviceFactories2()); ++i;) {
53 IServiceInfo* si = sf2->serviceInfo();
54 // The service must be authorized for the dataset.
55 if (!(si->usageType() & Arcane::ST_CaseOption))
56 continue;
57 if (container->hasInterfaceImplemented(sf2)) {
58 names.add(sf2->serviceInfo()->localName());
59 }
60 }
61 }
62
63 bool
64 _tryCreateService(ICaseOptionServiceContainer* container, IApplication* app,
65 const String& service_name, Integer index, ICaseOptions* opt)
66 {
67 // Iterates through the list of factories and tries to create a service with the desired name
68 // that implements the correct interface.
69 bool is_found = false;
70 ServiceBuildInfoBase sbi(_arcaneDeprecatedGetSubDomain(opt), opt);
71 for (ServiceFactory2Collection::Enumerator i(app->serviceFactories2()); ++i;) {
73 IServiceInfo* si = sf2->serviceInfo();
74 if (si->localName() == service_name && container->tryCreateService(index, sf2, sbi)) {
75 opt->setCaseServiceInfo(si);
76 is_found = true;
77 break;
78 }
79 }
80 return is_found;
81 }
82
83} // namespace
84
85/*---------------------------------------------------------------------------*/
86/*---------------------------------------------------------------------------*/
87
89setMeshName(const String& mesh_name)
90{
91 m_impl->setMeshName(mesh_name);
92}
93
94/*---------------------------------------------------------------------------*/
95/*---------------------------------------------------------------------------*/
96
98meshName() const
99{
100 return m_impl->meshName();
101}
102
103/*---------------------------------------------------------------------------*/
104/*---------------------------------------------------------------------------*/
105
107setMeshName(const String& mesh_name)
108{
109 m_impl->setMeshName(mesh_name);
110}
111
112/*---------------------------------------------------------------------------*/
113/*---------------------------------------------------------------------------*/
114
116meshName() const
117{
118 return m_impl->meshName();
119}
120
121/*---------------------------------------------------------------------------*/
122/*---------------------------------------------------------------------------*/
123
124/*---------------------------------------------------------------------------*/
125/*---------------------------------------------------------------------------*/
126
127CaseOptionServiceImpl::
128CaseOptionServiceImpl(const CaseOptionBuildInfo& cob, bool allow_null, bool is_optional)
129: CaseOptions(cob.caseOptionList(), cob.name())
130, m_name(cob.name())
131, m_default_value(cob.defaultValue())
132, m_element(cob.element())
133, m_allow_null(allow_null)
134, m_is_optional(is_optional)
135, m_is_override_default(false)
136, m_container(nullptr)
137{
138}
139
140/*---------------------------------------------------------------------------*/
141/*---------------------------------------------------------------------------*/
142
143void CaseOptionServiceImpl::
144print(const String& lang, std::ostream& o) const
145{
146 ARCANE_UNUSED(lang);
147 o << serviceName();
148}
149
150/*---------------------------------------------------------------------------*/
151/*---------------------------------------------------------------------------*/
152
154visit(ICaseDocumentVisitor* visitor) const
155{
156 visitor->beginVisit(this);
157 CaseOptions::visit(visitor);
158 visitor->endVisit(this);
159}
160
161/*---------------------------------------------------------------------------*/
162/*---------------------------------------------------------------------------*/
163
166{
167 m_container = container;
168}
169
170/*---------------------------------------------------------------------------*/
171/*---------------------------------------------------------------------------*/
172
173void CaseOptionServiceImpl::
174_readPhase1()
175{
176 if (!m_container)
177 ARCANE_FATAL("null 'm_container'. did you called setContainer() method ?");
178
179 ITraceMng* tm = traceMng();
180
181 _setTranslatedName();
182 ICaseOptionList* col = configList();
183
185
186 if (child.null()) {
188 }
189 else {
190 if (col->rootElement() != child) // skip when rootElement already set to child (may appear in subDomain service)
191 col->_internalApi()->setRootElement(child);
192 }
193
194 XmlNode element = col->rootElement();
195 const ParameterListWithCaseOption& params = caseMng()->_internalImpl()->parameters();
196 ICaseDocumentFragment* doc = caseDocumentFragment();
197
198 const ParameterCaseOption pco{ params.getParameterCaseOption(doc->language()) };
199
200 String mesh_name;
201 {
202 String reference_input = pco.getParameterOrNull(element.xpathFullName(), "@mesh-name", 1);
203 if (!reference_input.null())
204 mesh_name = reference_input;
205 else
206 mesh_name = element.attrValue("mesh-name");
207 }
208
209 if (mesh_name.null()) {
210 mesh_name = meshName();
211 }
212 else {
213 // In an else block: Symbol replacement does not apply to default values in .axl.
214 mesh_name = StringVariableReplace::replaceWithCmdLineArgs(params, mesh_name, true);
215 }
216
217 tm->info(5) << "** CaseOptionService::read() ELEMENT <" << rootTagName() << "> " << col->rootElement().name()
218 << " full=" << col->rootElement().xpathFullName()
219 << " is_present=" << col->isPresent()
220 << " is_optional=" << isOptional()
221 << " allow_null=" << m_allow_null
222 << " mesh-name=" << mesh_name
223 << "\n";
224
225 if (_setMeshHandleAndCheckDisabled(mesh_name))
226 return;
227
228 String str_val;
229 {
230 String reference_input = pco.getParameterOrNull(element.xpathFullName(), "@name", 1);
231 if (!reference_input.null())
232 str_val = reference_input;
233 else
234 str_val = element.attrValue("name");
235 }
236
237 //cerr << "** STR_VAL <" << str_val << " - " << m_default_value << ">\n";
238
239 if (str_val.null()) {
240 // Uses the default value:
241 // - if it was specified by the user, use this one.
242 // - otherwise use the one associated with the default category.
243 // - otherwise, the classic default value.
244 if (!m_is_override_default) {
245 String category = doc->defaultCategory();
246 if (!category.null()) {
247 String v = m_default_values.find(category);
248 if (!v.null()) {
249 m_default_value = v;
250 }
251 }
252 }
253 str_val = m_default_value;
254 }
255 else {
256 // In an else block: Symbol replacement does not apply to default values in .axl.
257 str_val = StringVariableReplace::replaceWithCmdLineArgs(params, str_val, true);
258 }
259 if (str_val.null() && !isOptional()) {
260 CaseOptionError::addOptionNotFoundError(doc, A_FUNCINFO, "@name", element);
261 return;
262 }
263 m_service_name = str_val;
264
265 // If the service can be null and the element is not present in the dataset,
266 // it is considered that it should not be loaded.
267 bool need_create = col->isPresent() || !isOptional();
268
269 if (need_create) {
270 m_container->allocate(1);
271 bool is_found = _tryCreateService(m_container, caseMng()->application(), str_val, 0, this);
272
273 if (!is_found && !m_allow_null) {
274 // The desired service was not found. This is an error.
275 // Searches for the names of valid implementations to display in the corresponding error message.
276 StringUniqueArray valid_names;
277 getAvailableNames(valid_names);
278 CaseOptionError::addError(doc, A_FUNCINFO, element.xpathFullName(),
279 String::format("Unable to find a service named '{0}' (valid values:{1})",
280 str_val, valid_names),
281 true);
282 }
283 }
284}
285
286/*---------------------------------------------------------------------------*/
287/*---------------------------------------------------------------------------*/
288
290read(eCaseOptionReadPhase read_phase)
291{
292 if (read_phase == eCaseOptionReadPhase::Phase1)
293 _readPhase1();
294 CaseOptions::read(read_phase);
295}
296
297/*---------------------------------------------------------------------------*/
298/*---------------------------------------------------------------------------*/
299
300void CaseOptionServiceImpl::
301setDefaultValue(const String& def_value)
302{
303 if (!m_service_name.null()) {
304 String xpath_name = configList()->rootElement().xpathFullName();
305 ARCANE_FATAL("Can not set default service name because service is already allocated (option='{0}')",
306 xpath_name);
307 }
308 m_default_value = def_value;
309 m_is_override_default = true;
310}
311
312/*---------------------------------------------------------------------------*/
313/*---------------------------------------------------------------------------*/
314
315void CaseOptionServiceImpl::
316addDefaultValue(const String& category, const String& value)
317{
318 m_default_values.add(category, value);
319}
320
321/*---------------------------------------------------------------------------*/
322/*---------------------------------------------------------------------------*/
323
325getAvailableNames(StringArray& names) const
326{
327 _getAvailableServiceNames(m_container, caseMng()->application(), names);
328}
329
330/*---------------------------------------------------------------------------*/
331/*---------------------------------------------------------------------------*/
332
333/*---------------------------------------------------------------------------*/
334/*---------------------------------------------------------------------------*/
335
336CaseOptionMultiServiceImpl::
337CaseOptionMultiServiceImpl(const CaseOptionBuildInfo& cob, bool allow_null)
338: CaseOptionsMulti(cob.caseOptionList(), cob.name(), cob.element(), cob.minOccurs(), cob.maxOccurs())
339, m_allow_null(allow_null)
340, m_default_value(cob.defaultValue())
341, m_notify_functor(nullptr)
342, m_container(nullptr)
343{
344}
345
346/*---------------------------------------------------------------------------*/
347/*---------------------------------------------------------------------------*/
348
349CaseOptionMultiServiceImpl::
350~CaseOptionMultiServiceImpl()
351{
352}
353
354/*---------------------------------------------------------------------------*/
355/*---------------------------------------------------------------------------*/
356
359{
360 m_container = container;
361}
362
363/*---------------------------------------------------------------------------*/
364/*---------------------------------------------------------------------------*/
365
367visit(ICaseDocumentVisitor* visitor) const
368{
369 Integer index = 0;
370 for (const auto& o : m_allocated_options) {
371 visitor->beginVisit(this, index);
372 o->visit(visitor);
373 visitor->endVisit(this, index);
374 ++index;
375 }
376}
377
378/*---------------------------------------------------------------------------*/
379/*---------------------------------------------------------------------------*/
380
381void CaseOptionMultiServiceImpl::
382multiAllocate(const XmlNodeList& elem_list)
383{
384 if (!m_container)
385 ARCANE_FATAL("null 'm_container'. did you called setContainer() method ?");
386
387 const ParameterListWithCaseOption& params = caseMng()->_internalImpl()->parameters();
388 const ParameterCaseOption pco{ params.getParameterCaseOption(caseDocumentFragment()->language()) };
389
390 XmlNode parent_element = configList()->parentElement();
391
392 String full_xpath = String::format("{0}/{1}", parent_element.xpathFullName(), name());
393 // !!! In XML, we start counting from 1, not 0.
394 UniqueArray<Integer> option_in_param;
395 pco.indexesInParam(full_xpath, option_in_param, true);
396
397 Integer size = elem_list.size();
398
399 bool is_optional = configList()->isOptional();
400
401 if (size == 0 && option_in_param.empty() && is_optional) {
402 return;
403 }
404
405 Integer min_occurs = configList()->minOccurs();
406 Integer max_occurs = configList()->maxOccurs();
407
408 Integer max_in_param = 0;
409
410 // We check if the user has provided an index too high for the option in the command line.
411 if (!option_in_param.empty()) {
412 max_in_param = option_in_param[0];
413 for (Integer index : option_in_param) {
414 if (index > max_in_param)
415 max_in_param = index;
416 }
417 if (max_occurs >= 0) {
418 if (max_in_param > max_occurs) {
419 StringBuilder msg = "Bad number of occurences in command line (greater than max)";
420 msg += " index_max_in_param=";
421 msg += max_in_param;
422 msg += " max_occur=";
423 msg += max_occurs;
424 msg += " option=";
425 msg += full_xpath;
426 throw CaseOptionException(A_FUNCINFO, msg.toString(), true);
427 }
428 }
429 }
430
431 if (max_occurs >= 0) {
432 if (size > max_occurs) {
433 StringBuilder msg = "Bad number of occurences (greater than max)";
434 msg += " nb_occur=";
435 msg += size;
436 msg += " max_occur=";
437 msg += max_occurs;
438 msg += " option=";
439 msg += full_xpath;
440 throw CaseOptionException(A_FUNCINFO, msg.toString(), true);
441 }
442 }
443
444 // There will always be at least min_occurs options.
445 // If there are not enough options in the dataset and in the command line parameters,
446 // we add default services (if there is no default, there will be a crash).
447 Integer final_size = std::max(size, std::max(min_occurs, max_in_param));
448
449 ITraceMng* tm = traceMng();
450
451 IApplication* app = caseMng()->application();
452 ICaseDocumentFragment* doc = caseDocumentFragment();
453
454 m_container->allocate(final_size);
455
456 m_allocated_options.resize(final_size);
457 m_services_name.resize(final_size);
458
459 // First, we will have the dataset options: since we cannot define an index
460 // for options in the dataset, they will necessarily be at the beginning and contiguous.
461 // Then, if options are missing to reach min_occurs, we add default options.
462 // If there is no default option, there will be an exception.
463 // Finally, the user may have added options from the command line. We add them then.
464 // If the user wants to modify dataset values from the command line, we replace the
465 // options as we read them.
466 for (Integer index = 0; index < final_size; ++index) {
467 XmlNode element;
468
469 String mesh_name;
470 String str_val;
471
472 // Command line parameters section.
473 if (option_in_param.contains(index + 1)) {
474 mesh_name = pco.getParameterOrNull(full_xpath, "@mesh-name", index + 1);
475 str_val = pco.getParameterOrNull(full_xpath, "@name", index + 1);
476 }
477 // Dataset section.
478 if (index < size && (mesh_name.null() || str_val.null())) {
479 element = elem_list[index];
480 if (!element.null()) {
481 if (mesh_name.null())
482 mesh_name = element.attrValue("mesh-name");
483 if (str_val.null())
484 str_val = element.attrValue("name");
485 }
486 }
487
488 // Default value.
489 if (mesh_name.null()) {
490 mesh_name = meshName();
491 }
492 else {
493 // In an else: Symbol replacement does not apply to .axl default values.
494 mesh_name = StringVariableReplace::replaceWithCmdLineArgs(params, mesh_name, true);
495 }
496
497 // Default value.
498 if (str_val.null()) {
499 str_val = _defaultValue();
500 }
501 else {
502 // In an else: Symbol replacement does not apply to .axl default values.
503 str_val = StringVariableReplace::replaceWithCmdLineArgs(params, str_val, true);
504 }
505
506 // If we are not using the dataset options, we must create new options.
507 if (element.null()) {
508 element = parent_element.createElement(name());
509
510 element.setAttrValue("mesh-name", mesh_name);
511 element.setAttrValue("name", str_val);
512 }
513
514 tm->info(5) << "CaseOptionMultiServiceImpl name=" << name()
515 << " index=" << index
516 << " v=" << str_val
517 << " default_value='" << _defaultValue() << "'"
518 << " mesh=" << meshHandle().meshName();
519
520 // Now, this crash also concerns the case where there are no default values and not enough options to reach min_occurs.
521 if (str_val.null())
522 throw CaseOptionException("get_value", "@name");
523
524 // TODO: check if we cannot create a CaseOptionService directly.
525 auto* coptions = new CaseOptions(configList(), name(), parent_element, false, true);
526 if (coptions->_setMeshHandleAndCheckDisabled(mesh_name)) {
527 delete coptions;
528 continue;
529 }
530 coptions->configList()->_internalApi()->setRootElement(element);
531 bool is_found = _tryCreateService(m_container, app, str_val, index, coptions);
532
533 if (!is_found) {
534 tm->info(5) << "CaseOptionMultiServiceImpl name=" << name()
535 << " index=" << index
536 << " service not found";
537 delete coptions;
538 coptions = nullptr;
539 // Search for valid implementation names
540 StringUniqueArray valid_names;
541 getAvailableNames(valid_names);
542 CaseOptionError::addError(doc, A_FUNCINFO, element.xpathFullName(),
543 String::format("Unable to find a service named '{0}' (valid values:{1})",
544 str_val, valid_names),
545 true);
546 }
547 m_services_name[index] = str_val;
548 m_allocated_options[index] = coptions;
549 }
550
551 if (m_notify_functor)
552 m_notify_functor->executeFunctor();
553}
554
555/*---------------------------------------------------------------------------*/
556/*---------------------------------------------------------------------------*/
557
559getAvailableNames(StringArray& names) const
560{
561 _getAvailableServiceNames(m_container, caseMng()->application(), names);
562}
563
564/*---------------------------------------------------------------------------*/
565/*---------------------------------------------------------------------------*/
566
567} // End namespace Arcane
568
569/*---------------------------------------------------------------------------*/
570/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
Information for building a dataset option.
static void addError(ICaseDocumentFragment *document, const TraceInfo &where, const String &node_name, const String &message, bool is_collective=false)
Generic error.
static void addOptionNotFoundError(ICaseDocumentFragment *document, const TraceInfo &where, const String &node_name, const XmlNode &parent)
Error when a dataset option is not found. This error is collective.
UniqueArray< String > m_services_name
Service names for each occurrence.
void setContainer(ICaseOptionServiceContainer *container)
Positions the instance container.
UniqueArray< ReferenceCounter< ICaseOptions > > m_allocated_options
List of allocated options that must be deleted.
void visit(ICaseDocumentVisitor *visitor) const override
Applies the visitor to this option.
void getAvailableNames(StringArray &names) const
Returns the valid implementation names for this service in names.
String meshName() const
Mesh name to which the service is associated.
void setMeshName(const String &mesh_name)
Sets the mesh name to which the service will be associated.
void setContainer(ICaseOptionServiceContainer *container)
Positions the instance container.
void read(eCaseOptionReadPhase phase) override
Performs the reading of the read_phase phase of the options.
void visit(ICaseDocumentVisitor *visitor) const override
Applies the visitor to this option.
virtual void getAvailableNames(StringArray &names) const
Returns the valid implementation names for this service in names.
StringDictionary m_default_values
List of default values by category.
void setMeshName(const String &mesh_name)
Sets the mesh name to which the service will be associated.
String meshName() const
Mesh name to which the service is associated.
Base class for an array of complex data set options.
Base class for a data set options list.
Definition CaseOptions.h:58
bool _setMeshHandleAndCheckDisabled(const String &mesh_name)
Positions the mesh associated with this option.
void visit(ICaseDocumentVisitor *visitor) const override
Applies the visitor to this option.
String rootTagName() const override
Returns the name of the element in the data set language.
virtual String name() const
Returns the name of the element in the data set language.
CaseOptions(ICaseMng *cm, const String &name)
Constructs an options set.
Application interface.
Visitor interface for a dataset option.
virtual const ParameterListWithCaseOption & parameters() const =0
List of parameters that can override the dataset.
virtual ICaseMngInternal * _internalImpl()=0
Internal implementation.
virtual void setRootElementWithParent(const XmlNode &parent_element)=0
Positions the root element of the list, with parent_element as parent. If already positioned,...
virtual void setRootElement(const XmlNode &root_element)=0
Positions the root element of the list. If already positioned, throws an exception.
Interface for a data set options list.
virtual XmlNode rootElement() const =0
Returns the element associated with this options list.
virtual bool isOptional() const =0
Indicates if the option is optional.
virtual XmlNode parentElement() const =0
Returns the parent element.
virtual bool isPresent() const =0
Indicates if the option is present in the data set.
virtual ICaseOptionListInternal * _internalApi()=0
Internal Arcane API.
Interface of a service instance container.
Interface for a list of data set options.
Interface for service or module information.
virtual TraceMessage info()=0
Stream for an information message.
Class representing the set of parameters that can modify the dataset options.
void indexesInParam(const String &xpath_before_index, const String &xpath_after_index, UniqueArray< Integer > &indexes) const
Method allowing retrieval of the index or indices of the option.
String getParameterOrNull(const String &xpath_before_index, const String &xpath_after_index, Integer index) const
Method allowing retrieval of an option's value.
Parameter list with information to override dataset options.
ParameterCaseOption getParameterCaseOption(const String &language) const
Method to retrieve an object of type ParameterCaseOption.
Information for creating a service.
bool null() const
Returns true if the string is null.
Definition String.cc:306
List of nodes of a DOM tree.
Definition XmlNodeList.h:36
Node of a DOM tree.
Definition XmlNode.h:51
String xpathFullName() const
XPath name of the node with its ancestors.
Definition XmlNode.cc:152
XmlNode child(const String &name) const
Child node of this node with name name.
Definition XmlNode.cc:73
bool null() const
True if the node is null.
Definition XmlNode.h:303
String name() const
Node name.
Definition XmlNode.cc:141
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Array< String > StringArray
Dynamic one-dimensional array of strings.
Definition UtilsTypes.h:145
eCaseOptionReadPhase
Reading phases.
Int32 Integer
Type representing an integer.
@ ST_CaseOption
The service is used at the dataset level.
UniqueArray< String > StringUniqueArray
Dynamic 1D array of strings.
Definition UtilsTypes.h:359