Arcane  v4.1.2.0
Documentation développeur
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/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
46void
47_getAvailableServiceNames(ICaseOptionServiceContainer* container,IApplication* app,
48 StringArray& names)
49{
50 for( ServiceFactory2Collection::Enumerator i(app->serviceFactories2()); ++i; ){
52 IServiceInfo* si = sf2->serviceInfo();
53 // Il faut que le service soit autorisé pour le jeu de données.
54 if (!(si->usageType() & Arcane::ST_CaseOption))
55 continue;
56 if (container->hasInterfaceImplemented(sf2)){
57 names.add(sf2->serviceInfo()->localName());
58 }
59 }
60}
61
62bool
63_tryCreateService(ICaseOptionServiceContainer* container,IApplication* app,
64 const String& service_name,Integer index,ICaseOptions* opt)
65{
66 // Parcours la liste des fabriques et essaie de créer un service avec le nom
67 // qu'on souhaite et qui implémente la bonne interface.
68 bool is_found = false;
69 ServiceBuildInfoBase sbi(_arcaneDeprecatedGetSubDomain(opt),opt);
70 for( ServiceFactory2Collection::Enumerator i(app->serviceFactories2()); ++i; ){
72 IServiceInfo* si = sf2->serviceInfo();
73 if (si->localName()==service_name && container->tryCreateService(index,sf2,sbi)){
74 opt->setCaseServiceInfo(si);
75 is_found = true;
76 break;
77 }
78 }
79 return is_found;
80}
81
82}
83
84/*---------------------------------------------------------------------------*/
85/*---------------------------------------------------------------------------*/
86
88setMeshName(const String& mesh_name)
89{
90 m_impl->setMeshName(mesh_name);
91}
92
93/*---------------------------------------------------------------------------*/
94/*---------------------------------------------------------------------------*/
95
97meshName() const
98{
99 return m_impl->meshName();
100}
101
102/*---------------------------------------------------------------------------*/
103/*---------------------------------------------------------------------------*/
104
106setMeshName(const String& mesh_name)
107{
108 m_impl->setMeshName(mesh_name);
109}
110
111/*---------------------------------------------------------------------------*/
112/*---------------------------------------------------------------------------*/
113
115meshName() const
116{
117 return m_impl->meshName();
118}
119
120/*---------------------------------------------------------------------------*/
121/*---------------------------------------------------------------------------*/
122
123/*---------------------------------------------------------------------------*/
124/*---------------------------------------------------------------------------*/
125
126CaseOptionServiceImpl::
127CaseOptionServiceImpl(const CaseOptionBuildInfo& cob,bool allow_null,bool is_optional)
128: CaseOptions(cob.caseOptionList(),cob.name())
129, m_name(cob.name())
130, m_default_value(cob.defaultValue())
131, m_element(cob.element())
132, m_allow_null(allow_null)
133, m_is_optional(is_optional)
134, m_is_override_default(false)
135, m_container(nullptr)
136{
137}
138
139/*---------------------------------------------------------------------------*/
140/*---------------------------------------------------------------------------*/
141
142void CaseOptionServiceImpl::
143print(const String& lang,std::ostream& o) const
144{
145 ARCANE_UNUSED(lang);
146 o << serviceName();
147}
148
149/*---------------------------------------------------------------------------*/
150/*---------------------------------------------------------------------------*/
151
153visit(ICaseDocumentVisitor* visitor) const
154{
155 visitor->beginVisit(this);
156 CaseOptions::visit(visitor);
157 visitor->endVisit(this);
158}
159
160/*---------------------------------------------------------------------------*/
161/*---------------------------------------------------------------------------*/
162
165{
166 m_container = container;
167}
168
169/*---------------------------------------------------------------------------*/
170/*---------------------------------------------------------------------------*/
171
172void CaseOptionServiceImpl::
173_readPhase1()
174{
175 if (!m_container)
176 ARCANE_FATAL("null 'm_container'. did you called setContainer() method ?");
177
178 ITraceMng* tm = traceMng();
179
180 _setTranslatedName();
181 ICaseOptionList* col = configList();
182
184
185 if (child.null()) {
187 }
188 else {
189 if (col->rootElement() != child) // skip when rootElement already set to child (may appear in subDomain service)
190 col->_internalApi()->setRootElement(child);
191 }
192
193 XmlNode element = col->rootElement();
194 const ParameterListWithCaseOption& params = caseMng()->_internalImpl()->parameters();
195 ICaseDocumentFragment* doc = caseDocumentFragment();
196
197 const ParameterCaseOption pco{ params.getParameterCaseOption(doc->language()) };
198
199 String mesh_name;
200 {
201 String reference_input = pco.getParameterOrNull(element.xpathFullName(), "@mesh-name", 1);
202 if (!reference_input.null())
203 mesh_name = reference_input;
204 else
205 mesh_name = element.attrValue("mesh-name");
206 }
207
208 if (mesh_name.null()) {
209 mesh_name = meshName();
210 }
211 else {
212 // Dans un else : Le remplacement de symboles ne s'applique pas pour les valeurs par défault du .axl.
213 mesh_name = StringVariableReplace::replaceWithCmdLineArgs(params, mesh_name, true);
214 }
215
216 tm->info(5) << "** CaseOptionService::read() ELEMENT <" << rootTagName() << "> " << col->rootElement().name()
217 << " full=" << col->rootElement().xpathFullName()
218 << " is_present=" << col->isPresent()
219 << " is_optional=" << isOptional()
220 << " allow_null=" << m_allow_null
221 << " mesh-name=" << mesh_name
222 << "\n";
223
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 // Utilise la valeur par défaut :
241 // - si elle a été spécifiée par l'utilisateur, utilise celle-ci.
242 // - sinon utilise celle de la catégorie associée aux défauts.
243 // - sinon, la valeur par défaut classique.
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 // Dans un else : Le remplacement de symboles ne s'applique pas pour les valeurs par défault du .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 // Si le service peut-être nul et que l'élément n'est pas présent dans le jeu de données,
266 // on considère qu'il ne doit pas être chargé.
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 // Le service souhaité n'est pas trouvé. Il s'agit d'une erreur.
275 // Recherche les noms des implémentations valides pour affichage dans le message
276 // d'erreur correspondant.
277 StringUniqueArray valid_names;
278 getAvailableNames(valid_names);
279 CaseOptionError::addError(doc,A_FUNCINFO,element.xpathFullName(),
280 String::format("Unable to find a service named '{0}' (valid values:{1})",
281 str_val,valid_names),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 // !!! En XML, on commence par 1 et non 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 // On regarde si l'utilisateur n'a pas mis un indice trop élevé pour l'option dans la ligne de commande.
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 // Il y aura toujours au moins min_occurs options.
445 // S'il n'y a pas assez l'options dans le jeu de données et dans les paramètres de la
446 // ligne de commande, on ajoute des services par défaut (si pas de défaut, il y aura un plantage).
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 // D'abord, on aura les options du jeu de données : comme on ne peut pas définir un indice
460 // pour les options dans le jeu de données, elles seront forcément au début et seront contigües.
461 // Puis, s'il manque des options pour atteindre le min_occurs, on ajoute des options par défaut.
462 // S'il n'y a pas d'option par défaut, il y aura une exception.
463 // Enfin, l'utilisateur peut avoir ajouté des options à partir de la ligne de commande. On les ajoute alors.
464 // Si l'utilisateur souhaite modifier des valeurs du jeu de données à partir de la ligne de commande, on
465 // remplace les options au fur et à mesure de la lecture.
466 for (Integer index = 0; index < final_size; ++index) {
467 XmlNode element;
468
469 String mesh_name;
470 String str_val;
471
472 // Partie paramètres de la ligne de commande.
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 // Partie jeu de données.
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 // Valeur par défaut.
489 if (mesh_name.null()) {
490 mesh_name = meshName();
491 }
492 else {
493 // Dans un else : Le remplacement de symboles ne s'applique pas pour les valeurs par défault du .axl.
494 mesh_name = StringVariableReplace::replaceWithCmdLineArgs(params, mesh_name, true);
495 }
496
497 // Valeur par défaut.
498 if (str_val.null()) {
499 str_val = _defaultValue();
500 }
501 else {
502 // Dans un else : Le remplacement de symboles ne s'applique pas pour les valeurs par défault du .axl.
503 str_val = StringVariableReplace::replaceWithCmdLineArgs(params, str_val, true);
504 }
505
506 // Si l'on n'utilise pas les options du jeu de données, on doit créer de nouvelles 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 // Maintenant, ce plantage concerne aussi le cas où il n'y a pas de valeurs par défaut et qu'il n'y a
521 // pas assez d'options pour atteindre le min_occurs.
522 if (str_val.null())
523 throw CaseOptionException("get_value", "@name");
524
525 // TODO: regarder si on ne peut pas créer directement un CaseOptionService.
526 auto* coptions = new CaseOptions(configList(), name(), parent_element, false, true);
527 if (coptions->_setMeshHandleAndCheckDisabled(mesh_name)) {
528 delete coptions;
529 continue;
530 }
531 coptions->configList()->_internalApi()->setRootElement(element);
532 bool is_found = _tryCreateService(m_container, app, str_val, index, coptions);
533
534 if (!is_found) {
535 tm->info(5) << "CaseOptionMultiServiceImpl name=" << name()
536 << " index=" << index
537 << " service not found";
538 delete coptions;
539 coptions = nullptr;
540 // Recherche les noms des implémentations valides
541 StringUniqueArray valid_names;
542 getAvailableNames(valid_names);
543 CaseOptionError::addError(doc, A_FUNCINFO, element.xpathFullName(),
544 String::format("Unable to find a service named '{0}' (valid values:{1})",
545 str_val, valid_names),
546 true);
547 }
548 m_services_name[index] = str_val;
549 m_allocated_options[index] = coptions;
550 }
551
552 if (m_notify_functor)
553 m_notify_functor->executeFunctor();
554}
555
556/*---------------------------------------------------------------------------*/
557/*---------------------------------------------------------------------------*/
558
560getAvailableNames(StringArray& names) const
561{
562 _getAvailableServiceNames(m_container,caseMng()->application(),names);
563}
564
565/*---------------------------------------------------------------------------*/
566/*---------------------------------------------------------------------------*/
567
568} // End namespace Arcane
569
570/*---------------------------------------------------------------------------*/
571/*---------------------------------------------------------------------------*/
572
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Informations pour construire une option de jeu de données.
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.
XmlNode m_element
Element de l'option.
StringDictionary m_default_values
Liste des valeurs par défaut par catégorie.
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é.
Classe de base d'un tableau d'options complexes du jeu de données.
Classe de base d'une liste d'options du jeu de données.
Definition CaseOptions.h:57
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.
Interface de l'application.
Interface du visiteur pour une option du jeu de données.
virtual const ParameterListWithCaseOption & parameters() const =0
Liste des paramètres pouvant surcharger le jeu de données.
virtual ICaseMngInternal * _internalImpl()=0
Implémentation interne.
virtual void setRootElementWithParent(const XmlNode &parent_element)=0
Positionne l'élément racine de la liste, avec parent_element comme parent. Si déjà positionné,...
virtual void setRootElement(const XmlNode &root_element)=0
Positionne l'élément racine de la liste. Si déjà positionné, lance une exception.
Interface d'une liste d'options du jeu de données.
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 d'une liste d'options du jeu de données.
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 avec informations pour surcharger les options du jeu de données.
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:305
Liste de noeuds d'un arbre DOM.
Definition XmlNodeList.h:35
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:149
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:363