diff --git a/arcane/src/arcane/core/CaseOptionBase.cc b/arcane/src/arcane/core/CaseOptionBase.cc index de2ed7af4..157ee1edd 100644 --- a/arcane/src/arcane/core/CaseOptionBase.cc +++ b/arcane/src/arcane/core/CaseOptionBase.cc @@ -1,11 +1,11 @@ // -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- //----------------------------------------------------------------------------- -// Copyright 2000-2023 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: Apache-2.0 //----------------------------------------------------------------------------- /*---------------------------------------------------------------------------*/ -/* CaseOptionBase.cc (C) 2000-2023 */ +/* CaseOptionBase.cc (C) 2000-2025 */ /* */ /* Gestion des options du jeu de données. */ /*---------------------------------------------------------------------------*/ @@ -54,6 +54,7 @@ class CaseOptionBasePrivate String m_default_value; //!< Valeur par défaut Integer m_min_occurs; //!< Nombre minimum d'occurences Integer m_max_occurs; //!< Nombre maximum d'occurences (-1 == unbounded) + bool m_is_optional; bool m_is_initialized; //!< \a true si initialisé bool m_is_override_default; //!< \a true si la valeur par défaut est surchargée //! Liste des noms d'options par langue. @@ -77,6 +78,7 @@ CaseOptionBasePrivate(const CaseOptionBuildInfo& cob) , m_default_value(m_axl_default_value) , m_min_occurs(cob.minOccurs()) , m_max_occurs(cob.maxOccurs()) +, m_is_optional(cob.isOptional()) , m_is_initialized(false) , m_is_override_default(false) { @@ -227,6 +229,15 @@ maxOccurs() const /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ +bool CaseOptionBase:: +isOptional() const +{ + return m_p->m_is_optional; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + void CaseOptionBase:: _setTranslatedName() { @@ -349,6 +360,11 @@ _checkMinMaxOccurs(Integer nb_occur) { Integer min_occurs = m_p->m_min_occurs; Integer max_occurs = m_p->m_max_occurs; + bool is_optional = m_p->m_is_optional; + + if (nb_occur == 0 && is_optional) { + return; + } if (nb_occurapplication()->applicationInfo().commandLineArguments().parameters(); + const ParameterCaseOption pco{ params.getParameterCaseOption(caseDocumentFragment()->language()) }; + String full_xpath = String::format("{0}/{1}", rootElement().xpathFullName(), name()); + + // !!! En XML, on commence par 1 et non 0. + UniqueArray option_in_param; + pco.indexesInParam(full_xpath, option_in_param, false); + + XmlNodeList elem_list = rootElement().children(name()); Integer size = elem_list.size(); - _checkMinMaxOccurs(size); - if (size==0) + bool is_optional = isOptional(); + + if (size == 0 && option_in_param.empty() && is_optional) { return; + } + + Integer min_occurs = minOccurs(); + Integer max_occurs = maxOccurs(); + + Integer max_in_param = 0; + + // On regarde si l'utilisateur n'a pas mis un indice trop élevé pour l'option dans la ligne de commande. + if (!option_in_param.empty()) { + max_in_param = option_in_param[0]; + for (Integer index : option_in_param) { + if (index > max_in_param) + max_in_param = index; + } + if (max_occurs >= 0) { + if (max_in_param > max_occurs) { + StringBuilder msg = "Bad number of occurences in command line (greater than max)"; + msg += " index_max_in_param="; + msg += max_in_param; + msg += " max_occur="; + msg += max_occurs; + msg += " option="; + msg += full_xpath; + throw CaseOptionException(A_FUNCINFO, msg.toString(), true); + } + } + } - if (is_phase1){ - _allocate(size); - m_values.resize(size); + if (max_occurs >= 0) { + if (size > max_occurs) { + StringBuilder msg = "Bad number of occurences (greater than max)"; + msg += " nb_occur="; + msg += size; + msg += " max_occur="; + msg += max_occurs; + msg += " option="; + msg += full_xpath; + throw CaseOptionException(A_FUNCINFO, msg.toString(), true); + } + } + + // Il y aura toujours au moins min_occurs options. + // S'il n'y a pas assez l'options dans le jeu de données et dans les paramètres de la + // ligne de commande, on ajoute des services par défaut (si pas de défaut, il y aura un plantage). + Integer final_size = std::max(size, std::max(min_occurs, max_in_param)); + + if (is_phase1) { + _allocate(final_size); + m_values.resize(final_size); } - else{ - //cerr << "** MULTI SEARCH " << size << endl; - for( Integer i=0; imultiAllocate(m_root_element_list); + // Ces vérifications sont faites dans multiAllocate(). + //Integer s = m_root_element_list.size(); + //_checkMinMaxOccurs(s); + //if (s!=0) + m_case_option_multi->multiAllocate(m_root_element_list); // Récupère les options créées lors de l'appel à 'multiAllocate' et // les ajoute à la liste. Integer nb_children = m_case_option_multi->nbChildren(); diff --git a/arcane/src/arcane/core/CaseOptionService.cc b/arcane/src/arcane/core/CaseOptionService.cc index c0aeebf5d..5ecf70075 100644 --- a/arcane/src/arcane/core/CaseOptionService.cc +++ b/arcane/src/arcane/core/CaseOptionService.cc @@ -1,11 +1,11 @@ // -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- //----------------------------------------------------------------------------- -// Copyright 2000-2023 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: Apache-2.0 //----------------------------------------------------------------------------- /*---------------------------------------------------------------------------*/ -/* CaseOptions.cc (C) 2000-2023 */ +/* CaseOptionsService.cc (C) 2000-2025 */ /* */ /* Gestion des options du jeu de données. */ /*---------------------------------------------------------------------------*/ @@ -17,6 +17,9 @@ #include "arcane/utils/Enumerator.h" #include "arcane/utils/NotImplementedException.h" #include "arcane/utils/FatalErrorException.h" +#include "arcane/utils/ApplicationInfo.h" +#include "arcane/utils/CommandLineArguments.h" +#include "arcane/utils/StringBuilder.h" #include "arcane/core/IApplication.h" #include "arcane/core/IServiceFactory.h" @@ -28,6 +31,8 @@ #include "arcane/core/ICaseDocument.h" #include "arcane/core/ICaseMng.h" #include "arcane/core/internal/ICaseOptionListInternal.h" +#include "arcane/core/internal/StringVariableReplace.h" +#include "arcane/utils/ParameterCaseOption.h" #include @@ -187,7 +192,28 @@ _readPhase1() } XmlNode element = col->rootElement(); - String mesh_name = meshName(); + const ParameterList& params = caseMng()->application()->applicationInfo().commandLineArguments().parameters(); + ICaseDocumentFragment* doc = caseDocumentFragment(); + + const ParameterCaseOption pco{ params.getParameterCaseOption(doc->language()) }; + + String mesh_name; + { + String reference_input = pco.getParameterOrNull(element.xpathFullName(), "@mesh-name", 1); + if (!reference_input.null()) + mesh_name = reference_input; + else + mesh_name = element.attrValue("mesh-name"); + } + + if (mesh_name.null()) { + mesh_name = meshName(); + } + else { + // Dans un else : Le remplacement de symboles ne s'applique pas pour les valeurs par défault du .axl. + mesh_name = StringVariableReplace::replaceWithCmdLineArgs(params, mesh_name, true); + } + tm->info(5) << "** CaseOptionService::read() ELEMENT <" << rootTagName() << "> " << col->rootElement().name() << " full=" << col->rootElement().xpathFullName() << " is_present=" << col->isPresent() @@ -200,9 +226,15 @@ _readPhase1() if (_setMeshHandleAndCheckDisabled(mesh_name)) return; - ICaseDocumentFragment* doc = caseDocumentFragment(); + String str_val; + { + String reference_input = pco.getParameterOrNull(element.xpathFullName(), "@name", 1); + if (!reference_input.null()) + str_val = reference_input; + else + str_val = element.attrValue("name"); + } - String str_val = element.attrValue("name"); //cerr << "** STR_VAL <" << str_val << " - " << m_default_value << ">\n"; if (str_val.null()){ @@ -221,6 +253,10 @@ _readPhase1() } str_val = m_default_value; } + else { + // Dans un else : Le remplacement de symboles ne s'applique pas pour les valeurs par défault du .axl. + str_val = StringVariableReplace::replaceWithCmdLineArgs(params, str_val, true); + } if (str_val.null() && !isOptional()){ CaseOptionError::addOptionNotFoundError(doc,A_FUNCINFO,"@name",element); return; @@ -349,46 +385,154 @@ multiAllocate(const XmlNodeList& elem_list) if (!m_container) ARCANE_FATAL("null 'm_container'. did you called setContainer() method ?"); + const ParameterList& params = caseMng()->application()->applicationInfo().commandLineArguments().parameters(); + const ParameterCaseOption pco{ params.getParameterCaseOption(caseDocumentFragment()->language()) }; + + XmlNode parent_element = configList()->parentElement(); + + String full_xpath = String::format("{0}/{1}", parent_element.xpathFullName(), name()); + // !!! En XML, on commence par 1 et non 0. + UniqueArray option_in_param; + pco.indexesInParam(full_xpath, option_in_param, true); + Integer size = elem_list.size(); - if (size==0) + bool is_optional = configList()->isOptional(); + + if (size == 0 && option_in_param.empty() && is_optional) { return; + } + + Integer min_occurs = configList()->minOccurs(); + Integer max_occurs = configList()->maxOccurs(); - m_services_name.resize(size); + Integer max_in_param = 0; + + // On regarde si l'utilisateur n'a pas mis un indice trop élevé pour l'option dans la ligne de commande. + if (!option_in_param.empty()) { + max_in_param = option_in_param[0]; + for (Integer index : option_in_param) { + if (index > max_in_param) + max_in_param = index; + } + if (max_occurs >= 0) { + if (max_in_param > max_occurs) { + StringBuilder msg = "Bad number of occurences in command line (greater than max)"; + msg += " index_max_in_param="; + msg += max_in_param; + msg += " max_occur="; + msg += max_occurs; + msg += " option="; + msg += full_xpath; + throw CaseOptionException(A_FUNCINFO, msg.toString(), true); + } + } + } + + if (max_occurs >= 0) { + if (size > max_occurs) { + StringBuilder msg = "Bad number of occurences (greater than max)"; + msg += " nb_occur="; + msg += size; + msg += " max_occur="; + msg += max_occurs; + msg += " option="; + msg += full_xpath; + throw CaseOptionException(A_FUNCINFO, msg.toString(), true); + } + } + + // Il y aura toujours au moins min_occurs options. + // S'il n'y a pas assez l'options dans le jeu de données et dans les paramètres de la + // ligne de commande, on ajoute des services par défaut (si pas de défaut, il y aura un plantage). + Integer final_size = std::max(size, std::max(min_occurs, max_in_param)); ITraceMng* tm = traceMng(); - m_container->allocate(size); - m_allocated_options.resize(size); IApplication* app = caseMng()->application(); - XmlNode parent_element = configList()->parentElement(); ICaseDocumentFragment* doc = caseDocumentFragment(); - String mesh_name = meshName(); - if (_setMeshHandleAndCheckDisabled(mesh_name)) - return; + m_container->allocate(final_size); + + m_allocated_options.resize(final_size); + m_services_name.resize(final_size); + + // D'abord, on aura les options du jeu de données : comme on ne peut pas définir un indice + // pour les options dans le jeu de données, elles seront forcément au début et seront contigües. + // Puis, s'il manque des options pour atteindre le min_occurs, on ajoute des options par défaut. + // S'il n'y a pas d'option par défaut, il y aura une exception. + // Enfin, l'utilisateur peut avoir ajouté des options à partir de la ligne de commande. On les ajoute alors. + // Si l'utilisateur souhaite modifier des valeurs du jeu de données à partir de la ligne de commande, on + // remplace les options au fur et à mesure de la lecture. + for (Integer index = 0; index < final_size; ++index) { + XmlNode element; + + String mesh_name; + String str_val; + + // Partie paramètres de la ligne de commande. + if (option_in_param.contains(index + 1)) { + mesh_name = pco.getParameterOrNull(full_xpath, "@mesh-name", index + 1); + str_val = pco.getParameterOrNull(full_xpath, "@name", index + 1); + } + // Partie jeu de données. + if (index < size && (mesh_name.null() || str_val.null())) { + element = elem_list[index]; + if (!element.null()) { + if (mesh_name.null()) + mesh_name = element.attrValue("mesh-name"); + if (str_val.null()) + str_val = element.attrValue("name"); + } + } + + // Valeur par défaut. + if (mesh_name.null()) { + mesh_name = meshName(); + } + else { + // Dans un else : Le remplacement de symboles ne s'applique pas pour les valeurs par défault du .axl. + mesh_name = StringVariableReplace::replaceWithCmdLineArgs(params, mesh_name, true); + } + + // Valeur par défaut. + if (str_val.null()) { + str_val = _defaultValue(); + } + else { + // Dans un else : Le remplacement de symboles ne s'applique pas pour les valeurs par défault du .axl. + str_val = StringVariableReplace::replaceWithCmdLineArgs(params, str_val, true); + } + + // Si l'on n'utilise pas les options du jeu de données, on doit créer de nouvelles options. + if (element.null()) { + element = parent_element.createElement(name()); + + element.setAttrValue("mesh-name", mesh_name); + element.setAttrValue("name", str_val); + } - for( Integer index=0; indexinfo(5) << "CaseOptionMultiServiceImpl name=" << name() << " index=" << index << " v=" << str_val << " default_value='" << _defaultValue() << "'" << " mesh=" << meshHandle().meshName(); - - if (str_val.null()) - str_val = _defaultValue(); + + // Maintenant, ce plantage concerne aussi le cas où il n'y a pas de valeurs par défaut et qu'il n'y a + // pas assez d'options pour atteindre le min_occurs. if (str_val.null()) - throw CaseOptionException("get_value","@name",element); + throw CaseOptionException("get_value", "@name"); + // TODO: regarder si on ne peut pas créer directement un CaseOptionService. - CaseOptions* coptions = new CaseOptions(configList(),name(),parent_element,false,true); + auto* coptions = new CaseOptions(configList(), name(), parent_element, false, true); + if (coptions->_setMeshHandleAndCheckDisabled(mesh_name)) { + delete coptions; + continue; + } coptions->configList()->_internalApi()->setRootElement(element); - bool is_found = _tryCreateService(m_container,app,str_val,index,coptions); + bool is_found = _tryCreateService(m_container, app, str_val, index, coptions); - if (!is_found){ + if (!is_found) { tm->info(5) << "CaseOptionMultiServiceImpl name=" << name() << " index=" << index << " service not found"; @@ -397,12 +541,15 @@ multiAllocate(const XmlNodeList& elem_list) // Recherche les noms des implémentations valides StringUniqueArray valid_names; getAvailableNames(valid_names); - CaseOptionError::addError(doc,A_FUNCINFO,element.xpathFullName(), + CaseOptionError::addError(doc, A_FUNCINFO, element.xpathFullName(), String::format("Unable to find a service named '{0}' (valid values:{1})", - str_val,valid_names),true); + str_val, valid_names), + true); } + m_services_name[index] = str_val; m_allocated_options[index] = coptions; } + if (m_notify_functor) m_notify_functor->executeFunctor(); } diff --git a/arcane/src/arcane/core/CaseOptionSimple.cc b/arcane/src/arcane/core/CaseOptionSimple.cc index 20f98ce30..032d5d25a 100644 --- a/arcane/src/arcane/core/CaseOptionSimple.cc +++ b/arcane/src/arcane/core/CaseOptionSimple.cc @@ -1,13 +1,13 @@ // -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- //----------------------------------------------------------------------------- -// Copyright 2000-2022 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: Apache-2.0 //----------------------------------------------------------------------------- /*---------------------------------------------------------------------------*/ -/* CaseOptionEnum.cc (C) 2000-2023 */ +/* CaseOptionSimple.cc (C) 2000-2025 */ /* */ -/* Option du jeu de données de type énuméré. */ +/* Option du jeu de données de type simple . */ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ @@ -16,7 +16,12 @@ #include "arcane/utils/ValueConvert.h" #include "arcane/utils/ITraceMng.h" #include "arcane/utils/FatalErrorException.h" +#include "arcane/utils/ApplicationInfo.h" +#include "arcane/utils/CommandLineArguments.h" +#include "arcane/utils/ParameterCaseOption.h" +#include "arcane/utils/StringBuilder.h" +#include "arcane/core/IApplication.h" #include "arcane/core/CaseOptionException.h" #include "arcane/core/CaseOptionBuildInfo.h" #include "arcane/core/XmlNodeList.h" @@ -29,6 +34,7 @@ #include "arcane/core/IPhysicalUnitSystem.h" #include "arcane/core/IStandardFunction.h" #include "arcane/core/ICaseDocumentVisitor.h" +#include "arcane/core/internal/StringVariableReplace.h" /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ @@ -127,6 +133,25 @@ _search(bool is_phase1) } } + // Liste des options de la ligne de commande. + { + const ParameterList& params = caseMng()->application()->applicationInfo().commandLineArguments().parameters(); + const ParameterCaseOption pco{ params.getParameterCaseOption(doc->language()) }; + + String reference_input = pco.getParameterOrNull(String::format("{0}/{1}", rootElement().xpathFullName(), velem_name), 1, false); + if (!reference_input.null()) { + // Si l'utilisateur a spécifié une option qui n'est pas présente dans le + // jeu de données, on doit la créer. + if (velem.null()) { + velem = rootElement().createElement(name()); + } + velem.setValue(reference_input); + } + if (!velem.null()) { + velem.setValue(StringVariableReplace::replaceWithCmdLineArgs(params, velem.value(), true)); + } + } + m_element = velem; m_function = 0; @@ -630,43 +655,125 @@ _allowPhysicalUnit() * Si la valeur n'est pas présente dans le jeu de donnée, regarde s'il * existe une valeur par défaut et utilise cette dernière. */ -template void CaseOptionMultiSimpleT:: +template +void CaseOptionMultiSimpleT:: _search(bool is_phase1) { if (!is_phase1) return; - XmlNodeList elem_list = rootElement().children(name()); + const ParameterList& params = caseMng()->application()->applicationInfo().commandLineArguments().parameters(); + const ParameterCaseOption pco{ params.getParameterCaseOption(caseDocumentFragment()->language()) }; + + String full_xpath = String::format("{0}/{1}", rootElement().xpathFullName(), name()); + // !!! En XML, on commence par 1 et non 0. + UniqueArray option_in_param; + pco.indexesInParam(full_xpath, option_in_param, false); + + XmlNodeList elem_list = rootElement().children(name()); Integer asize = elem_list.size(); - _checkMinMaxOccurs(asize); - if (asize==0) + + bool is_optional = isOptional(); + + if (asize == 0 && option_in_param.empty() && is_optional) { return; + } + + Integer min_occurs = minOccurs(); + Integer max_occurs = maxOccurs(); + + Integer max_in_param = 0; + + if (!option_in_param.empty()) { + max_in_param = option_in_param[0]; + for (Integer index : option_in_param) { + if (index > max_in_param) + max_in_param = index; + } + if (max_occurs >= 0) { + if (max_in_param > max_occurs) { + StringBuilder msg = "Bad number of occurences in command line (greater than max)"; + msg += " index_max_in_param="; + msg += max_in_param; + msg += " max_occur="; + msg += max_occurs; + msg += " option="; + msg += full_xpath; + throw CaseOptionException(A_FUNCINFO, msg.toString(), true); + } + } + } + + if (max_occurs >= 0) { + if (asize > max_occurs) { + StringBuilder msg = "Bad number of occurences (greater than max)"; + msg += " nb_occur="; + msg += asize; + msg += " max_occur="; + msg += max_occurs; + msg += " option="; + msg += full_xpath; + throw CaseOptionException(A_FUNCINFO, msg.toString(), true); + } + } + // Il y aura toujours au moins min_occurs options. + // S'il n'y a pas assez l'options dans le jeu de données et dans les paramètres de la + // ligne de commande, on ajoute des services par défaut (si pas de défaut, il y aura un plantage). + Integer final_size = std::max(asize, std::max(min_occurs, max_in_param)); const Type* old_value = m_view.data(); delete[] old_value; using Type = typename CaseOptionTraitsT::ContainerType; - Type* ptr_value = new Type[asize]; - m_view = ArrayViewType(asize,ptr_value); - this->_setArray(ptr_value,asize); - - //cerr << "** MULTI SEARCH " << size << endl; - for( Integer i=0; i_setArray(ptr_value, final_size); + + // D'abord, on aura les options du jeu de données : comme on ne peut pas définir un indice + // pour les options dans le jeu de données, elles seront forcément au début et seront contigües. + // Puis, s'il manque des options pour atteindre le min_occurs, on ajoute des options par défaut. + // S'il n'y a pas d'option par défaut, il y aura une exception. + // Enfin, l'utilisateur peut avoir ajouté des options à partir de la ligne de commande. On les ajoute alors. + // Si l'utilisateur souhaite modifier des valeurs du jeu de données à partir de la ligne de commande, on + // remplace les options au fur et à mesure de la lecture. + for (Integer i = 0; i < final_size; ++i) { + String str_val; + + // Partie paramètres de la ligne de commande. + if (option_in_param.contains(i + 1)) { + str_val = pco.getParameterOrNull(full_xpath, i + 1, false); + } + + // Partie jeu de données. + else if (i < asize) { + XmlNode velem = elem_list[i]; + if (!velem.null()) { + str_val = velem.value(); + } + } + + // Valeur par défaut. + if (str_val.null()) { + str_val = _defaultValue(); + } + else { + // Dans un else : Le remplacement de symboles ne s'applique pas pour les valeurs par défault du .axl. + str_val = StringVariableReplace::replaceWithCmdLineArgs(params, str_val, true); + } + + // Maintenant, ce plantage concerne aussi le cas où il n'y a pas de valeurs par défaut et qu'il n'y a + // pas assez d'options pour atteindre le min_occurs. if (str_val.null()) - CaseOptionError::addOptionNotFoundError(caseDocumentFragment(),A_FUNCINFO, - name(),rootElement()); - Type val = Type(); + CaseOptionError::addOptionNotFoundError(caseDocumentFragment(), A_FUNCINFO, + name(), rootElement()); + Type val = Type(); str_val = StringCollapser::collapse(str_val); - bool is_bad = builtInGetValue(val,str_val); + bool is_bad = builtInGetValue(val, str_val); if (is_bad) - CaseOptionError::addInvalidTypeError(caseDocumentFragment(),A_FUNCINFO, - name(),rootElement(),str_val,typeToName(val)); + CaseOptionError::addInvalidTypeError(caseDocumentFragment(), A_FUNCINFO, + name(), rootElement(), str_val, typeToName(val)); //throw CaseOptionException("get_value",name(),rootElement(),str_val,typeToName(val)); //ptr_value[i] = val; - _copyCaseOptionValue(ptr_value[i],val); + _copyCaseOptionValue(ptr_value[i], val); } } diff --git a/arcane/src/arcane/core/CaseOptionSimple.h b/arcane/src/arcane/core/CaseOptionSimple.h index f8d05177b..8b1cdf6ef 100644 --- a/arcane/src/arcane/core/CaseOptionSimple.h +++ b/arcane/src/arcane/core/CaseOptionSimple.h @@ -1,11 +1,11 @@ // -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- //----------------------------------------------------------------------------- -// Copyright 2000-2023 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: Apache-2.0 //----------------------------------------------------------------------------- /*---------------------------------------------------------------------------*/ -/* CaseOptionSimple.h (C) 2000-2023 */ +/* CaseOptionSimple.h (C) 2000-2025 */ /* */ /* Option simple du jeu de données. */ /*---------------------------------------------------------------------------*/ @@ -385,6 +385,7 @@ class CaseOptionMultiSimpleT const T& value(Integer index) const { return this->operator[](index); } Integer size() const { return ArrayView::size(); } ARCANE_CORE_EXPORT void visit(ICaseDocumentVisitor* visitor) const override; + bool isPresent() const { return !m_view.empty(); } protected: diff --git a/arcane/src/arcane/core/CaseOptions.h b/arcane/src/arcane/core/CaseOptions.h index 4c033f5ff..99ea50021 100644 --- a/arcane/src/arcane/core/CaseOptions.h +++ b/arcane/src/arcane/core/CaseOptions.h @@ -1,11 +1,11 @@ // -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- //----------------------------------------------------------------------------- -// Copyright 2000-2023 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: Apache-2.0 //----------------------------------------------------------------------------- /*---------------------------------------------------------------------------*/ -/* CaseOptions.h (C) 2000-2023 */ +/* CaseOptions.h (C) 2000-2025 */ /* */ /* Options du jeu de données. */ /*---------------------------------------------------------------------------*/ @@ -186,6 +186,8 @@ class ARCANE_CORE_EXPORT CaseOptions protected: + friend class CaseOptionMultiServiceImpl; + void _setTranslatedName(); bool _setMeshHandleAndCheckDisabled(const String& mesh_name); diff --git a/arcane/src/arcane/core/ICaseOptionList.h b/arcane/src/arcane/core/ICaseOptionList.h index 61485808a..d514f46d6 100644 --- a/arcane/src/arcane/core/ICaseOptionList.h +++ b/arcane/src/arcane/core/ICaseOptionList.h @@ -1,11 +1,11 @@ // -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- //----------------------------------------------------------------------------- -// Copyright 2000-2023 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: Apache-2.0 //----------------------------------------------------------------------------- /*---------------------------------------------------------------------------*/ -/* ICaseOptionList.h (C) 2000-2023 */ +/* ICaseOptionList.h (C) 2000-2025 */ /* */ /* Options du jeu de données. */ /*---------------------------------------------------------------------------*/ @@ -72,6 +72,10 @@ class ARCANE_CORE_EXPORT ICaseOptionList virtual bool isPresent() const =0; //! Indique si l'option est optionnelle virtual bool isOptional() const =0; + //! Nombre minimum d'occurences + virtual Integer minOccurs() const = 0; + //! Nombre maximum d'occurences + virtual Integer maxOccurs() const = 0; //! Applique le visiteur \a visitor virtual void visit(ICaseDocumentVisitor* visitor) =0; //! Nom complet au format XPath correspondant à rootElement() diff --git a/arcane/src/arcane/core/internal/StringVariableReplace.cc b/arcane/src/arcane/core/internal/StringVariableReplace.cc index a354df49b..41b9af723 100644 --- a/arcane/src/arcane/core/internal/StringVariableReplace.cc +++ b/arcane/src/arcane/core/internal/StringVariableReplace.cc @@ -14,11 +14,15 @@ /* Exemple : @mon_symbole@ */ /*---------------------------------------------------------------------------*/ +#include "arcane/core/internal/StringVariableReplace.h" + +#include "arcane/utils/CommandLineArguments.h" #include "arcane/utils/PlatformUtils.h" #include "arcane/utils/SmallArray.h" #include "arcane/utils/StringBuilder.h" - -#include "arcane/core/internal/StringVariableReplace.h" +#include "arcane/utils/String.h" +#include "arcane/utils/FatalErrorException.h" +#include "arcane/utils/List.h" /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ @@ -29,6 +33,15 @@ namespace Arcane /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ +String StringVariableReplace:: +replaceWithCmdLineArgs(StringView string_with_symbols, bool fatal_if_not_found, bool fatal_if_invalid) +{ + StringList args; + platform::fillCommandLineArguments(args); + const CommandLineArguments cla{ args }; + return replaceWithCmdLineArgs(cla.parameters(), string_with_symbols, fatal_if_not_found, fatal_if_invalid); +} + /*! * \brief Méthode permettant de remplacer les symboles de la chaine de * caractères \a string_with_symbols par leurs valeurs définies dans la liste diff --git a/arcane/src/arcane/core/internal/StringVariableReplace.h b/arcane/src/arcane/core/internal/StringVariableReplace.h index 9afe8fb87..af0049612 100644 --- a/arcane/src/arcane/core/internal/StringVariableReplace.h +++ b/arcane/src/arcane/core/internal/StringVariableReplace.h @@ -22,8 +22,6 @@ #include "arcane/utils/ParameterList.h" -#include "arcane/core/VariableTypes.h" - /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ @@ -37,6 +35,7 @@ class ARCANE_CORE_EXPORT StringVariableReplace { public: + static String replaceWithCmdLineArgs(StringView string_with_symbols, bool fatal_if_not_found = false, bool fatal_if_invalid = true); static String replaceWithCmdLineArgs(const ParameterList& parameter_list, StringView string_with_symbols, bool fatal_if_not_found = false, bool fatal_if_invalid = true); private: diff --git a/arcane/src/arcane/impl/ArcaneCaseMeshService.cc b/arcane/src/arcane/impl/ArcaneCaseMeshService.cc index e0097393b..d08f22532 100644 --- a/arcane/src/arcane/impl/ArcaneCaseMeshService.cc +++ b/arcane/src/arcane/impl/ArcaneCaseMeshService.cc @@ -115,8 +115,8 @@ createMesh(const String& default_name) options()->configList()->xpathFullName(), options()->generator.rootTagName(), options()->filename.name()); - if (has_filename){ - m_mesh_file_name = StringVariableReplace::replaceWithCmdLineArgs(m_sub_domain->applicationInfo().commandLineArguments().parameters(), options()->filename().view()); + if (has_filename) { + m_mesh_file_name = options()->filename(); if (m_mesh_file_name.empty()) ARCANE_FATAL("Invalid filename '{0}' in option '{1}'", m_mesh_file_name,options()->filename.xpathFullName()); diff --git a/arcane/src/arcane/launcher/ArcaneLauncher.cc b/arcane/src/arcane/launcher/ArcaneLauncher.cc index e0a540f7e..7b69116be 100644 --- a/arcane/src/arcane/launcher/ArcaneLauncher.cc +++ b/arcane/src/arcane/launcher/ArcaneLauncher.cc @@ -1,11 +1,11 @@ // -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- //----------------------------------------------------------------------------- -// Copyright 2000-2024 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: Apache-2.0 //----------------------------------------------------------------------------- /*---------------------------------------------------------------------------*/ -/* ArcaneLauncher.cc (C) 2000-2024 */ +/* ArcaneLauncher.cc (C) 2000-2025 */ /* */ /* Classe gérant le lancement de l'exécution. */ /*---------------------------------------------------------------------------*/ @@ -15,6 +15,7 @@ #include "arcane/launcher/IDirectExecutionContext.h" #include "arcane/launcher/DirectSubDomainExecutionContext.h" +#include "arcane/launcher/GeneralHelp.h" #include "arcane/utils/Property.h" #include "arcane/utils/FatalErrorException.h" @@ -417,6 +418,28 @@ _notifyRemoveStandaloneSubDomain() /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ +bool ArcaneLauncher:: +needHelp() +{ + return applicationInfo().commandLineArguments().needHelp(); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +bool ArcaneLauncher:: +printHelp() +{ + if (applicationInfo().commandLineArguments().needHelp()) { + GeneralHelp::printHelp(); + return true; + } + return false; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + } // End namespace Arcane /*---------------------------------------------------------------------------*/ diff --git a/arcane/src/arcane/launcher/ArcaneLauncher.h b/arcane/src/arcane/launcher/ArcaneLauncher.h index 77f89489f..fd99a4869 100644 --- a/arcane/src/arcane/launcher/ArcaneLauncher.h +++ b/arcane/src/arcane/launcher/ArcaneLauncher.h @@ -1,11 +1,11 @@ // -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- //----------------------------------------------------------------------------- -// Copyright 2000-2024 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: Apache-2.0 //----------------------------------------------------------------------------- /*---------------------------------------------------------------------------*/ -/* ArcaneLauncher.h (C) 2000-2024 */ +/* ArcaneLauncher.h (C) 2000-2025 */ /* */ /* Classe gérant l'exécution. */ /*---------------------------------------------------------------------------*/ @@ -212,6 +212,26 @@ class ARCANE_LAUNCHER_EXPORT ArcaneLauncher */ static StandaloneSubDomain createStandaloneSubDomain(const String& case_file_name); + /*! + * \brief Demande d'aide avec l'option "--help" ou "-h". + * + * Méthode permettant de savoir si l'utilisateur a demandé l'aide + * avec l'option "--help" ou "-h". + * + * \return true si l'aide a été demandée. + */ + static bool needHelp(); + + /*! + * \brief Affichage de l'aide générique Arcane. + * + * Méthode permettant d'afficher l'aide générique Arcane si + * l'utilisateur l'a demandée avec l'option "--help" ou "-h". + * + * \return true si l'aide a été demandée. + */ + static bool printHelp(); + public: /*! diff --git a/arcane/src/arcane/launcher/GeneralHelp.cc b/arcane/src/arcane/launcher/GeneralHelp.cc new file mode 100644 index 000000000..191708118 --- /dev/null +++ b/arcane/src/arcane/launcher/GeneralHelp.cc @@ -0,0 +1,64 @@ +// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- +//----------------------------------------------------------------------------- +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: Apache-2.0 +//----------------------------------------------------------------------------- +/*---------------------------------------------------------------------------*/ +/* GeneralHelp.cc (C) 2000-2025 */ +/* */ +/* Classe gérant le message d'aide générique. */ +/*---------------------------------------------------------------------------*/ + +#include "arcane/launcher/GeneralHelp.h" + +#include "arcane/core/ApplicationBuildInfo.h" + +#include "arcane/impl/ArcaneMain.h" + +#include "arcane/utils/ApplicationInfo.h" +#include "arcane/utils/CommandLineArguments.h" + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +namespace Arcane +{ + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +void GeneralHelp:: +printHelp() +{ + ApplicationInfo& infos = ArcaneMain::defaultApplicationInfo(); + const CommandLineArguments& args = infos.commandLineArguments(); + + std::cout << infos.codeName() << " v" << infos.codeVersion() << std::endl; + std::cout << std::endl; + std::cout << "Usage:" << std::endl; + std::cout << " " << *args.commandLineArgv()[0] << " [OPTIONS] dataset.arc" << std::endl; + std::cout << std::endl; + std::cout << "General options:" << std::endl; + std::cout << " -h, --help Give this help list" << std::endl; + std::cout << std::endl; + std::cout << "Arcane option usage: -A,Option1=Value,Option2=Value" << std::endl; + std::cout << " and/or" << std::endl; + std::cout << " -A,Option1=Value -A,Option2=Value" << std::endl; + std::cout << std::endl; + std::cout << "Arcane options:" << std::endl; + std::cout << " -A,T= Nombre de tâches concurrentes à exécuter (default=1)" << std::endl; + std::cout << " -A,S= Nombre de sous-domaines en mémoire partagée" << std::endl; + std::cout << " -A,R= Nombre de sous-domaines répliqués (default=1)" << std::endl; + std::cout << " -A,P= Nombre de processus à utiliser pour les sous-domaines. Cette valeur est normalement calculée automatiquement en fonction des paramètres MPI. Elle n'est utile que si on souhaite utiliser moins de processus pour le partitionnement de domaine que ceux alloués pour le calcul." << std::endl; + std::cout << " -A,AcceleratorRuntime= Runtime accélérateur à utiliser. Les deux valeurs possibles sont cuda ou hip. Il faut avoir compiler Arcane avec le support des accélérateurs pour que cette option soit accessible. " << std::endl; + std::cout << " -A,MaxIteration= Nombre maximum d'itérations à effectuer pour l'exécution. Si le nombre d'itérations spécifié par cette variable est atteint, le calcul s'arrête." << std::endl; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +} // namespace Arcane + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ diff --git a/arcane/src/arcane/launcher/GeneralHelp.h b/arcane/src/arcane/launcher/GeneralHelp.h new file mode 100644 index 000000000..1c3763d62 --- /dev/null +++ b/arcane/src/arcane/launcher/GeneralHelp.h @@ -0,0 +1,42 @@ +// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- +//----------------------------------------------------------------------------- +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: Apache-2.0 +//----------------------------------------------------------------------------- +/*---------------------------------------------------------------------------*/ +/* GeneralHelp.h (C) 2000-2025 */ +/* */ +/* Classe gérant le message d'aide générique. */ +/*---------------------------------------------------------------------------*/ +#ifndef ARCANE_LAUNCHER_GENERALHELP_H +#define ARCANE_LAUNCHER_GENERALHELP_H +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +#include "arcane/launcher/LauncherGlobal.h" + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +namespace Arcane +{ + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +class ARCANE_LAUNCHER_EXPORT GeneralHelp +{ +public: + static void printHelp(); +}; + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +} // namespace Arcane + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +#endif diff --git a/arcane/src/arcane/launcher/srcs.cmake b/arcane/src/arcane/launcher/srcs.cmake index dfb0f8a6f..d23aaa6bf 100644 --- a/arcane/src/arcane/launcher/srcs.cmake +++ b/arcane/src/arcane/launcher/srcs.cmake @@ -6,6 +6,8 @@ set( ARCANE_SOURCES DirectExecutionContext.cc DirectSubDomainExecutionContext.h DirectSubDomainExecutionContext.cc + GeneralHelp.h + GeneralHelp.cc LauncherGlobal.h StandaloneAcceleratorMng.h StandaloneAcceleratorMng.cc diff --git a/arcane/src/arcane/tests/CMakeLists.txt b/arcane/src/arcane/tests/CMakeLists.txt index d48da2c90..4c88db426 100644 --- a/arcane/src/arcane/tests/CMakeLists.txt +++ b/arcane/src/arcane/tests/CMakeLists.txt @@ -331,6 +331,117 @@ endif() ARCANE_ADD_TEST_SEQUENTIAL(caseoptions testCaseOptions-1.arc) arcane_add_test_sequential(caseoptions_specificentrypoint testCaseOptions-specific-entry-point.arc "-We,ARCANE_CALL_SPECIFIC_ENTRY_POINT,CaseOptionLoop2") + +arcane_add_test_sequential(caseoptions_commandlinereplace testCaseOptions-commandLineReplace.arc + "-We,ARCANE_CALL_SPECIFIC_ENTRY_POINT,CaseOptionLoop2" + "-We,ARCANE_REPLACE_SYMBOLS_IN_DATASET,1" + "-A,TestId=1" + "-A,SimpleRealUnit2=0.0" + "-A,//case-options-tester/simple-real[]=3.0" + "-A,//case-options-tester/simple-real-unit=4.2" + "-A,SimpleRealUnit2=0.0" + "-A,//case-options-tester/simple-realarray-unit=\"2.2 2.3 0.1 3.5\"" + "-A,//case-options-tester/simple-real2=\"3.5 7.2\"" + "-A,SimpleReal3=\"1.2 4.7 7.9\"" + "-A,//case-options-tester/simple-real2x2=\"3.1 3.0 2.9 2.7\"" + "-A,//case-options-tester/simple-real3x3=\"3.3 3.2 2.1 1.1 0.3 0.5 7.2 7.1 4.0\"" + "-A,//case-options-tester/simple-integer=4" + "-A,//case-options-tester/simple-int32=-23" + "-A,//case-options-tester/simple-int64=454653457457455474" + "-A,//case-options-tester/simple-bool=true" + "-A,//case-options-tester/simple-string=toto" + "-A,SimpleStringMultiple=toto2" + "-A,//case-options-tester/simple-string-multiple[3]=toto3" + "-A,//case-options-tester/simple-string-multiple[4]=\"\"" + "-A,//case-options-tester/simple-real-array=\"3.0 4.1 5.6\"" + "-A,//case-options-tester/simple-real-array-multi[2]=\"4.0 1.1 7.3\"" + "-A,//case-options-tester/simple-integer-array=\"4 5 6 7\"" + "-A,//case-options-tester/simple-int32-array=\"-23 32 -32\"" + "-A,//case-options-tester/simple-int64-array=\"454653457457455474 -453463634634634634\"" + "-A,//case-options-tester/simple-bool-array=\"true false false true\"" + "-A,//case-options-tester/simple-string-array=\"toto titi tata tutu tete\"" + "-A,//case-options-tester/simple-enum=enum1" + "-A,PostProcessor1FrName=Ensight7PostProcessor" + "-A,//case-options-tester/post-processor1/fileset-size=1" + "-A,//case-options-tester/post-processor1[1]/binary-file=false" + "-A,//case-options-tester/post-processor1[3]/@name=VtkHdfV2PostProcessor" + "-A,//case-options-tester/post-processor1[4]/@name=UCDPostProcessor" + "-A,//case-options-tester/post-processor2[2]/@mesh-name=Mesh1" + "-A,//case-options-tester/complex1/simple-real-2=3" + "-A,//case-options-tester/complex1/simple-real-2-multi[2]=2.3" +) + +arcane_add_test_sequential(caseoptions_commandlinereplace_fail1 testCaseOptions-specific-entry-point.arc + "-We,ARCANE_CALL_SPECIFIC_ENTRY_POINT,CaseOptionLoop2" + "-We,ARCANE_REPLACE_SYMBOLS_IN_DATASET,1" + "-A,//case-options-tester/[1]=3.0" +) +set_property(TEST caseoptions_commandlinereplace_fail1 PROPERTY WILL_FAIL true) + +arcane_add_test_sequential(caseoptions_commandlinereplace_fail2 testCaseOptions-specific-entry-point.arc + "-We,ARCANE_CALL_SPECIFIC_ENTRY_POINT,CaseOptionLoop2" + "-We,ARCANE_REPLACE_SYMBOLS_IN_DATASET,1" + "-A,//case-options-tester/simple-real[aa]=3.0" +) +set_property(TEST caseoptions_commandlinereplace_fail2 PROPERTY WILL_FAIL true) + +arcane_add_test_sequential(caseoptions_commandlinereplace_fail3 testCaseOptions-specific-entry-point.arc + "-We,ARCANE_CALL_SPECIFIC_ENTRY_POINT,CaseOptionLoop2" + "-We,ARCANE_REPLACE_SYMBOLS_IN_DATASET,1" + "-A,//case-options-tester/simple-real[1]/=3.0" +) +set_property(TEST caseoptions_commandlinereplace_fail3 PROPERTY WILL_FAIL true) + +arcane_add_test_sequential(caseoptions_commandlinereplace_fail4 testCaseOptions-specific-entry-point.arc + "-We,ARCANE_CALL_SPECIFIC_ENTRY_POINT,CaseOptionLoop2" + "-We,ARCANE_REPLACE_SYMBOLS_IN_DATASET,1" + "-A,//case-options-tester/simple-real-array-multi[6]=\"3.0 4.0\"" +) +set_property(TEST caseoptions_commandlinereplace_fail4 PROPERTY WILL_FAIL true) + +arcane_add_test_sequential(caseoptions_commandlinereplace_fail5 testCaseOptions-specific-entry-point.arc + "-We,ARCANE_CALL_SPECIFIC_ENTRY_POINT,CaseOptionLoop2" + "-We,ARCANE_REPLACE_SYMBOLS_IN_DATASET,1" + "-A,//case-options-tester/simple-integer=aaa" +) +set_property(TEST caseoptions_commandlinereplace_fail5 PROPERTY WILL_FAIL true) + +arcane_add_test_sequential(caseoptions_commandlinereplace_fail6 testCaseOptions-specific-entry-point.arc + "-We,ARCANE_CALL_SPECIFIC_ENTRY_POINT,CaseOptionLoop2" + "-We,ARCANE_REPLACE_SYMBOLS_IN_DATASET,1" + "-A,//case-options-tester/post-processor1-fr[20]/@name=UCDPostProcessor" +) +set_property(TEST caseoptions_commandlinereplace_fail6 PROPERTY WILL_FAIL true) + +arcane_add_test_sequential(caseoptions_commandlinereplace_fail7 testCaseOptions-specific-entry-point.arc + "-We,ARCANE_CALL_SPECIFIC_ENTRY_POINT,CaseOptionLoop2" + "-We,ARCANE_REPLACE_SYMBOLS_IN_DATASET,1" + "-A,//case-options-tester/post-processor1-fr/binary-file-=false" +) +set_property(TEST caseoptions_commandlinereplace_fail7 PROPERTY WILL_FAIL true) + +arcane_add_test_sequential(caseoptions_commandlinereplace_fail8 testCaseOptions-specific-entry-point.arc + "-We,ARCANE_CALL_SPECIFIC_ENTRY_POINT,CaseOptionLoop2" + "-We,ARCANE_REPLACE_SYMBOLS_IN_DATASET,1" + "-A,//case-options-tester/post-processor1-fr/binary-file:=false" +) +set_property(TEST caseoptions_commandlinereplace_fail8 PROPERTY WILL_FAIL true) + +arcane_add_test_sequential(caseoptions_commandlinereplace_fail9 testCaseOptions-specific-entry-point.arc + "-We,ARCANE_CALL_SPECIFIC_ENTRY_POINT,CaseOptionLoop2" + "-We,ARCANE_REPLACE_SYMBOLS_IN_DATASET,1" + "-A,//case-options-tester/complex2[2]/complex3/extended-real-int-c3[9]=enum1" +) +set_property(TEST caseoptions_commandlinereplace_fail9 PROPERTY WILL_FAIL true) + +#arcane_add_test_sequential(caseoptions_commandlinereplace_fail99 testCaseOptions-specific-entry-point.arc +# "-We,ARCANE_CALL_SPECIFIC_ENTRY_POINT,CaseOptionLoop2" +# "-We,ARCANE_REPLACE_SYMBOLS_IN_DATASET,1" +# "-A,//case-options-tester/simple-real[=3.0" +#) +#set_property(TEST caseoptions_commandlinereplace_fail99 PROPERTY WILL_FAIL true) + + if(UDUNITS_FOUND) ARCANE_ADD_TEST_SEQUENTIAL(caseoptions_unit testCaseOptions-2.arc) endif() @@ -1118,7 +1229,16 @@ endif() ################################################################# # Test pour la partie StringVariableReplace. -arcane_add_test_sequential(string_variable_replace testStringVariableReplace.arc) +arcane_add_test_sequential(string_variable_replace testStringVariableReplace.arc + "-We,ARCANE_REPLACE_SYMBOLS_IN_DATASET,1" + "-A,aa=bb" +) + +################################################################# +################################################################# + +# Test pour la partie ParameterOption. +arcane_add_test_sequential(parameter_option testParameterOption.arc) ################################################################# ################################################################# diff --git a/arcane/src/arcane/tests/CaseOptionsTester.axl b/arcane/src/arcane/tests/CaseOptionsTester.axl index 0be2cb1f2..81a3a2735 100644 --- a/arcane/src/arcane/tests/CaseOptionsTester.axl +++ b/arcane/src/arcane/tests/CaseOptionsTester.axl @@ -130,7 +130,8 @@ SimpleStringMultiple @@ -391,7 +392,7 @@ name = "post-processor1" type = "Arcane::IPostProcessorWriter" minOccurs = "0" - maxOccurs = "unbounded" + maxOccurs="10" > post-processor1-fr Liste des services de protection @@ -621,7 +622,7 @@ name = "extended-real-int-c3" type = "ArcaneTest::TestRealInt" minOccurs = "0" - maxOccurs = "unbounded" + maxOccurs = "5" > ExtendedRealInt2 diff --git a/arcane/src/arcane/tests/ParameterOptionTest.axl b/arcane/src/arcane/tests/ParameterOptionTest.axl new file mode 100644 index 000000000..58bd3f47a --- /dev/null +++ b/arcane/src/arcane/tests/ParameterOptionTest.axl @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/arcane/src/arcane/tests/ParameterOptionTest.cc b/arcane/src/arcane/tests/ParameterOptionTest.cc new file mode 100644 index 000000000..659f104f1 --- /dev/null +++ b/arcane/src/arcane/tests/ParameterOptionTest.cc @@ -0,0 +1,464 @@ +// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- +//----------------------------------------------------------------------------- +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: Apache-2.0 +//----------------------------------------------------------------------------- +/*---------------------------------------------------------------------------*/ +/* ParameterOptionTest.cc (C) 2000-2025 */ +/* */ +/* Service de test de ParameterOption. */ +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +#include "arcane/utils/ParameterList.h" + +#include "arcane/core/BasicUnitTest.h" +#include "arcane/utils/internal/ParameterOption.h" + +#include "arcane/tests/ParameterOptionTest_axl.h" + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +namespace +{ +struct POption +{ + Arcane::String param; + Arcane::String value; +}; +} // namespace + +namespace ArcaneTest +{ + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +using namespace Arcane; + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +class ParameterOptionTest +: public ArcaneParameterOptionTestObject +{ + public: + + explicit ParameterOptionTest(const ServiceBuildInfo& sbi); + ~ParameterOptionTest(); + + public: + + void initializeTest() override; + void executeTest() override; + void _internalStructs(); + void _userStruct(); +}; + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +ARCANE_REGISTER_SERVICE_PARAMETEROPTIONTEST(ParameterOptionTest, ParameterOptionTest); + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +ParameterOptionTest:: +ParameterOptionTest(const ServiceBuildInfo& sbi) +: ArcaneParameterOptionTestObject(sbi) +{ +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +ParameterOptionTest:: +~ParameterOptionTest() += default; + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +void ParameterOptionTest:: +initializeTest() +{ + +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +void ParameterOptionTest:: +executeTest() +{ + _internalStructs(); + _userStruct(); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +void ParameterOptionTest:: +_internalStructs() +{ + ParameterOptionElementsCollection poec; + + POption option0{"module/option0", "0 0"}; + + const UniqueArray p_options{ + {"//module/option1", "5"}, + {"//module/option2", "aa"}, + {"//module/option2[2]", "bb"} + }; + + poec.addElement(option0.param, option0.value); + for (const auto& [param, value] : p_options) { + poec.addParameter(param, value); + } + + { + const String addr = "module/option1"; + const String value = poec.value(ParameterOptionAddr(addr.view())).value(); + if (value != "5") { + ARCANE_FATAL("_internalStructs -- Test 1"); + } + } + + { + const String addr = "noexist/option"; + if (poec.value(ParameterOptionAddr(addr.view())).has_value()) { + ARCANE_FATAL("_internalStructs -- Test 2"); + } + } + + { + const String addr = "module/option0"; + if (!poec.isExistAddr(ParameterOptionAddr(addr.view()))) { + ARCANE_FATAL("_internalStructs -- Test 3"); + } + } + + { + const String addr = "module/option2"; + ParameterOptionAddr addr_option(addr.view()); + + // En correspondance parfaite, il n'y en a qu'un seul. + if (poec.countAddr(addr_option) != 1) { + ARCANE_FATAL("_internalStructs -- Test 4.1"); + } + + addr_option.lastAddrPart()->setIndex(2); + + // Avec l'index 2, il n'y en a aussi qu'un seul. + if (poec.countAddr(addr_option) != 1) { + ARCANE_FATAL("_internalStructs -- Test 4.2"); + } + + addr_option.lastAddrPart()->setIndex(ParameterOptionAddrPart::ANY_INDEX); + + // Avec un ANY_INDEX à la fin, il y en a les deux. + if (poec.countAddr(addr_option) != 2) { + ARCANE_FATAL("_internalStructs -- Test 4.3"); + } + } + + { + const String addr = "module/option2"; + + ParameterOptionAddr addr_option(addr.view()); + addr_option.lastAddrPart()->setIndex(ParameterOptionAddrPart::GET_INDEX); + + UniqueArray index; + poec.getIndexInAddr(addr_option, index); + + if (index != UniqueArray{1, 2}) { + ARCANE_FATAL("_internalStructs -- Test 5"); + } + } + + { + const String addr1 = "test/option2"; + const String addr2 = "test/option2"; + + ParameterOptionAddr addr1_option(addr1.view()); + ParameterOptionAddr addr2_option(addr2.view()); + addr1_option.addAddrPart(new ParameterOptionAddrPart()); + if (addr1_option != addr2_option) { + ARCANE_FATAL("_internalStructs -- Test 6.1"); + } + if (addr2_option != addr1_option) { + ARCANE_FATAL("_internalStructs -- Test 6.2"); + } + } + + { + const String addr1 = "test/option2"; + const String addr2 = "test"; + + ParameterOptionAddr addr1_option(addr1.view()); + ParameterOptionAddr addr2_option(addr2.view()); + + if (addr1_option == addr2_option) { + ARCANE_FATAL("_internalStructs -- Test 7"); + } + } + + { + const String addr1 = "test/option2"; + const String addr2 = "test/option2[2]"; + + ParameterOptionAddr addr1_option(addr1.view()); + ParameterOptionAddr addr2_option(addr2.view()); + + if (addr1_option == addr2_option) { + ARCANE_FATAL("_internalStructs -- Test 8"); + } + } + + { + const String addr1 = "test/option2[3]/option[10]"; + const String addr2 = "test/option2/option"; + + ParameterOptionAddr addr1_option(addr1.view()); + ParameterOptionAddr addr2_option(addr2.view()); + + addr2_option.addrPart(0)->setIndex(ParameterOptionAddrPart::GET_INDEX); + addr2_option.addrPart(1)->setIndex(ParameterOptionAddrPart::GET_INDEX); + addr2_option.addrPart(2)->setIndex(ParameterOptionAddrPart::GET_INDEX); + + // On a trois GET_INDEX. + if (addr2_option.nbIndexToGetInAddr() != 3) { + ARCANE_FATAL("_internalStructs -- Test 9.1"); + } + + UniqueArray index(3); + UniqueArray result{1, 3, 10}; + bool ret = addr1_option.getIndexInAddr(addr2_option, index.view()); + + if (!ret) { + ARCANE_FATAL("_internalStructs -- Test 9.2"); + } + + if (index != result) { + ARCANE_FATAL("_internalStructs -- Test 9.3"); + } + } + + { + const String addr1 = "test/option2"; + const String addr2 = "test/option2"; + + ParameterOptionAddr addr1_option(addr1.view()); + ParameterOptionAddr addr2_option(addr2.view()); + + addr1_option.lastAddrPart()->setIndex(ParameterOptionAddrPart::ANY_INDEX); + addr2_option.lastAddrPart()->setIndex(ParameterOptionAddrPart::GET_INDEX); + + UniqueArray index(1); + bool ret = addr1_option.getIndexInAddr(addr2_option, index.view()); + + // Impossible de faire un GET_INDEX sur un ANY_INDEX. + if (ret) { + ARCANE_FATAL("_internalStructs -- Test 10"); + } + } + + { + const String addr1 = "test/option2"; + ParameterOptionAddr addr1_option(addr1.view()); + + const String option3 = "option3"; + addr1_option.lastAddrPart()->setTag(option3.view()); + + if (addr1_option.lastAddrPart()->tag() != option3.view()) { + ARCANE_FATAL("_internalStructs -- Test 11"); + } + } +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +void ParameterOptionTest:: +_userStruct() +{ + auto poec = new ParameterOptionElementsCollection(); + + const UniqueArray p_options{ + { "//module/option1", "5" }, + { "//module/option2", "aa" }, + { "//module/option2[2]", "bb" }, + { "//module/option3[]/@min", "3" }, + { "//module/option3", "4" }, + { "//module/option3[2]", "10" }, + { "//module//@mesh-name", "Mesh1" }, + { "//module/service1/@name", "SayHello" }, + { "//module/service1/option1", "2.0 3.0" }, + { "//module/service1/option2[1]", "123456789 987654321" }, + { "//module/service1/option2[2]", "-3 2" }, + { "//module/service1[2]/@name", "SayGoodbye" }, + { "//module/service1[2]/option1", "2.0 3.0" }, + { "//module/service1[2]/option2[1]", "123456789 987654321" }, + { "//module/service1[2]/option2[2]", "-3 2" } + }; + + for (const auto& [param, value] : p_options) { + poec->addParameter(param, value); + } + { + ParameterCaseOption pco(poec, "en"); + + { + const String xpath = "//case/module/option1"; + if (pco.count(xpath) != 1) { + ARCANE_FATAL("_userStruct -- Test 1"); + } + } + + { + const String xpath_before = "//case/module/service1"; + const String xpath_after = "@name"; + if (pco.count(xpath_before, xpath_after) != 2) { + ARCANE_FATAL("_userStruct -- Test 2"); + } + } + + { + const String xpath = "//case/module/service1"; + UniqueArray index; + + pco.indexesInParam(xpath, index, false); + + if (!index.empty()) { + ARCANE_FATAL("_userStruct -- Test 3.1"); + } + + index.clear(); + pco.indexesInParam(xpath, index, true); + + if (index != UniqueArray{ 1, 1, 1, 1, 2, 2, 2, 2 }) { + ARCANE_FATAL("_userStruct -- Test 3.2"); + } + } + + { + const String xpath_before = "//case/module/service1"; + const String xpath_after = "@name"; + UniqueArray index; + pco.indexesInParam(xpath_before, xpath_after, index); + + if (index != UniqueArray{ 1, 2 }) { + ARCANE_FATAL("_userStruct -- Test 4"); + } + } + + { + const String xpath = "//case/module/service1/option2"; + if (!pco.existAnyIndex(xpath)) { + ARCANE_FATAL("_userStruct -- Test 5"); + } + } + + { + const String xpath = "//case/module/service1/option3"; + if (pco.existAnyIndex(xpath)) { + ARCANE_FATAL("_userStruct -- Test 6"); + } + } + + { + const String xpath_before = "//case/module/service1"; + const String xpath_after = "option2"; + if (!pco.existAnyIndex(xpath_before, xpath_after)) { + ARCANE_FATAL("_userStruct -- Test 7"); + } + } + + { + const String xpath_before = "//case/module/service2"; + const String xpath_after = "option2"; + if (pco.existAnyIndex(xpath_before, xpath_after)) { + ARCANE_FATAL("_userStruct -- Test 8"); + } + } + + { + const String xpath = "//case/module/service1/option3"; + if (pco.existAnyIndex(xpath)) { + ARCANE_FATAL("_userStruct -- Test 9"); + } + } + + { + const String xpath = "//case/module/service1/option2[2]"; + if (!pco.exist(xpath)) { + ARCANE_FATAL("_userStruct -- Test 10"); + } + } + + { + const String xpath = "//case/module/service1/option2[3]"; + if (pco.exist(xpath)) { + ARCANE_FATAL("_userStruct -- Test 11"); + } + } + + { + const String xpath = "//case/module[1]/service1[1]/option2[1]"; + if (pco.getParameterOrNull(xpath) != "123456789 987654321") { + ARCANE_FATAL("_userStruct -- Test 12"); + } + } + + { + const String xpath = "//case/module[1]/service1[3]/option2[1]"; + if (pco.getParameterOrNull(xpath) != String()) { + ARCANE_FATAL("_userStruct -- Test 13"); + } + } + + { + const String xpath = "//case/module/option3"; + if (pco.getParameterOrNull(xpath, 1, false) != "4") { + ARCANE_FATAL("_userStruct -- Test 14"); + } + } + + { + const String xpath = "//case/module/option4"; + if (pco.getParameterOrNull(xpath, 1, false) != String()) { + ARCANE_FATAL("_userStruct -- Test 15"); + } + } + + { + const String xpath_before = "//case/module/option3"; + const String xpath_after = "@min"; + if (pco.getParameterOrNull(xpath_before, xpath_after, 1) != "3") { + ARCANE_FATAL("_userStruct -- Test 16.1"); + } + if (pco.getParameterOrNull(xpath_before, xpath_after, 2) != "3") { + ARCANE_FATAL("_userStruct -- Test 16.2"); + } + } + + { + const String xpath = "//case/module/service1[2]/@mesh-name"; + if (pco.getParameterOrNull(xpath) != "Mesh1") { + ARCANE_FATAL("_userStruct -- Test 17"); + } + } + } + + delete poec; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ diff --git a/arcane/src/arcane/tests/StringVariableReplaceTest.cc b/arcane/src/arcane/tests/StringVariableReplaceTest.cc index 4d61ee374..af5c9b9ad 100644 --- a/arcane/src/arcane/tests/StringVariableReplaceTest.cc +++ b/arcane/src/arcane/tests/StringVariableReplaceTest.cc @@ -12,6 +12,9 @@ /*---------------------------------------------------------------------------*/ #include "arcane/utils/ParameterList.h" +#include "arcane/utils/CommandLineArguments.h" +#include "arcane/utils/List.h" +#include "arcane/utils/PlatformUtils.h" #include "arcane/core/BasicUnitTest.h" #include "arcane/core/internal/StringVariableReplace.h" @@ -82,40 +85,42 @@ initializeTest() void StringVariableReplaceTest:: executeTest() { - ParameterList params; + StringList args; + platform::fillCommandLineArguments(args); + const CommandLineArguments cla{ args }; + + ParameterList params(cla.parameters()); String test("aaaa"); bool fatal = true; - String result = StringVariableReplace::replaceWithCmdLineArgs(params, test); + String result = StringVariableReplace::replaceWithCmdLineArgs(test); - if(result != test) { + if (result != test) { ARCANE_FATAL("Test 1 -- Expected: {0} -- Actual: {1}", test, result); } info() << "Test 1 OK"; /*---------------------------------------------------------------------------*/ - params.addParameterLine("ARCANE_REPLACE_SYMBOLS_IN_DATASET=1"); - - result = StringVariableReplace::replaceWithCmdLineArgs(params, ""); + result = StringVariableReplace::replaceWithCmdLineArgs(""); - if(result != "") { + if (result != "") { ARCANE_FATAL("Test 2 -- Expected: {0} -- Actual: {1}", "", result); } info() << "Test 2 OK"; /*---------------------------------------------------------------------------*/ - result = StringVariableReplace::replaceWithCmdLineArgs(params, test); + result = StringVariableReplace::replaceWithCmdLineArgs(test); - if(result != test) { + if (result != test) { ARCANE_FATAL("Test 3 -- Expected: {0} -- Actual: {1}", test, result); } info() << "Test 3 OK"; /*---------------------------------------------------------------------------*/ - params.addParameterLine("aa=bb"); + //params.addParameterLine("aa=bb"); // Dans le CMakeLists. /*---------------------------------------------------------------------------*/ @@ -123,7 +128,7 @@ executeTest() fatal = false; try { - StringVariableReplace::replaceWithCmdLineArgs(params, test); + StringVariableReplace::replaceWithCmdLineArgs(test); fatal = true; } catch (const FatalErrorException& e) { @@ -132,7 +137,7 @@ executeTest() ARCANE_FATAL("Test 4 no fatal test"); } - result = StringVariableReplace::replaceWithCmdLineArgs(params, test, false, false); + result = StringVariableReplace::replaceWithCmdLineArgs(test, false, false); String expected = "aaaa"; if (result != expected) { @@ -144,7 +149,7 @@ executeTest() test = "@aa@aa@aa@"; - result = StringVariableReplace::replaceWithCmdLineArgs(params, test); + result = StringVariableReplace::replaceWithCmdLineArgs(test); expected = "bbaabb"; if (result != expected) { @@ -156,7 +161,7 @@ executeTest() test = "@aa@@aa@"; - result = StringVariableReplace::replaceWithCmdLineArgs(params, test); + result = StringVariableReplace::replaceWithCmdLineArgs(test); expected = "bbbb"; if (result != expected) { @@ -170,7 +175,7 @@ executeTest() fatal = false; try { - StringVariableReplace::replaceWithCmdLineArgs(params, test); + StringVariableReplace::replaceWithCmdLineArgs(test); fatal = true; } catch (const FatalErrorException& e) { @@ -179,7 +184,7 @@ executeTest() ARCANE_FATAL("Test 7 no fatal test"); } - result = StringVariableReplace::replaceWithCmdLineArgs(params, test, false, false); + result = StringVariableReplace::replaceWithCmdLineArgs(test, false, false); expected = ""; if (result != expected) { diff --git a/arcane/src/arcane/tests/srcs.cmake b/arcane/src/arcane/tests/srcs.cmake index 50c01cdb8..3afa4e49a 100644 --- a/arcane/src/arcane/tests/srcs.cmake +++ b/arcane/src/arcane/tests/srcs.cmake @@ -80,6 +80,7 @@ set(ARCANE_SOURCES StringVariableReplaceTest.cc TimeHistoryAdderTestModule.cc MeshCriteriaLoadBalanceMngTestModule.cc + ParameterOptionTest.cc ) set(AXL_FILES @@ -132,5 +133,6 @@ set(AXL_FILES StringVariableReplaceTest TimeHistoryAdderTest MeshCriteriaLoadBalanceMngTest + ParameterOptionTest ) diff --git a/arcane/src/arcane/utils/CommandLineArguments.cc b/arcane/src/arcane/utils/CommandLineArguments.cc index 2be21846c..5a168308d 100644 --- a/arcane/src/arcane/utils/CommandLineArguments.cc +++ b/arcane/src/arcane/utils/CommandLineArguments.cc @@ -47,12 +47,22 @@ class CommandLineArguments::Impl }; public: Impl(int* argc,char*** argv) - : m_nb_ref(0), m_args(), m_argc(argc), m_argv(argv), m_need_destroy(false) + : m_nb_ref(0) + , m_args() + , m_argc(argc) + , m_argv(argv) + , m_need_destroy(false) + , m_need_help(false) { } Impl(const StringList& aargs) - : m_nb_ref(0), m_args(aargs), m_argc(nullptr), m_argv(nullptr), m_need_destroy(true) + : m_nb_ref(0) + , m_args(aargs) + , m_argc(nullptr) + , m_argv(nullptr) + , m_need_destroy(true) + , m_need_help(false) { Integer nb_arg = aargs.count(); m_argc_orig = new int; @@ -70,7 +80,12 @@ class CommandLineArguments::Impl } Impl() - : m_nb_ref(0), m_args(), m_argc(nullptr), m_argv(nullptr), m_need_destroy(true) + : m_nb_ref(0) + , m_args() + , m_argc(nullptr) + , m_argv(nullptr) + , m_need_destroy(true) + , m_need_help(false) { m_argc_orig = new int; m_argc = m_argc_orig; @@ -110,8 +125,17 @@ class CommandLineArguments::Impl // -A,x=b,y=c StringList args; command_line_args.fillArgs(args); - for( Integer i=0, n=args.count(); i m_nb_ref; StringList m_args; @@ -143,6 +172,7 @@ class CommandLineArguments::Impl char*** m_argv_orig = nullptr; char* m_argv0 = nullptr; bool m_need_destroy; + bool m_need_help; ParameterList m_parameter_list; }; @@ -273,6 +303,15 @@ parameters() const /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ +bool CommandLineArguments:: +needHelp() const +{ + return m_p->needHelp(); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + } // End namespace Arcane /*---------------------------------------------------------------------------*/ diff --git a/arcane/src/arcane/utils/CommandLineArguments.h b/arcane/src/arcane/utils/CommandLineArguments.h index 2c5bdf8a1..91fda1a71 100644 --- a/arcane/src/arcane/utils/CommandLineArguments.h +++ b/arcane/src/arcane/utils/CommandLineArguments.h @@ -94,6 +94,12 @@ class ARCANE_UTILS_EXPORT CommandLineArguments //! Liste des paramètres ParameterList& parameters(); + /*! + * \brief Méthode permettant de savoir si l'utilisateur a demandé + * de l'aide dans la ligne de commande. + */ + bool needHelp() const; + private: Arccore::ReferenceCounter m_p; diff --git a/arcane/src/arcane/utils/ParameterCaseOption.cc b/arcane/src/arcane/utils/ParameterCaseOption.cc new file mode 100644 index 000000000..357e013e6 --- /dev/null +++ b/arcane/src/arcane/utils/ParameterCaseOption.cc @@ -0,0 +1,205 @@ +// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- +//----------------------------------------------------------------------------- +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: Apache-2.0 +//----------------------------------------------------------------------------- +/*---------------------------------------------------------------------------*/ +/* ParameterCaseOption.cc (C) 2000-2025 */ +/* */ +/* Classe permettant d'interroger les paramètres pour savoir si des options */ +/* du jeu de données doivent être modifiées par ceux-ci. */ +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +#include "arcane/utils/ParameterCaseOption.h" + +#include "arcane/utils/ApplicationInfo.h" +#include "arcane/utils/ValueConvert.h" +#include "arcane/utils/Array.h" +#include "arcane/utils/FatalErrorException.h" +#include "arcane/utils/ITraceMng.h" +#include "arcane/utils/Ref.h" + +#include "arcane/utils/internal/ParameterOption.h" + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +namespace Arcane +{ + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +ParameterCaseOption:: +ParameterCaseOption(ParameterOptionElementsCollection* parameter_options, const String& lang) +: m_is_fr(lang == "fr") +, m_lines(parameter_options) // On ne récupère pas la propriété. +{} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +String ParameterCaseOption:: +getParameterOrNull(const String& xpath_before_index, const String& xpath_after_index, Integer index) const +{ + if (index <= 0) { + ARCANE_FATAL("Index in XML start at 1"); + } + + ParameterOptionAddr addr{ _removeUselessPartInXpath(xpath_before_index.view()) }; + addr.lastAddrPart()->setIndex(index); + addr.addAddrPart(new ParameterOptionAddrPart(xpath_after_index.view())); + + std::optional value = m_lines->value(addr); + if (value.has_value()) + return value.value(); + return {}; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +String ParameterCaseOption:: +getParameterOrNull(const String& xpath_before_index, Integer index, bool allow_elems_after_index) const +{ + if (index <= 0) { + ARCANE_FATAL("Index in XML start at 1"); + } + ParameterOptionAddr addr{ _removeUselessPartInXpath(xpath_before_index.view()) }; + addr.lastAddrPart()->setIndex(index); + if (allow_elems_after_index) { + addr.addAddrPart(new ParameterOptionAddrPart()); + } + + std::optional value = m_lines->value(addr); + if (value.has_value()) + return value.value(); + return {}; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +String ParameterCaseOption:: +getParameterOrNull(const String& full_xpath) const +{ + const ParameterOptionAddr addr{ _removeUselessPartInXpath(full_xpath.view()) }; + + std::optional value = m_lines->value(addr); + if (value.has_value()) + return value.value(); + return {}; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +bool ParameterCaseOption:: +exist(const String& full_xpath) const +{ + const ParameterOptionAddr addr{ _removeUselessPartInXpath(full_xpath.view()) }; + return m_lines->isExistAddr(addr); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +bool ParameterCaseOption:: +existAnyIndex(const String& xpath_before_index, const String& xpath_after_index) const +{ + ParameterOptionAddr addr{ _removeUselessPartInXpath(xpath_before_index.view()) }; + addr.lastAddrPart()->setIndex(ParameterOptionAddrPart::ANY_INDEX); + + addr.addAddrPart(new ParameterOptionAddrPart(xpath_after_index.view())); + + return m_lines->isExistAddr(addr); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +bool ParameterCaseOption:: +existAnyIndex(const String& full_xpath) const +{ + const ParameterOptionAddr addr{ _removeUselessPartInXpath(full_xpath.view()) }; + addr.lastAddrPart()->setIndex(ParameterOptionAddrPart::ANY_INDEX); + + return m_lines->isExistAddr(addr); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +void ParameterCaseOption:: +indexesInParam(const String& xpath_before_index, const String& xpath_after_index, UniqueArray& indexes) const +{ + ParameterOptionAddr addr{ _removeUselessPartInXpath(xpath_before_index.view()) }; + addr.lastAddrPart()->setIndex(ParameterOptionAddrPart::GET_INDEX); + addr.addAddrPart(new ParameterOptionAddrPart(xpath_after_index.view())); + + m_lines->getIndexInAddr(addr, indexes); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +void ParameterCaseOption:: +indexesInParam(const String& xpath_before_index, UniqueArray& indexes, bool allow_elems_after_index) const +{ + ParameterOptionAddr addr{ _removeUselessPartInXpath(xpath_before_index.view()) }; + addr.lastAddrPart()->setIndex(ParameterOptionAddrPart::GET_INDEX); + if (allow_elems_after_index) { + addr.addAddrPart(new ParameterOptionAddrPart()); + } + + m_lines->getIndexInAddr(addr, indexes); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +Integer ParameterCaseOption:: +count(const String& xpath_before_index, const String& xpath_after_index) const +{ + ParameterOptionAddr addr{ _removeUselessPartInXpath(xpath_before_index.view()) }; + addr.lastAddrPart()->setIndex(ParameterOptionAddrPart::ANY_INDEX); + addr.addAddrPart(new ParameterOptionAddrPart(xpath_after_index.view())); + + return m_lines->countAddr(addr); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +Integer ParameterCaseOption:: +count(const String& xpath_before_index) const +{ + const ParameterOptionAddr addr{ _removeUselessPartInXpath(xpath_before_index.view()) }; + addr.lastAddrPart()->setIndex(ParameterOptionAddrPart::ANY_INDEX); + + return m_lines->countAddr(addr); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +inline StringView ParameterCaseOption:: +_removeUselessPartInXpath(StringView xpath) const +{ + if (m_is_fr) + return xpath.subView(6); + return xpath.subView(7); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +} // End namespace Arcane + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ diff --git a/arcane/src/arcane/utils/ParameterCaseOption.h b/arcane/src/arcane/utils/ParameterCaseOption.h new file mode 100644 index 000000000..efe733cab --- /dev/null +++ b/arcane/src/arcane/utils/ParameterCaseOption.h @@ -0,0 +1,310 @@ +// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- +//----------------------------------------------------------------------------- +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: Apache-2.0 +//----------------------------------------------------------------------------- +/*---------------------------------------------------------------------------*/ +/* ParameterCaseOption.h (C) 2000-2025 */ +/* */ +/* Classe permettant d'interroger les paramètres pour savoir si des options */ +/* du jeu de données doivent être modifiées par ceux-ci. */ +/*---------------------------------------------------------------------------*/ + +#ifndef ARCANE_UTILS_PARAMETERCASEOPTION_H +#define ARCANE_UTILS_PARAMETERCASEOPTION_H + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +#include "arcane/utils/UtilsTypes.h" + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +namespace Arcane +{ + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +class ParameterOptionElementsCollection; + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +/*! + * \brief Classe représentant l'ensemble des paramètres pouvant modifier + * les options du jeu de données. + */ +class ARCANE_UTILS_EXPORT +ParameterCaseOption +{ + + public: + + ParameterCaseOption(ParameterOptionElementsCollection* parameter_options, const String& lang); + + public: + + /*! + * \brief Méthode permettant de récupérer la valeur d'une option. + * + * L'adresse de l'option est reformée comme ceci : + * xpath_before_index[index]/xpath_after_index + * + * xpath_before_index doit être de la forme suivante : + * //case/aaa/bbb[2]/ccc + * - le "//case/" au début (ou "//cas/" en français"), + * - une succession de tags avec possiblement leurs indices, + * - pas de "/" à la fin, + * - un indice peut être mise à la fin (mais il sera remplacé + * par celui passé en paramètre). + * + * xpath_after_index doit être de la forme suivante : + * ddd/eee + * - pas de "/" au début ni à la fin. + * + * Les indices sont des indices XML et ces indices commencent par 1. + * + * \param xpath_before_index L'adresse avant indice. + * \param xpath_after_index L'adresse après indice. + * \param index L'indice à mettre entre les deux parties de l'adresse. + * \return La valeur si trouvée, sinon chaîne null. + */ + String getParameterOrNull(const String& xpath_before_index, const String& xpath_after_index, Integer index) const; + + /*! + * \brief Méthode permettant de récupérer la valeur d'une option. + * + * L'adresse de l'option est reformée comme ceci : + * xpath_before_index[index] + * + * xpath_before_index doit être de la forme suivante : + * //case/aaa/bbb[2]/ccc + * - le "//case/" au début (ou "//cas/" en français"), + * - une succession de tags avec possiblement leurs indices, + * - pas de "/" à la fin, + * - un indice peut être mise à la fin (mais il sera remplacé + * par celui passé en paramètre). + * + * Si le paramètre allow_elems_after_index est activé, les adresses de la forme : + * xpath_before_index[index]/aaa/bbb + * seront aussi recherchées. + * + * Les indices sont des indices XML et ces indices commencent par 1. + * + * \param xpath_before_index L'adresse avant indice. + * \param index L'indice à mettre après l'adresse. + * \param allow_elems_after_index Doit-on vérifier la présence d'éléments après l'indice ? + * \return La valeur si trouvée, sinon chaîne null. + */ + String getParameterOrNull(const String& xpath_before_index, Integer index, bool allow_elems_after_index) const; + + /*! + * \brief Méthode permettant de récupérer la valeur d'une option. + * + * L'adresse doit être de la forme suivante : + * //case/aaa/bbb[2]/ccc + * - le "//case/" au début (ou "//cas/" en français"), + * - une succession de tags avec possiblement leurs indices, + * - pas de "/" à la fin, + * - un indice peut être mise à la fin. + * + * Les indices sont des indices XML et ces indices commencent par 1. + * + * \param full_xpath L'adresse à rechercher. + * \return La valeur si trouvée, sinon chaîne null. + */ + String getParameterOrNull(const String& full_xpath) const; + + /*! + * \brief Méthode permettant de savoir si une option est présente. + * + * L'adresse doit être de la forme suivante : + * //case/aaa/bbb[2]/ccc + * - le "//case/" au début (ou "//cas/" en français"), + * - une succession de tags avec possiblement leurs indices, + * - pas de "/" à la fin, + * - un indice peut être mise à la fin. + * + * Les indices sont des indices XML et ces indices commencent par 1. + * + * \param full_xpath L'adresse à rechercher. + * \return true si l'adresse est trouvée dans la liste. + */ + bool exist(const String& full_xpath) const; + + /*! + * \brief Méthode permettant de savoir si une option est présente. + * + * L'adresse de l'option est reformée comme ceci : + * xpath_before_index[ANY_INDEX]/xpath_after_index + * + * xpath_before_index doit être de la forme suivante : + * //case/aaa/bbb[2]/ccc + * - le "//case/" au début (ou "//cas/" en français"), + * - une succession de tags avec possiblement leurs indices, + * - pas de "/" à la fin, + * - un indice peut être mise à la fin (mais il sera remplacé + * par ANY_INDEX). + * + * xpath_after_index doit être de la forme suivante : + * ddd/eee + * - pas de "/" au début ni à la fin. + * + * Les indices sont des indices XML et ces indices commencent par 1. + * L'indice ANY_INDEX est un indice spécial désignant tous les indices. + * + * \param xpath_before_index L'adresse avant indice. + * \param xpath_after_index L'adresse après indice. + * \return true si l'adresse est trouvée dans la liste. + */ + bool existAnyIndex(const String& xpath_before_index, const String& xpath_after_index) const; + + /*! + * \brief Méthode permettant de savoir si une option est présente. + * + * L'adresse de l'option est reformée comme ceci : + * full_xpath[ANY_INDEX] + * + * L'adresse doit être de la forme suivante : + * //case/aaa/bbb[2]/ccc + * - le "//case/" au début (ou "//cas/" en français"), + * - une succession de tags avec possiblement leurs indices, + * - pas de "/" à la fin, + * - un indice peut être mise à la fin (mais il sera remplacé + * par ANY_INDEX). + * + * Les indices sont des indices XML et ces indices commencent par 1. + * L'indice ANY_INDEX est un indice spécial désignant tous les indices. + * + * \param full_xpath L'adresse à rechercher. + * \return true si l'adresse est trouvée dans la liste. + */ + bool existAnyIndex(const String& full_xpath) const; + + /*! + * \brief Méthode permettant de récupérer le ou les indices de l'option. + * + * L'adresse de l'option est reformée comme ceci : + * xpath_before_index[GET_INDEX]/xpath_after_index + * + * xpath_before_index doit être de la forme suivante : + * //case/aaa/bbb[2]/ccc + * - le "//case/" au début (ou "//cas/" en français"), + * - une succession de tags avec possiblement leurs indices, + * - pas de "/" à la fin, + * - un indice peut être mise à la fin (mais il sera remplacé + * par GET_INDEX). + * + * xpath_after_index doit être de la forme suivante : + * ddd/eee + * - pas de "/" au début ni à la fin. + * + * Les indices sont des indices XML et ces indices commencent par 1. + * L'indice GET_INDEX est un indice spécial désignant les indices que l'on souhaite récupérer. + * + * \param xpath_before_index L'adresse avant indice. + * \param xpath_after_index L'adresse après indice. + * \param indexes Le tableau qui contiendra l'ensemble des indices trouvés + * (ce tableau n'est pas effacé avant utilisation). + */ + void indexesInParam(const String& xpath_before_index, const String& xpath_after_index, UniqueArray& indexes) const; + + /*! + * \brief Méthode permettant de récupérer le ou les indices de l'option. + * + * L'adresse de l'option est reformée comme ceci : + * xpath_before_index[GET_INDEX] + * + * xpath_before_index doit être de la forme suivante : + * //case/aaa/bbb[2]/ccc + * - le "//case/" au début (ou "//cas/" en français"), + * - une succession de tags avec possiblement leurs indices, + * - pas de "/" à la fin, + * - un indice peut être mise à la fin (mais il sera remplacé + * par GET_INDEX). + * + * Si le paramètre allow_elems_after_index est activé, les adresses de la forme : + * xpath_before_index[GET_INDEX]/aaa/bbb + * seront aussi recherchées. + * + * Les indices sont des indices XML et ces indices commencent par 1. + * L'indice GET_INDEX est un indice spécial désignant les indices que l'on souhaite récupérer. + * + * \param xpath_before_index L'adresse avant indice. + * \param indexes Le tableau qui contiendra l'ensemble des indices trouvés + * \param allow_elems_after_index Doit-on vérifier la présence d'éléments après l'indice ? + * (ce tableau n'est pas effacé avant utilisation). + */ + void indexesInParam(const String& xpath_before_index, UniqueArray& indexes, bool allow_elems_after_index) const; + + /*! + * \brief Méthode permettant de connaitre le nombre d'indices de l'option. + * + * L'adresse de l'option est reformée comme ceci : + * xpath_before_index[GET_INDEX]/xpath_after_index + * + * xpath_before_index doit être de la forme suivante : + * //case/aaa/bbb[2]/ccc + * - le "//case/" au début (ou "//cas/" en français"), + * - une succession de tags avec possiblement leurs indices, + * - pas de "/" à la fin, + * - un indice peut être mise à la fin (mais il sera remplacé + * par GET_INDEX). + * + * xpath_after_index doit être de la forme suivante : + * ddd/eee + * - pas de "/" au début ni à la fin. + * + * Les indices sont des indices XML et ces indices commencent par 1. + * L'indice GET_INDEX est un indice spécial désignant les indices que l'on souhaite récupérer. + * + * \param xpath_before_index L'adresse avant indice. + * \param xpath_after_index L'adresse après indice. + * \return Le nombre d'indices de l'option. + */ + Integer count(const String& xpath_before_index, const String& xpath_after_index) const; + + /*! + * \brief Méthode permettant de connaitre le nombre d'indices de l'option. + * + * L'adresse de l'option est reformée comme ceci : + * xpath_before_index[GET_INDEX] + * + * xpath_before_index doit être de la forme suivante : + * //case/aaa/bbb[2]/ccc + * - le "//case/" au début (ou "//cas/" en français"), + * - une succession de tags avec possiblement leurs indices, + * - pas de "/" à la fin, + * - un indice peut être mise à la fin (mais il sera remplacé + * par GET_INDEX). + * + * Les indices sont des indices XML et ces indices commencent par 1. + * L'indice GET_INDEX est un indice spécial désignant les indices que l'on souhaite récupérer. + * + * \param xpath_before_index L'adresse avant indice. + * \return Le nombre d'indices de l'option. + */ + Integer count(const String& xpath_before_index) const; + + private: + + inline StringView _removeUselessPartInXpath(StringView xpath) const; + + private: + + bool m_is_fr; + ParameterOptionElementsCollection* m_lines; +}; + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +} // End namespace Arcane + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +#endif diff --git a/arcane/src/arcane/utils/ParameterList.cc b/arcane/src/arcane/utils/ParameterList.cc index 03adedbce..d60d8ed11 100644 --- a/arcane/src/arcane/utils/ParameterList.cc +++ b/arcane/src/arcane/utils/ParameterList.cc @@ -1,11 +1,11 @@ // -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- //----------------------------------------------------------------------------- -// Copyright 2000-2022 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: Apache-2.0 //----------------------------------------------------------------------------- /*---------------------------------------------------------------------------*/ -/* ParameterList.cc (C) 2000-2020 */ +/* ParameterList.cc (C) 2000-2025 */ /* */ /* Liste de paramêtres. */ /*---------------------------------------------------------------------------*/ @@ -15,6 +15,10 @@ #include "arcane/utils/StringDictionary.h" #include "arcane/utils/String.h" #include "arcane/utils/Array.h" +#include "arcane/utils/FatalErrorException.h" +#include "arcane/utils/Ref.h" + +#include "arcane/utils/internal/ParameterOption.h" #include @@ -40,10 +44,19 @@ class ParameterList::Impl } }; public: - Impl() {} + + Impl() + : m_parameter_option(makeRef(new ParameterOptionElementsCollection())) + {} + public: String getParameter(const String& key) { + if (key.startsWith("//")) { + if (const auto value = m_parameter_option->value(ParameterOptionAddr(key.view().subView(2)))) + return value.value(); + return {}; + } String x = m_parameters_dictionary.find(key); return x; } @@ -53,14 +66,27 @@ class ParameterList::Impl //std::cout << "__ADD_PARAMETER name='" << name << "' v='" << value << "'\n"; if (name.empty()) return; - m_parameters_dictionary.add(name,value); - m_parameters_list.add({name,value}); + + if (name.startsWith("//")) { + m_parameters_option_list.add({ name, value }); + m_parameter_option->addParameter(m_parameters_option_list[m_parameters_option_list.size() - 1].name, m_parameters_option_list[m_parameters_option_list.size() - 1].value); + return; + } + + m_parameters_dictionary.add(name, value); + m_parameters_list.add({ name, value }); + m_parameter_option->addParameter(m_parameters_list[m_parameters_list.size() - 1].name, m_parameters_list[m_parameters_list.size() - 1].value); } void setParameter(const String& name,const String& value) { //std::cout << "__SET_PARAMETER name='" << name << "' v='" << value << "'\n"; if (name.empty()) return; + + if (name.startsWith("//")) { + ARCANE_FATAL("Set parameter not supported for ParameterOptions."); + } + m_parameters_dictionary.add(name,value); // Supprime de la liste toutes les occurences ayant // pour paramètre \a name @@ -73,6 +99,9 @@ class ParameterList::Impl //std::cout << "__REMOVE_PARAMETER name='" << name << "' v='" << value << "'\n"; if (name.empty()) return; + if (name.startsWith("//")) { + ARCANE_FATAL("Remove parameter not supported for ParameterOptions."); + } // Si le paramètre \a name avec la valeur \a value est trouvé, le supprime. // Dans ce cas, il faudra regarder s'il y a toujours // dans \a m_parameters_list un paramètre \a name et si c'est le @@ -93,8 +122,17 @@ class ParameterList::Impl } void fillParameters(StringList& param_names,StringList& values) const { - m_parameters_dictionary.fill(param_names,values); + m_parameters_dictionary.fill(param_names, values); + for (const auto& [name, value] : m_parameters_option_list) { + param_names.add(name); + values.add(value); + } + } + ParameterOptionElementsCollection* getParameterOption() const + { + return m_parameter_option.get(); } + private: void _fillDictionaryWithValueInList(const String& name) { @@ -105,6 +143,8 @@ class ParameterList::Impl private: StringDictionary m_parameters_dictionary; UniqueArray m_parameters_list; + UniqueArray m_parameters_option_list; + Ref m_parameter_option; }; /*---------------------------------------------------------------------------*/ @@ -186,6 +226,15 @@ fillParameters(StringList& param_names,StringList& values) const /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ +ParameterCaseOption ParameterList:: +getParameterCaseOption(const String& language) const +{ + return { m_p->getParameterOption(), language }; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + } // End namespace Arcane /*---------------------------------------------------------------------------*/ diff --git a/arcane/src/arcane/utils/ParameterList.h b/arcane/src/arcane/utils/ParameterList.h index a7c675358..801586dca 100644 --- a/arcane/src/arcane/utils/ParameterList.h +++ b/arcane/src/arcane/utils/ParameterList.h @@ -1,11 +1,11 @@ // -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- //----------------------------------------------------------------------------- -// Copyright 2000-2022 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: Apache-2.0 //----------------------------------------------------------------------------- /*---------------------------------------------------------------------------*/ -/* ParameterList.h (C) 2000-2020 */ +/* ParameterList.h (C) 2000-2025 */ /* */ /* Liste de paramètres. */ /*---------------------------------------------------------------------------*/ @@ -15,6 +15,7 @@ /*---------------------------------------------------------------------------*/ #include "arcane/utils/UtilsTypes.h" +#include "arcane/utils/ParameterCaseOption.h" /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ @@ -87,6 +88,16 @@ class ARCANE_UTILS_EXPORT ParameterList */ void fillParameters(StringList& param_names,StringList& values) const; + /*! + * \brief Méthode permettant de récupérer un objet de type ParameterCaseOption. + * + * Cet objet peut être détruit après utilisation. + * + * \param language Le langage dans lequel est écrit le jeu de données. + * \return Un objet de type ParameterCaseOption. + */ + ParameterCaseOption getParameterCaseOption(const String& language) const; + private: Impl* m_p; //!< Implémentation diff --git a/arcane/src/arcane/utils/internal/ParameterOption.cc b/arcane/src/arcane/utils/internal/ParameterOption.cc new file mode 100644 index 000000000..9a64eeaab --- /dev/null +++ b/arcane/src/arcane/utils/internal/ParameterOption.cc @@ -0,0 +1,494 @@ +// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- +//----------------------------------------------------------------------------- +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: Apache-2.0 +//----------------------------------------------------------------------------- +/*---------------------------------------------------------------------------*/ +/* ParameterOption.cc (C) 2000-2025 */ +/* */ +/* Classe représentant l'ensemble des paramètres pouvant modifier les */ +/* options du jeu de données. */ +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +#include "arcane/utils/internal/ParameterOption.h" + +#include "arcane/utils/ApplicationInfo.h" +#include "arcane/utils/ValueConvert.h" +#include "arcane/utils/Array.h" +#include "arcane/utils/FatalErrorException.h" +#include "arcane/utils/ITraceMng.h" +#include "arcane/utils/Ref.h" + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +namespace Arcane +{ + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +ParameterOptionAddrPart:: +ParameterOptionAddrPart() +: m_tag(ANY_TAG) +, m_index(ANY_INDEX) +{} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +ParameterOptionAddrPart:: +ParameterOptionAddrPart(const StringView tag) +: m_tag(tag) +, m_index(1) +{ + ARCANE_ASSERT(tag != ANY_TAG, ("ANY_TAG without ANY_INDEX is forbidden")); + ARCANE_ASSERT(!tag.empty(), ("tag is empty")); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +ParameterOptionAddrPart:: +ParameterOptionAddrPart(const StringView tag, const Integer index) +: m_tag(tag) +, m_index(index) +{ + ARCANE_ASSERT(index == ANY_INDEX || tag != ANY_TAG, ("ANY_TAG without ANY_INDEX is forbidden")); + ARCANE_ASSERT(!tag.empty(), ("tag is empty")); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +StringView ParameterOptionAddrPart:: +tag() const +{ + return m_tag; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +Integer ParameterOptionAddrPart:: +index() const +{ + return m_index; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +void ParameterOptionAddrPart:: +setTag(const StringView tag) +{ + ARCANE_ASSERT(m_index == ANY_INDEX || tag != ANY_TAG, ("ANY_TAG without ANY_INDEX is forbidden")); + ARCANE_ASSERT(!tag.empty(), ("tag is empty")); + + m_tag = tag; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +void ParameterOptionAddrPart:: +setIndex(const Integer index) +{ + m_index = index; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +bool ParameterOptionAddrPart:: +isAny() const +{ + return (m_tag == ANY_TAG && m_index == ANY_INDEX); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +bool ParameterOptionAddrPart:: +operator==(const ParameterOptionAddrPart& other) const +{ + return (m_tag == other.m_tag || m_tag == ANY_TAG || other.m_tag == ANY_TAG) && + (m_index == other.m_index || m_index == ANY_INDEX || other.m_index == ANY_INDEX || m_index == GET_INDEX || other.m_index == GET_INDEX); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +bool ParameterOptionAddrPart:: +operator!=(const ParameterOptionAddrPart& other) const +{ + return !operator==(other); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +std::ostream& operator<<(std::ostream& o, const ParameterOptionAddrPart& h) +{ + o << (h.tag() == ParameterOptionAddrPart::ANY_TAG ? "ANY" : h.tag()) + << "[" << (h.index() == ParameterOptionAddrPart::ANY_INDEX ? "ANY" : (h.index() == ParameterOptionAddrPart::GET_INDEX ? "GET" : std::to_string(h.index()))) + << "]"; + return o; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +ParameterOptionAddr:: +ParameterOptionAddr(const StringView addr_str_view) +{ + Span span_line(addr_str_view.bytes()); + Integer begin = 0; + Integer size = 0; + Integer index_begin = -1; + // On interdit les options qui s'appliquent à toutes les caseoptions. + bool have_a_no_any = false; + + // aaa[0] + for (Integer i = 0; i < span_line.size(); ++i) { + if (span_line[i] == '[') { + index_begin = i + 1; + size = i - begin; + if (size == 0) { + const StringView current = addr_str_view.subView(0, i + 1); + ARCANE_FATAL("Invalid parameter option (empty tag) -- Current read : {0}", current); + } + if (index_begin >= span_line.size()) { + const StringView current = addr_str_view.subView(0, i + 1); + ARCANE_FATAL("Invalid parameter option (']' not found) -- Current read : {0}", current); + } + } + else if (span_line[i] == ']') { + if (index_begin == -1) { + const StringView current = addr_str_view.subView(0, i + 1); + ARCANE_FATAL("Invalid parameter option (']' found without '[' before) -- Current read : {0}", current); + } + + // Motif spécial "[]" (= ANY_INDEX) + if (index_begin == i) { + m_parts.add(makeRef(new ParameterOptionAddrPart(addr_str_view.subView(begin, size), ParameterOptionAddrPart::ANY_INDEX))); + have_a_no_any = true; + } + else { + StringView index_str = addr_str_view.subView(index_begin, i - index_begin); + Integer index; + bool is_bad = builtInGetValue(index, index_str); + if (is_bad) { + const StringView current = addr_str_view.subView(0, i + 1); + ARCANE_FATAL("Invalid index in parameter option -- Current read : {0}", current); + } + m_parts.add(makeRef(new ParameterOptionAddrPart(addr_str_view.subView(begin, size), index))); + have_a_no_any = true; + } + } + + else if (span_line[i] == '/') { + if (i + 1 == span_line.size()) { + const StringView current = addr_str_view.subView(0, i + 1); + ARCANE_FATAL("Invalid parameter option ('/' found at the end of the param option) -- Current read : {0}", current); + } + + if (index_begin == -1) { + size = i - begin; + // Cas ou on a un any_tag any_index ("truc1//truc2"). + if (size == 0) { + m_parts.add(makeRef(new ParameterOptionAddrPart())); + } + else { + m_parts.add(makeRef(new ParameterOptionAddrPart(addr_str_view.subView(begin, size)))); + have_a_no_any = true; + } + } + + begin = i + 1; + size = 0; + index_begin = -1; + } + } + if (index_begin == -1) { + size = static_cast(span_line.size()) - begin; + if (size == 0) { + const StringView current = addr_str_view.subView(0, size); + ARCANE_FATAL("Invalid parameter option (empty tag) -- Current read : {0}", current); + } + + m_parts.add(makeRef(new ParameterOptionAddrPart(addr_str_view.subView(begin, size)))); + have_a_no_any = true; + } + if (!have_a_no_any) { + ARCANE_FATAL("Invalid option"); + } +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +// On ne doit pas bloquer les multiples ParameterOptionAddrPart(ANY) : +// Construction par iteration : aaaa/bb/ANY/ANY/cc +void ParameterOptionAddr:: +addAddrPart(ParameterOptionAddrPart* part) +{ + m_parts.add(makeRef(part)); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +ParameterOptionAddrPart* ParameterOptionAddr:: +addrPart(const Integer index_of_part) const +{ + if (index_of_part >= m_parts.size()) { + if (m_parts[m_parts.size() - 1]->isAny()) { + return lastAddrPart(); + } + ARCANE_FATAL("Invalid index"); + } + return m_parts[index_of_part].get(); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +ParameterOptionAddrPart* ParameterOptionAddr:: +lastAddrPart() const +{ + return m_parts[m_parts.size() - 1].get(); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +Integer ParameterOptionAddr:: +nbAddrPart() const +{ + return m_parts.size(); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +bool ParameterOptionAddr:: +getIndexInAddr(const ParameterOptionAddr& addr_with_get_index, ArrayView indexes) const +{ + if (!operator==(addr_with_get_index)) + return false; + + ARCANE_ASSERT(indexes.size() == addr_with_get_index.nbIndexToGetInAddr(), ("ArrayView too small")); + + Integer index = 0; + for (Integer i = 0; i < addr_with_get_index.nbAddrPart(); ++i) { + if (addr_with_get_index.addrPart(i)->index() == ParameterOptionAddrPart::GET_INDEX) { + Integer index_tag = addrPart(i)->index(); + if (index_tag == ParameterOptionAddrPart::ANY_INDEX) + return false; + indexes[index++] = index_tag; + } + } + return true; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +Integer ParameterOptionAddr:: +nbIndexToGetInAddr() const +{ + Integer count = 0; + for (const auto& elem : m_parts) { + if (elem->index() == ParameterOptionAddrPart::GET_INDEX) { + count++; + } + } + return count; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +bool ParameterOptionAddr:: +operator==(const ParameterOptionAddr& other) const +{ + Integer nb_iter = 0; + if (lastAddrPart()->isAny()) { + nb_iter = nbAddrPart() - 1; + } + else if (other.lastAddrPart()->isAny()) { + nb_iter = other.nbAddrPart() - 1; + } + else if (nbAddrPart() != other.nbAddrPart()) { + return false; + } + else { + nb_iter = nbAddrPart(); + } + + for (Integer i = 0; i < nb_iter; ++i) { + if (*addrPart(i) != *other.addrPart(i)) { + return false; + } + } + return true; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +bool ParameterOptionAddr:: +operator!=(const ParameterOptionAddr& other) const +{ + return !operator==(other); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +std::ostream& operator<<(std::ostream& o, const ParameterOptionAddr& h) +{ + Integer nb_part = h.nbAddrPart(); + if (nb_part != 0) + o << *(h.addrPart(0)); + for (Integer i = 1; i < nb_part; ++i) { + o << "/" << *(h.addrPart(i)); + } + return o; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +ParameterOptionElement:: +ParameterOptionElement(const StringView addr, const StringView value) +: m_addr(addr) +, m_value(value) +{} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +ParameterOptionAddr ParameterOptionElement:: +addr() const +{ + return m_addr; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +StringView ParameterOptionElement:: +value() const +{ + return m_value; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +bool ParameterOptionElement:: +operator==(const ParameterOptionAddr& addr) const +{ + return m_addr == addr; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +void ParameterOptionElementsCollection:: +addParameter(const String& parameter, const String& value) +{ + if (parameter.startsWith("//")) { + addElement(parameter.view().subView(2), value.view()); + } +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +void ParameterOptionElementsCollection:: +addElement(StringView addr, StringView value) +{ + m_elements.add({ addr, value }); +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +// Un StringView "vide" est éqal à un StringView "nul". +// Comme on travaille avec des String et que la distinction +// vide/nul est importante, on passe par un std::optional. +std::optional ParameterOptionElementsCollection:: +value(const ParameterOptionAddr& addr) +{ + for (const auto& elem : m_elements) { + if (elem == addr) + return elem.value(); + } + return {}; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +bool ParameterOptionElementsCollection:: +isExistAddr(const ParameterOptionAddr& addr) +{ + for (const auto& elem : m_elements) { + if (elem == addr) + return true; + } + return false; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +Integer ParameterOptionElementsCollection:: +countAddr(const ParameterOptionAddr& addr) +{ + Integer count = 0; + for (const auto& elem : m_elements) { + if (elem == addr) + count++; + } + return count; +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +void ParameterOptionElementsCollection:: +getIndexInAddr(const ParameterOptionAddr& addr_with_get_index, UniqueArray& indexes) +{ + UniqueArray new_indexes(addr_with_get_index.nbIndexToGetInAddr()); + for (const auto& elem : m_elements) { + if (elem.addr().getIndexInAddr(addr_with_get_index, new_indexes)) { + indexes.addRange(new_indexes); + } + } +} + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +} // End namespace Arcane + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ diff --git a/arcane/src/arcane/utils/internal/ParameterOption.h b/arcane/src/arcane/utils/internal/ParameterOption.h new file mode 100644 index 000000000..24d538c7f --- /dev/null +++ b/arcane/src/arcane/utils/internal/ParameterOption.h @@ -0,0 +1,337 @@ +// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*- +//----------------------------------------------------------------------------- +// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com) +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: Apache-2.0 +//----------------------------------------------------------------------------- +/*---------------------------------------------------------------------------*/ +/* ParameterOption.h (C) 2000-2025 */ +/* */ +/* Classe représentant l'ensemble des paramètres pouvant modifier les */ +/* options du jeu de données. */ +/*---------------------------------------------------------------------------*/ + +#ifndef ARCANE_UTILS_INTERNAL_PARAMETEROPTION_H +#define ARCANE_UTILS_INTERNAL_PARAMETEROPTION_H + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +#include "arcane/utils/UtilsTypes.h" +#include "arcane/utils/String.h" +#include "arcane/utils/List.h" + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +namespace Arcane +{ + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +/*! + * \brief Classe représentant une partie d'une adresse d'option du jeu de données. + * À noter qu'en XML, l'index commence à 1 et non à 0. + * + * Un tag spécial nommé ANY_TAG représente n'importe quel tag. + * Deux index spéciaux sont aussi disponibles : + * - ANY_INDEX : Représente n'importe quel index, + * - GET_INDEX : Représente un index à récupérer (voir la classe ParameterOptionAddr). + * Ces élements sont utiles pour l'opérateur ==. + * À noter que ANY_TAG ne peut pas être définit sans ANY_INDEX. + * Aussi, le tag ne peut pas être vide. + */ +class ARCANE_UTILS_EXPORT +ParameterOptionAddrPart +{ + public: + static constexpr const char* ANY_TAG = "/"; + static constexpr Integer ANY_INDEX = -1; + static constexpr Integer GET_INDEX = -2; + + public: + + /*! + * \brief Constructeur. Définit le tag en ANY_TAG et l'index en ANY_INDEX. + */ + ParameterOptionAddrPart(); + + /*! + * \brief Constructeur. Définit l'index à 1. + * \param tag Le tag de cette partie d'adresse. Ce tag ne peut pas être ANY_TAG. + */ + explicit ParameterOptionAddrPart(const StringView tag); + + /*! + * \brief Constructeur. + * \param tag Le tag de cette partie d'adresse. Ce tag ne peut pas être ANY_TAG + * si l'index n'est pas ANY_INDEX. + * \param index L'index de cette partie d'adresse. + */ + ParameterOptionAddrPart(const StringView tag, const Integer index); + + public: + + StringView tag() const; + Integer index() const; + + //! Si l'index est ANY_INDEX, le tag ne peut pas être ANY_TAG. + //! Attention à la durée de vie de tag. + void setTag(const StringView tag); + void setIndex(const Integer index); + + //! isAny si ANY_TAG et ANY_INDEX. + bool isAny() const; + + /*! + * \brief Opérateur d'égalité. + * Le tag ANY_TAG est égal à tous les tags. + * L'index ANY_INDEX est égal à tous les index. + * L'index GET_INDEX est égal à tous les index. + */ + bool operator==(const ParameterOptionAddrPart& other) const; + // TODO AH : À supprimer lors du passage en C++20. + bool operator!=(const ParameterOptionAddrPart& other) const; + + private: + + StringView m_tag; + Integer m_index; +}; + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +/*! + * \brief Classe représentant une adresse d'option du jeu de données. + * Cette adresse doit être de la forme : "tag/tag[index]/tag" + * Les parties de l'adresse sans index auront l'index par défaut (=1). + * + * Cette adresse doit obéir à certaines règles : + * - elle ne doit pas être vide, + * - elle ne doit pas représenter l'ensemble des options ("/"), + * - ses tags peuvent être vides ssi l'index est vide (voir après), + * - l'index spécial ANY_INDEX ne peut être présent que si le tag est non vide, + * - l'adresse peut terminer par un attribut ("\@name"), + * - l'adresse donnée au constructeur ne peut pas terminer par un ANY_TAG (mais + * ANY_TAG peut être ajouté après avec la méthode addAddrPart()), + * + * Dans une chaine de caractères : + * - le motif ANY_TAG[ANY_INDEX] peut être défini avec "//" : + * -> "tag/tag//tag" sera convertie ainsi : "tag[1]/tag[1]/ANY_TAG[ANY_INDEX]/tag[1]". + * - l'index ANY_INDEX peut être défini avec un index vide "[]" : + * -> "tag/tag[]/\@attr" sera convertie ainsi : "tag[1]/tag[ANY_INDEX]/\@attr[1]", + * -> le motif "tag/[]/tag" est interdit. + */ +class ARCANE_UTILS_EXPORT +ParameterOptionAddr +{ + public: + + /*! + * \brief Constructeur. + * \param addr_str_view L'adresse à convertir. + */ + explicit ParameterOptionAddr(StringView addr_str_view); + + public: + + // On ne doit pas bloquer les multiples ParameterOptionAddrPart(ANY) : + // Construction par iteration : aaaa/bb/ANY/ANY/cc + /*! + * \brief Méthode permettant d'ajouter une partie à la fin de l'adresse actuelle. + * \param part Un pointeur vers la nouvelle partie. Attention, on récupère la + * propriété de l'objet (on gère le delete). + */ + void addAddrPart(ParameterOptionAddrPart* part); + + /*! + * \brief Méthode permettant de récupérer une partie de l'adresse. + * Si l'adresse termine par un ANY_TAG[ANY_INDEX], tous index donnés en paramètre + * supérieur au nombre de partie de l'adresse retournera le dernier élément de + * l'adresse ("ANY_TAG[ANY_INDEX]"). + * + * \param index_of_part L'index de la partie à récupérer. + * \return La partie de l'adresse. + */ + ParameterOptionAddrPart* addrPart(const Integer index_of_part) const; + + ParameterOptionAddrPart* lastAddrPart() const; + + /*! + * \brief Méthode permettant de récupérer le nombre de partie de l'adresse. + * Les parties égales à "ANY_TAG[ANY_INDEX]" sont comptées. + * + * \return Le nombre de partie de l'adresse. + */ + Integer nbAddrPart() const; + + /*! + * \brief Méthode permettant de récupérer un ou plusieurs indices dans l'adresse. + * + * Le fonctionnement de cette méthode est simple. + * Nous avons l'adresse suivante : "aaa[1]/bbb[2]/ccc[4]/\@name[1]". + * L'adresse en paramètre est la suivante : "aaa[1]/bbb[GET_INDEX]/ccc[4]/\@name[1]". + * L'indice ajouté dans la vue en paramètre sera 2. + * + * Si l'adresse en paramètre est : "aaa[1]/bbb[GET_INDEX]/ccc[GET_INDEX]/\@name[1]". + * Les indices ajoutés dans la vue seront 2 et 4. + * + * En revanche, un "GET_INDEX" ne peut pas être utilisé sur un "ANY_INDEX" (return false). + * Exemple : si l'on a : "aaa[1]/bbb[ANY_INDEX]/ccc[4]/\@name[1]". + * Et si l'adresse en paramètre est : "aaa[1]/bbb[GET_INDEX]/ccc[GET_INDEX]/\@name[1]". + * Le booléen retourné sera false. + * + * Pour avoir la bonne taille de la vue, un appel à la méthode "nbIndexToGetInAddr()" + * peut être effectué. + * + * \param addr_with_get_index L'adresse contenant des indices "GET_INDEX". + * \param indexes [OUT] La vue dans laquelle sera ajouté le ou les indices (la taille devra être correct). + * \return true si la vue a pu être remplie correctement. + */ + bool getIndexInAddr(const ParameterOptionAddr& addr_with_get_index, ArrayView indexes) const; + + /*! + * \brief Méthode permettant de savoir combien il y a de "GET_INDEX" dans l'adresse. + * \return Le nombre de "GET_INDEX". + */ + Integer nbIndexToGetInAddr() const; + + public: + + /*! + * \brief Opérateur d'égalité. + * Cet opérateur tient compte des ANY_TAG / ANY_INDEX. + * L'adresse "aaa[1]/bbb[2]/ANY_TAG[ANY_INDEX]" + * sera éqale à l'adresse "aaa[1]/bbb[2]/ccc[5]/ddd[7]" + * ou à l'adresse "aaa[1]/bbb[ANY_INDEX]/ccc[5]/ddd[7]" + * ou à l'adresse "aaa[1]/bbb[2]" + * mais pas à l'adresse "aaa[1]" + */ + bool operator==(const ParameterOptionAddr& other) const; + + // TODO AH : À supprimer lors du passage en C++20. + bool operator!=(const ParameterOptionAddr& other) const; + + private: + + UniqueArray> m_parts; +}; + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +/*! + * \brief Classe représentant un élément XML (une option Arcane). + * Cet élement a une adresse et une valeur. + */ +class ARCANE_UTILS_EXPORT +ParameterOptionElement +{ + public: + + ParameterOptionElement(const StringView addr, const StringView value); + + ParameterOptionAddr addr() const; + + StringView value() const; + + bool operator==(const ParameterOptionAddr& addr) const; + + private: + + ParameterOptionAddr m_addr; + StringView m_value; +}; + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +/*! + * \brief Classe représentant un ensemble d'éléments XML (un ensemble d'options Arcane). + */ +class ARCANE_UTILS_EXPORT +ParameterOptionElementsCollection +{ + public: + + /*! + * \brief Méthode permettant d'ajouter un paramètre d'option dans la liste + * des paramètres d'options. + * + * \warning Les deux paramètres ne sont pas copiés ! On ne récupère qu'une vue. L'utilisateur + * de cette classe doit gérer la durée de vie de ces objets. + * + * \param parameter Le paramètre d'option brut (avec les "//" au début). + * \param value La valeur de l'option. + */ + void addParameter(const String& parameter, const String& value); + + void addElement(StringView addr, StringView value); + + // ParameterOptionElement element(const Integer index) + // { + // return m_elements[index]; + // } + + // Un StringView "vide" est éqal à un StringView "nul". + // Comme on travaille avec des String et que la distinction + // vide/nul est importante, on passe par un std::optional. + std::optional value(const ParameterOptionAddr& addr); + + /*! + * \brief Méthode permettant de savoir si une adresse est présente dans la liste d'éléments. + * Les ANY_TAG/ANY_INDEX sont pris en compte. + * \param addr L'adresse à rechercher. + * \return true si l'adresse est trouvé. + */ + bool isExistAddr(const ParameterOptionAddr& addr); + + /*! + * \brief Méthode permettant de savoir combien de fois une adresse est présente dans la liste d'élements. + * Méthode particulièrement utile avec les ANY_TAG/ANY_INDEX. + * + * \param addr L'adresse à rechercher. + * \return Le nombre de correspondances trouvé. + */ + Integer countAddr(const ParameterOptionAddr& addr); + + /*! + * \brief Méthode permettant de récupérer un ou plusieurs indices dans la liste d'adresses. + * + * Le fonctionnement de cette méthode est simple. + * Nous avons les adresses suivantes : "aaa[1]/bbb[2]/ccc[1]/\@name[1]". + * "aaa[1]/bbb[2]/ccc[2]/\@name[1]". + * "ddd[1]/eee[2]". + * "fff[1]/ggg[2]/hhh[4]". + * L'adresse en paramètre est la suivante : "aaa[1]/bbb[2]/ccc[GET_INDEX]/\@name[1]". + * Les indices ajoutés dans le tableau en paramètre seront 1 et 2. + * + * Attention : Avoir une adresse en entrée avec plusieurs "GET_INDEX" est autorisé mais + * ça peut être dangereux si le nombre d'indices trouvé par adresse est différent pour + * chaque adresse (s'il y a deux "GET_INDEX" mais que dans une des adresses, il n'y a + * pas deux correspondances, ces éventuelles correspondances ne seront pas prises en + * compte). + * + * \param addr_with_get_index L'adresse contenant des indices "GET_INDEX". + * \param indexes [OUT] Le tableau dans lequel sera ajouté le ou les indices (le tableau + * n'est pas effacé avant utilisation). + */ + void getIndexInAddr(const ParameterOptionAddr& addr_with_get_index, UniqueArray& indexes); + + private: + + UniqueArray m_elements; +}; + + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +} // End namespace Arcane + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +#endif diff --git a/arcane/src/arcane/utils/srcs.cmake b/arcane/src/arcane/utils/srcs.cmake index 4ade74bd0..145dc50d8 100644 --- a/arcane/src/arcane/utils/srcs.cmake +++ b/arcane/src/arcane/utils/srcs.cmake @@ -117,6 +117,8 @@ set(ARCANE_SOURCES ParallelFatalErrorException.h ParallelLoopOptions.h ParallelLoopOptions.cc + ParameterCaseOption.h + ParameterCaseOption.cc PerfCounterMng.cc PerfCounterMng.h PlatformUtils.cc @@ -343,6 +345,8 @@ set(ARCANE_SOURCES internal/MemoryUtilsInternal.h internal/IMemoryRessourceMngInternal.h internal/IMemoryCopier.h + internal/ParameterOption.h + internal/ParameterOption.cc internal/ProfilingInternal.h internal/ValueConvertInternal.h internal/SpecificMemoryCopyList.h diff --git a/arcane/tests/testCaseOptions-commandLineReplace.arc b/arcane/tests/testCaseOptions-commandLineReplace.arc new file mode 100644 index 000000000..468c9de23 --- /dev/null +++ b/arcane/tests/testCaseOptions-commandLineReplace.arc @@ -0,0 +1,124 @@ + + + + Test Arcane 1 + Test Arcane 1 + CaseOptionsTester2 + + + + + + 422 + + + + + + @TestId@ + 20 + @SimpleRealUnit2@ + @SimpleReal3@ + + toto1 + @SimpleStringMultiple@ + + 3.0 4.1 5.6 + + 4.5 + + enum1 + + + + + + + + true + 50 + + + + 12 + false + + + + 5 + false + + + + 32 + false + + + 32 + false + + + 64 + false + + + + 5.2 + 3.0 2.0 4.0 + 4 + enum2 + + 2.0 3.0 + 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 + + + + true + 1. + 4 + enum1 + enum1 + + + true + 3 + 4 + enum1 + enum1 + + 3 + 4 + enum1 + enum1 + + 5 + 3 + + + + 5 + 7 + enum2 + enum2 + + 12 + 4 + + + + enum1 + + 5.2 + + + 5.2 + + + 4.9xs + + + 4.9xs + + + + diff --git a/arcane/tests/testParameterOption.arc b/arcane/tests/testParameterOption.arc new file mode 100644 index 000000000..26d170879 --- /dev/null +++ b/arcane/tests/testParameterOption.arc @@ -0,0 +1,37 @@ + + + + Test StringVariableReplace + UnitTest + + + + + + 1 + + 1 + 1 + + 0.0 0.0 + + + 1.0 + 1 + + + + 1.0 + 1 + + + + + + + + + + + + \ No newline at end of file