Arcane  v3.16.0.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
CaseOptionService.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2025 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/* Gestion des options du jeu de données. */
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/ApplicationInfo.h"
21#include "arcane/utils/CommandLineArguments.h"
22#include "arcane/utils/StringBuilder.h"
23
24#include "arcane/core/IApplication.h"
25#include "arcane/core/IServiceFactory.h"
26#include "arcane/core/CaseOptionBuildInfo.h"
27#include "arcane/core/CaseOptionException.h"
28#include "arcane/core/CaseOptionError.h"
29#include "arcane/core/XmlNodeList.h"
30#include "arcane/core/ICaseDocumentVisitor.h"
31#include "arcane/core/ICaseDocument.h"
32#include "arcane/core/ICaseMng.h"
33#include "arcane/core/internal/ICaseOptionListInternal.h"
34#include "arcane/core/internal/StringVariableReplace.h"
35#include "arcane/utils/ParameterCaseOption.h"
36
37#include <typeinfo>
38
39/*---------------------------------------------------------------------------*/
40/*---------------------------------------------------------------------------*/
41
42namespace Arcane
43{
44
45namespace {
46
47void
48_getAvailableServiceNames(ICaseOptionServiceContainer* container,IApplication* app,
49 StringArray& names)
50{
51 for( ServiceFactory2Collection::Enumerator i(app->serviceFactories2()); ++i; ){
53 IServiceInfo* si = sf2->serviceInfo();
54 // Il faut que le service soit autorisé pour le jeu de données.
55 if (!(si->usageType() & Arcane::ST_CaseOption))
56 continue;
57 if (container->hasInterfaceImplemented(sf2)){
58 names.add(sf2->serviceInfo()->localName());
59 }
60 }
61}
62
63bool
64_tryCreateService(ICaseOptionServiceContainer* container,IApplication* app,
65 const String& service_name,Integer index,ICaseOptions* opt)
66{
67 // Parcours la liste des fabriques et essaie de créer un service avec le nom
68 // qu'on souhaite et qui implémente la bonne 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}
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
184 XmlNode child = m_element.child(rootTagName());
185
186 if (child.null()) {
187 col->_internalApi()->setRootElementWithParent(m_element);
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 ParameterList& params = caseMng()->application()->applicationInfo().commandLineArguments().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 // Dans un else : Le remplacement de symboles ne s'applique pas pour les valeurs par défault du .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
226 if (_setMeshHandleAndCheckDisabled(mesh_name))
227 return;
228
229 String str_val;
230 {
231 String reference_input = pco.getParameterOrNull(element.xpathFullName(), "@name", 1);
232 if (!reference_input.null())
233 str_val = reference_input;
234 else
235 str_val = element.attrValue("name");
236 }
237
238 //cerr << "** STR_VAL <" << str_val << " - " << m_default_value << ">\n";
239
240 if (str_val.null()){
241 // Utilise la valeur par défaut :
242 // - si elle a été spécifiée par l'utilisateur, utilise celle-ci.
243 // - sinon utilise celle de la catégorie associée aux défauts.
244 // - sinon, la valeur par défaut classique.
245 if (!m_is_override_default){
246 String category = doc->defaultCategory();
247 if (!category.null()){
248 String v = m_default_values.find(category);
249 if (!v.null()){
250 m_default_value = v;
251 }
252 }
253 }
254 str_val = m_default_value;
255 }
256 else {
257 // Dans un else : Le remplacement de symboles ne s'applique pas pour les valeurs par défault du .axl.
258 str_val = StringVariableReplace::replaceWithCmdLineArgs(params, str_val, true);
259 }
260 if (str_val.null() && !isOptional()){
261 CaseOptionError::addOptionNotFoundError(doc,A_FUNCINFO,"@name",element);
262 return;
263 }
264 m_service_name = str_val;
265
266 // Si le service peut-être nul et que l'élément n'est pas présent dans le jeu de données,
267 // on considère qu'il ne doit pas être chargé.
268 bool need_create = col->isPresent() || !isOptional();
269
270 if (need_create){
271 m_container->allocate(1);
272 bool is_found = _tryCreateService(m_container,caseMng()->application(),str_val,0,this);
273
274 if (!is_found && !m_allow_null){
275 // Le service souhaité n'est pas trouvé. Il s'agit d'une erreur.
276 // Recherche les noms des implémentations valides pour affichage dans le message
277 // d'erreur correspondant.
278 StringUniqueArray valid_names;
279 getAvailableNames(valid_names);
280 CaseOptionError::addError(doc,A_FUNCINFO,element.xpathFullName(),
281 String::format("Unable to find a service named '{0}' (valid values:{1})",
282 str_val,valid_names),true);
283 }
284 }
285}
286
287/*---------------------------------------------------------------------------*/
288/*---------------------------------------------------------------------------*/
289
291read(eCaseOptionReadPhase read_phase)
292{
293 if (read_phase==eCaseOptionReadPhase::Phase1)
294 _readPhase1();
295 CaseOptions::read(read_phase);
296}
297
298/*---------------------------------------------------------------------------*/
299/*---------------------------------------------------------------------------*/
300
301void CaseOptionServiceImpl::
302setDefaultValue(const String& def_value)
303{
304 if (!m_service_name.null()){
305 String xpath_name = configList()->rootElement().xpathFullName();
306 ARCANE_FATAL("Can not set default service name because service is already allocated (option='{0}')",
307 xpath_name);
308 }
309 m_default_value = def_value;
310 m_is_override_default = true;
311}
312
313/*---------------------------------------------------------------------------*/
314/*---------------------------------------------------------------------------*/
315
316void CaseOptionServiceImpl::
317addDefaultValue(const String& category,const String& value)
318{
319 m_default_values.add(category,value);
320}
321
322/*---------------------------------------------------------------------------*/
323/*---------------------------------------------------------------------------*/
324
326getAvailableNames(StringArray& names) const
327{
328 _getAvailableServiceNames(m_container,caseMng()->application(),names);
329}
330
331/*---------------------------------------------------------------------------*/
332/*---------------------------------------------------------------------------*/
333
334/*---------------------------------------------------------------------------*/
335/*---------------------------------------------------------------------------*/
336
337CaseOptionMultiServiceImpl::
338CaseOptionMultiServiceImpl(const CaseOptionBuildInfo& cob,bool allow_null)
339: CaseOptionsMulti(cob.caseOptionList(),cob.name(),cob.element(),cob.minOccurs(),cob.maxOccurs())
340, m_allow_null(allow_null)
341, m_default_value(cob.defaultValue())
342, m_notify_functor(nullptr)
343, m_container(nullptr)
344{
345}
346
347/*---------------------------------------------------------------------------*/
348/*---------------------------------------------------------------------------*/
349
350CaseOptionMultiServiceImpl::
351~CaseOptionMultiServiceImpl()
352{
353}
354
355/*---------------------------------------------------------------------------*/
356/*---------------------------------------------------------------------------*/
357
360{
361 m_container = container;
362}
363
364/*---------------------------------------------------------------------------*/
365/*---------------------------------------------------------------------------*/
366
368visit(ICaseDocumentVisitor* visitor) const
369{
370 Integer index = 0;
371 for( const auto& o : m_allocated_options ){
372 visitor->beginVisit(this,index);
373 o->visit(visitor);
374 visitor->endVisit(this,index);
375 ++index;
376 }
377}
378
379/*---------------------------------------------------------------------------*/
380/*---------------------------------------------------------------------------*/
381
382void CaseOptionMultiServiceImpl::
383multiAllocate(const XmlNodeList& elem_list)
384{
385 if (!m_container)
386 ARCANE_FATAL("null 'm_container'. did you called setContainer() method ?");
387
388 const ParameterList& params = caseMng()->application()->applicationInfo().commandLineArguments().parameters();
389 const ParameterCaseOption pco{ params.getParameterCaseOption(caseDocumentFragment()->language()) };
390
391 XmlNode parent_element = configList()->parentElement();
392
393 String full_xpath = String::format("{0}/{1}", parent_element.xpathFullName(), name());
394 // !!! En XML, on commence par 1 et non 0.
395 UniqueArray<Integer> option_in_param;
396 pco.indexesInParam(full_xpath, option_in_param, true);
397
398 Integer size = elem_list.size();
399
400 bool is_optional = configList()->isOptional();
401
402 if (size == 0 && option_in_param.empty() && is_optional) {
403 return;
404 }
405
406 Integer min_occurs = configList()->minOccurs();
407 Integer max_occurs = configList()->maxOccurs();
408
409 Integer max_in_param = 0;
410
411 // On regarde si l'utilisateur n'a pas mis un indice trop élevé pour l'option dans la ligne de commande.
412 if (!option_in_param.empty()) {
413 max_in_param = option_in_param[0];
414 for (Integer index : option_in_param) {
415 if (index > max_in_param)
416 max_in_param = index;
417 }
418 if (max_occurs >= 0) {
419 if (max_in_param > max_occurs) {
420 StringBuilder msg = "Bad number of occurences in command line (greater than max)";
421 msg += " index_max_in_param=";
422 msg += max_in_param;
423 msg += " max_occur=";
424 msg += max_occurs;
425 msg += " option=";
426 msg += full_xpath;
427 throw CaseOptionException(A_FUNCINFO, msg.toString(), true);
428 }
429 }
430 }
431
432 if (max_occurs >= 0) {
433 if (size > max_occurs) {
434 StringBuilder msg = "Bad number of occurences (greater than max)";
435 msg += " nb_occur=";
436 msg += size;
437 msg += " max_occur=";
438 msg += max_occurs;
439 msg += " option=";
440 msg += full_xpath;
441 throw CaseOptionException(A_FUNCINFO, msg.toString(), true);
442 }
443 }
444
445 // Il y aura toujours au moins min_occurs options.
446 // S'il n'y a pas assez l'options dans le jeu de données et dans les paramètres de la
447 // ligne de commande, on ajoute des services par défaut (si pas de défaut, il y aura un plantage).
448 Integer final_size = std::max(size, std::max(min_occurs, max_in_param));
449
450 ITraceMng* tm = traceMng();
451
452 IApplication* app = caseMng()->application();
453 ICaseDocumentFragment* doc = caseDocumentFragment();
454
455 m_container->allocate(final_size);
456
457 m_allocated_options.resize(final_size);
458 m_services_name.resize(final_size);
459
460 // D'abord, on aura les options du jeu de données : comme on ne peut pas définir un indice
461 // pour les options dans le jeu de données, elles seront forcément au début et seront contigües.
462 // Puis, s'il manque des options pour atteindre le min_occurs, on ajoute des options par défaut.
463 // S'il n'y a pas d'option par défaut, il y aura une exception.
464 // Enfin, l'utilisateur peut avoir ajouté des options à partir de la ligne de commande. On les ajoute alors.
465 // Si l'utilisateur souhaite modifier des valeurs du jeu de données à partir de la ligne de commande, on
466 // remplace les options au fur et à mesure de la lecture.
467 for (Integer index = 0; index < final_size; ++index) {
468 XmlNode element;
469
470 String mesh_name;
471 String str_val;
472
473 // Partie paramètres de la ligne de commande.
474 if (option_in_param.contains(index + 1)) {
475 mesh_name = pco.getParameterOrNull(full_xpath, "@mesh-name", index + 1);
476 str_val = pco.getParameterOrNull(full_xpath, "@name", index + 1);
477 }
478 // Partie jeu de données.
479 if (index < size && (mesh_name.null() || str_val.null())) {
480 element = elem_list[index];
481 if (!element.null()) {
482 if (mesh_name.null())
483 mesh_name = element.attrValue("mesh-name");
484 if (str_val.null())
485 str_val = element.attrValue("name");
486 }
487 }
488
489 // Valeur par défaut.
490 if (mesh_name.null()) {
491 mesh_name = meshName();
492 }
493 else {
494 // Dans un else : Le remplacement de symboles ne s'applique pas pour les valeurs par défault du .axl.
495 mesh_name = StringVariableReplace::replaceWithCmdLineArgs(params, mesh_name, true);
496 }
497
498 // Valeur par défaut.
499 if (str_val.null()) {
500 str_val = _defaultValue();
501 }
502 else {
503 // Dans un else : Le remplacement de symboles ne s'applique pas pour les valeurs par défault du .axl.
504 str_val = StringVariableReplace::replaceWithCmdLineArgs(params, str_val, true);
505 }
506
507 // Si l'on n'utilise pas les options du jeu de données, on doit créer de nouvelles options.
508 if (element.null()) {
509 element = parent_element.createElement(name());
510
511 element.setAttrValue("mesh-name", mesh_name);
512 element.setAttrValue("name", str_val);
513 }
514
515 tm->info(5) << "CaseOptionMultiServiceImpl name=" << name()
516 << " index=" << index
517 << " v=" << str_val
518 << " default_value='" << _defaultValue() << "'"
519 << " mesh=" << meshHandle().meshName();
520
521 // Maintenant, ce plantage concerne aussi le cas où il n'y a pas de valeurs par défaut et qu'il n'y a
522 // pas assez d'options pour atteindre le min_occurs.
523 if (str_val.null())
524 throw CaseOptionException("get_value", "@name");
525
526 // TODO: regarder si on ne peut pas créer directement un CaseOptionService.
527 auto* coptions = new CaseOptions(configList(), name(), parent_element, false, true);
528 if (coptions->_setMeshHandleAndCheckDisabled(mesh_name)) {
529 delete coptions;
530 continue;
531 }
532 coptions->configList()->_internalApi()->setRootElement(element);
533 bool is_found = _tryCreateService(m_container, app, str_val, index, coptions);
534
535 if (!is_found) {
536 tm->info(5) << "CaseOptionMultiServiceImpl name=" << name()
537 << " index=" << index
538 << " service not found";
539 delete coptions;
540 coptions = nullptr;
541 // Recherche les noms des implémentations valides
542 StringUniqueArray valid_names;
543 getAvailableNames(valid_names);
544 CaseOptionError::addError(doc, A_FUNCINFO, element.xpathFullName(),
545 String::format("Unable to find a service named '{0}' (valid values:{1})",
546 str_val, valid_names),
547 true);
548 }
549 m_services_name[index] = str_val;
550 m_allocated_options[index] = coptions;
551 }
552
553 if (m_notify_functor)
554 m_notify_functor->executeFunctor();
555}
556
557/*---------------------------------------------------------------------------*/
558/*---------------------------------------------------------------------------*/
559
561getAvailableNames(StringArray& names) const
562{
563 _getAvailableServiceNames(m_container,caseMng()->application(),names);
564}
565
566/*---------------------------------------------------------------------------*/
567/*---------------------------------------------------------------------------*/
568
569} // End namespace Arcane
570
571/*---------------------------------------------------------------------------*/
572/*---------------------------------------------------------------------------*/
573
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
const CommandLineArguments & commandLineArguments() const
Arguments de la ligne de commande.
static void addError(ICaseDocumentFragment *document, const TraceInfo &where, const String &node_name, const String &message, bool is_collective=false)
Erreur générique.
static void addOptionNotFoundError(ICaseDocumentFragment *document, const TraceInfo &where, const String &node_name, const XmlNode &parent)
Erreur lorsqu'une option du jeu de données n'est pas trouvée. Cette erreur est collective.
UniqueArray< String > m_services_name
Noms du service pour chaque occurence.
void setContainer(ICaseOptionServiceContainer *container)
Positionne le conteneur d'instances.
UniqueArray< ReferenceCounter< ICaseOptions > > m_allocated_options
Liste des options allouées qu'il faudra supprimer.
void visit(ICaseDocumentVisitor *visitor) const override
Applique le visiteur sur cette option.
void getAvailableNames(StringArray &names) const
Retourne dans names les noms d'implémentations valides pour ce service.
String meshName() const
Nom du maillage auquel le service est associé.
void setMeshName(const String &mesh_name)
Positionne le nom du maillage auquel le service sera associé.
void setContainer(ICaseOptionServiceContainer *container)
Positionne le conteneur d'instances.
void read(eCaseOptionReadPhase phase) override
Effectue la lecture de la phase read_phase des options.
void visit(ICaseDocumentVisitor *visitor) const override
Applique le visiteur sur cette option.
virtual void getAvailableNames(StringArray &names) const
Retourne dans names les noms d'implémentations valides pour ce service.
void setMeshName(const String &mesh_name)
Positionne le nom du maillage auquel le service sera associé.
String meshName() const
Nom du maillage auquel le service est associé.
bool _setMeshHandleAndCheckDisabled(const String &mesh_name)
Positionne le maillage associé à cette option.
void visit(ICaseDocumentVisitor *visitor) const override
Applique le visiteur sur cette option.
String rootTagName() const override
Retourne le nom de l'élément dans le langage du jeu de données.
virtual String name() const
Retourne le nom de l'élément dans le langage du jeu de données.
CaseOptions(ICaseMng *cm, const String &name)
Construit un jeu d'options.
EnumeratorT< Internal::IServiceFactory2 * > Enumerator
Definition Collection.h:129
const ParameterList & parameters() const
Liste des paramètres.
Interface de l'application.
virtual const ApplicationInfo & applicationInfo() const =0
Informations sur l'exécutable.
Interface du visiteur pour une option du jeu de données.
virtual IApplication * application()=0
Application associée.
virtual XmlNode rootElement() const =0
Retourne l'élément lié à cette liste d'option.
virtual bool isOptional() const =0
Indique si l'option est optionnelle.
virtual XmlNode parentElement() const =0
Retourne l'élément parent.
virtual bool isPresent() const =0
Indique si l'option est présente dans le jeu de données.
virtual ICaseOptionListInternal * _internalApi()=0
API interne à Arcane.
Interface d'un conteneur d'instances de service.
Interface des informations d'un service ou d'un module.
Interface du gestionnaire de traces.
virtual TraceMessage info()=0
Flot pour un message d'information.
Classe représentant l'ensemble des paramètres pouvant modifier les options du jeu de données.
Liste de paramètres.
ParameterCaseOption getParameterCaseOption(const String &language) const
Méthode permettant de récupérer un objet de type ParameterCaseOption.
Informations pour créer un service.
Chaîne de caractères unicode.
bool null() const
Retourne true si la chaîne est nulle.
Definition String.cc:304
Liste de noeuds d'un arbre DOM.
Definition XmlNodeList.h:33
Noeud d'un arbre DOM.
Definition XmlNode.h:51
String xpathFullName() const
Nom XPath du noeud avec ces ancêtres.
Definition XmlNode.cc:143
XmlNode child(const String &name) const
Noeud fils de celui-ci de nom name.
Definition XmlNode.cc:64
bool null() const
Vrai si le noeud est nul.
Definition XmlNode.h:294
String name() const
Nom du noeud.
Definition XmlNode.cc:132
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
Array< String > StringArray
Tableau dynamique à une dimension de chaînes de caractères.
Definition UtilsTypes.h:232
eCaseOptionReadPhase
Phases de la lecture.
Int32 Integer
Type représentant un entier.
@ ST_CaseOption
Le service s'utilise au niveau du jeu de données.
UniqueArray< String > StringUniqueArray
Tableau dynamique à une dimension de chaînes de caractères.
Definition UtilsTypes.h:446