Skip to content

Commit ab23d3f

Browse files
authored
Merge pull request #1950 from arcaneframework/dev/ah-update-stringvariablereplace-part
Update the methods in the StringVariableReplace class
2 parents 3d70be2 + 7589240 commit ab23d3f

File tree

6 files changed

+454
-137
lines changed

6 files changed

+454
-137
lines changed

arcane/src/arcane/core/internal/StringVariableReplace.cc

Lines changed: 201 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
22
//-----------------------------------------------------------------------------
3-
// Copyright 2000-2023 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
3+
// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
44
// See the top-level COPYRIGHT file for details.
55
// SPDX-License-Identifier: Apache-2.0
66
//-----------------------------------------------------------------------------
77
/*---------------------------------------------------------------------------*/
8-
/* StringVariableReplace.cc (C) 2000-2023 */
8+
/* StringVariableReplace.cc (C) 2000-2025 */
99
/* */
1010
/* Classe permettant de remplacer les symboles d'une chaine de caractères */
1111
/* par une autre chaine de caractères définie dans les arguments de */
@@ -14,8 +14,11 @@
1414
/* Exemple : @mon_symbole@ */
1515
/*---------------------------------------------------------------------------*/
1616

17-
#include "arcane/core/internal/StringVariableReplace.h"
1817
#include "arcane/utils/PlatformUtils.h"
18+
#include "arcane/utils/SmallArray.h"
19+
#include "arcane/utils/StringBuilder.h"
20+
21+
#include "arcane/core/internal/StringVariableReplace.h"
1922

2023
/*---------------------------------------------------------------------------*/
2124
/*---------------------------------------------------------------------------*/
@@ -27,28 +30,58 @@ namespace Arcane
2730
/*---------------------------------------------------------------------------*/
2831

2932
/*!
30-
* Méthode permettant de remplacer les symboles de la chaine de caractères name
31-
* par leurs valeurs définies dans la liste des paramètres.
33+
* \brief Méthode permettant de remplacer les symboles de la chaine de
34+
* caractères \a string_with_symbols par leurs valeurs définies dans la liste
35+
* des paramètres.
3236
*
33-
* Pour l'instant, un symbole est représenté par une chaine de caractères entourée
37+
* Un symbole est représenté par une chaine de caractères entourée
3438
* de deux "\@".
3539
*
3640
* Exemple : "\@mesh_dir\@/cube.msh"
3741
* avec un paramètre "mesh_dir=~/mesh"
3842
* donnera : "~/mesh/cube.msh".
3943
*
40-
* À noter que les @ ne seront pas supprimés s'ils ne correspondent pas à un symbole.
44+
*
45+
* Pour éviter qu'un "\@" soit remplacé, il est possible de mettre un backslash avant.
46+
* Le backslash sera supprimé par cette méthode.
47+
*
48+
* Exemple : "\@mesh_dir\@/cube\\\@.msh"
49+
* avec un paramètre "mesh_dir=~/mesh"
50+
* donnera : "~/mesh/cube\@.msh".
51+
*
52+
*
53+
* Si le nombre d'arrobases est incorrect (sans compter les arrobases échappées),
54+
* une erreur sera déclenchée, sauf si le paramètre \a fatal_if_invalid est à \a false.
55+
* Dans ce cas, la dernière arrobase sera simplement supprimée.
56+
*
57+
* Exemple : "\@mesh_dir\@\@/cube.msh"
58+
* avec un paramètre "mesh_dir=~/mesh"
59+
* donnera : "~/mesh/cube.msh".
60+
*
61+
*
62+
* Les symboles qui ne sont pas trouvés seront supprimés ou, si le paramètre
63+
* \a fatal_if_not_found est à \a true, une erreur sera déclenchée.
4164
*
4265
* Exemple : "\@mesh_dir\@/cube.msh"
4366
* sans paramètres
44-
* donnera : "\@mesh_dir\@/cube.msh".
67+
* donnera : "/cube.msh".
68+
*
69+
* Enfin, avoir un paramètre ayant un nom contenant une arrobase sera invalide.
70+
* (en revanche, la valeur peut contenir des arrobases).
4571
*
46-
* @param parameter_list La liste des paramètres à considérer.
47-
* @param string_with_symbols La chaine de caractères avec les symboles à remplacer.
48-
* @return La chaine de caractères avec les symboles remplacés par leurs valeurs.
72+
* Exemple invalide : paramètre "mesh\@_dir=~/mesh"
73+
* Exemple valide : paramètre "mesh_dir=~/\@/mesh"
74+
*
75+
* \param parameter_list La liste des paramètres à considérer.
76+
* \param string_with_symbols La chaine de caractères avec les symboles à remplacer.
77+
* \param fatal_if_not_found Si un symbole n'est pas trouvé dans la liste des paramètres, une erreur sera déclenchée
78+
* si ce paramètre vaut true.
79+
* \param fatal_if_invalid Si la chaine de caractères est incorrecte, une erreur sera déclenchée
80+
* si ce paramètre vaut true. Sinon, le résultat n'est pas garanti.
81+
* \return La chaine de caractères avec les symboles remplacés par leurs valeurs.
4982
*/
5083
String StringVariableReplace::
51-
replaceWithCmdLineArgs(const ParameterList& parameter_list, const String& string_with_symbols)
84+
replaceWithCmdLineArgs(const ParameterList& parameter_list, StringView string_with_symbols, bool fatal_if_not_found, bool fatal_if_invalid)
5285
{
5386
// Si la variable d'environnement ARCANE_REPLACE_SYMBOLS_IN_DATASET n'est pas
5487
// définie, on ne touche pas à la chaine de caractères.
@@ -57,115 +90,191 @@ replaceWithCmdLineArgs(const ParameterList& parameter_list, const String& string
5790
return string_with_symbols;
5891
}
5992

60-
if(string_with_symbols.empty()) return string_with_symbols;
93+
if (string_with_symbols.empty())
94+
return string_with_symbols;
95+
96+
Integer nb_at = 0;
97+
Integer nb_at_with_escape = 0;
98+
99+
// Les arrobases peuvent être échappées. Il est nécessaire de les différencier pour avoir
100+
// la taille du tableau des morceaux et pour vérifier la validité de la chaine de caractères.
101+
_countChar(string_with_symbols, '@', nb_at, nb_at_with_escape);
102+
if (nb_at == 0 && nb_at_with_escape == 0)
103+
return string_with_symbols;
104+
105+
// Si le nombre d'arrobases est impaire, il y a forcément une incohérence.
106+
if (fatal_if_invalid && nb_at % 2 == 1) {
107+
ARCANE_FATAL("Invalid nb of @");
108+
}
109+
110+
// Une arrobase sépare la chaine en deux morceaux. Donc le nombre de morceaux sera de "nb_at + 1".
111+
// On ajoute les arrobases échappées. Ces arrobases sont considérées comme un symbole spécial et
112+
// génèrent deux morceaux (donc "nb_at_with_escape * 2").
113+
const Integer size_array_w_splits = (nb_at + 1) + (nb_at_with_escape * 2);
114+
115+
// Dans un cas valide, le nombre de morceaux doit être impaire.
116+
// Dans le cas où l'utilisateur désactive le fatal_if_invalid et que le nombre de morceaux est paire,
117+
// il ne faut pas qu'on considère le morceau final comme un symbole (voir la boucle plus bas pour comprendre).
118+
const Integer max_index = size_array_w_splits % 2 == 0 ? size_array_w_splits - 1 : size_array_w_splits;
61119

62-
UniqueArray<String> string_splited;
63-
UniqueArray<Integer> symbol_pos;
64-
Integer nb_at;
120+
SmallArray<StringView> string_splited(size_array_w_splits);
121+
_splitString(string_with_symbols, string_splited, '@');
65122

66-
// On découpe la string là où se trouve les @.
67-
// On récupère la position des symboles et le nombre de @.
68-
_splitString(string_with_symbols, string_splited, symbol_pos, nb_at, '@');
123+
StringBuilder combined{};
69124

70-
// S'il n'y a aucun symbole, on retourne la chaine d'origine.
71-
if(symbol_pos.empty()) return string_with_symbols;
125+
// Dans le tableau, les morceaux ayant une position impaire sont les élements entre arrobases
126+
// et donc des symboles.
127+
for (Integer i = 0; i < max_index; ++i) {
128+
StringView part{ string_splited[i] };
129+
if (part.empty())
130+
continue;
72131

73-
for(Integer pos : symbol_pos){
74-
String& symbol = string_splited[pos];
75-
String reference_input = parameter_list.getParameterOrNull(symbol);
76-
if(reference_input.null()) {
77-
symbol = "@" + symbol + "@";
132+
// Les morceaux avec une position paire ne sont pas des symboles.
133+
// On traite aussi le cas particulier des arrobases échappées.
134+
if (i % 2 == 0 || part.bytes()[0] == '@') {
135+
combined.append(part);
78136
}
79137
else {
80-
symbol = reference_input;
138+
String reference_input = parameter_list.getParameterOrNull(part);
139+
if (reference_input.null()) {
140+
if (fatal_if_not_found) {
141+
ARCANE_FATAL("Symbol @{0}@ not found in the parameter list", part);
142+
}
143+
}
144+
else {
145+
combined.append(reference_input);
146+
}
81147
}
82148
}
83149

84-
// On recombine la chaine de caractères.
85-
StringBuilder combined = "";
86-
for(Integer i = 0; i < string_splited.size() - 1; ++i){
87-
const String& str = string_splited[i];
88-
combined.append(str);
89-
}
90-
91-
// Si le nombre de @ est impair, alors il faut rajouter un @ avant le dernier element pour reproduire
92-
// la chaine d'origine.
93-
if(nb_at % 2 != 0){
94-
combined.append("@" + string_splited[string_splited.size()-1]);
95-
}
96-
else{
97-
combined.append(string_splited[string_splited.size()-1]);
150+
// Dans le cas où l'utilisateur désactive le fatal_if_invalid, le dernier morceau
151+
// est à une position impaire mais n'est pas un symbole, donc on le rajoute ici.
152+
if (size_array_w_splits % 2 == 0) {
153+
combined.append(string_splited[string_splited.size() - 1]);
98154
}
99155

100156
return combined.toString();
101157
}
102158

159+
/*---------------------------------------------------------------------------*/
160+
/*---------------------------------------------------------------------------*/
161+
103162
/*!
104-
* Méthode permettant de splitter la chaine "str" en plusieurs morceaux.
163+
* \brief Méthode permettant de splitter la chaine "str_view" en plusieurs morceaux.
105164
* Les splits seront entre les chars "c".
106-
* Les morceaux seront ajoutés dans le tableau "str_array".
107-
* Les positions des morceaux correspondant à un symbole seront ajoutées
108-
* dans le tableau "int_array".
109-
* Le nombre de char "c" sera mis dans le paramètre nb_c.
110-
*
111-
* @param str [IN] La chaine de caractères à split.
112-
* @param str_array [OUT] Le tableau qui contiendra les morceaux.
113-
* @param int_array [OUT] Le tableau avec les positions des symboles dans le tableau "str_array".
114-
* @param nb_c [OUT] Le nombre de char "c".
115-
* @param c Le char délimitant les morceaux.
165+
* Les morceaux seront ajoutés dans le tableau "str_view_array".
166+
* Les morceaux ayant une position impaire sont des symboles.
167+
* Les morceaux ayant une position paire ne sont pas des symboles.
168+
* Dans le cas où le nombre de morceaux est paire, le dernier morceau ne sera
169+
* pas un symbole.
170+
* Dans le cas où une arrobase est échappée, elle sera mise à une position
171+
* impaire. Ce symbole spécial sera à considérer.
172+
*
173+
* \param str_view [IN] La chaine de caractères à split.
174+
* \param str_view_array [OUT] Le tableau qui contiendra les morceaux.
175+
* \param c Le char délimitant les morceaux.
116176
*/
117-
void StringVariableReplace::_splitString(const String& str, UniqueArray<String>& str_array, UniqueArray<Integer>& int_array, Integer& nb_c, char c)
177+
void StringVariableReplace::
178+
_splitString(StringView str_view, ArrayView<StringView> str_view_array, char c)
118179
{
119-
Span<const Byte> str_str = str.bytes();
180+
/*
181+
aa@aa -> "aa", "aa" 2 (fatal_if_invalid)
182+
@aa@aa@aa@ -> "", "aa", "aa", "aa", "" 5
183+
@aa@@aa@ -> "", "aa", "", "aa", "" 5
184+
@ -> "", "" 2 (fatal_if_invalid)
185+
@aa@ -> "", "aa", "" 3
186+
@aa@aa\@aa -> "", "aa", "aa", "@", "aa" 5
187+
@aa@\@@aa@ -> "", "aa", "", "@", "", "aa", "" 7
188+
@aa@@aa@ -> "", "aa", "", "aa", "" 5
189+
\@aa -> "", "@", "aa" 3
190+
@aa@aa@aa -> "", "aa", "aa", "aa" 4 (fatal_if_invalid)
191+
@aa@aa -> "", "aa", "aa" 3
192+
*/
120193

121-
Int64 offset = 0;
122-
bool is_symbol = false;
123-
Int64 len = str.length();
124-
nb_c = 0;
194+
Span<const Byte> str_span = str_view.bytes();
125195

126-
for(Int64 i = 0; i < len; ++i) {
196+
Int64 offset = 0;
197+
Int64 len = str_view.length();
198+
Integer index = 0;
199+
bool previous_backslash = false;
127200

128-
if (str_str[i] == c) {
129-
nb_c++;
130-
str_array.add(str.substring(offset, i - offset));
131-
offset = i+1;
201+
for (Int64 i = 0; i < len; ++i) {
202+
// Si on trouve une arrobase.
203+
if (str_span[i] == c) {
204+
// Si cette arrobase est précédée par un backslash.
205+
if (previous_backslash) {
206+
// On enregistre le morceau qu'on parcourait, sans le backslash.
207+
str_view_array[index++] = str_view.subView(offset, i - 1 - offset);
208+
// On rajoute l'arrobase.
209+
str_view_array[index++] = str_view.subView(i, 1);
132210

133-
if(is_symbol){
134-
int_array.add(str_array.size() - 1);
135-
is_symbol = false;
211+
offset = i + 1;
212+
previous_backslash = false;
136213
}
137-
else{
138-
is_symbol = true;
214+
else {
215+
// On enregistre le morceau qu'on parcourait, sans l'arrobase.
216+
str_view_array[index++] = str_view.subView(offset, i - offset);
217+
218+
offset = i + 1;
139219
}
140220
}
221+
// Si on trouve un backslash.
222+
else if (str_span[i] == '\\') {
223+
// Et qu'on avait déjà trouvé un backslash, on n'y touche pas.
224+
if (previous_backslash)
225+
previous_backslash = false;
226+
else
227+
previous_backslash = true;
228+
}
229+
else {
230+
previous_backslash = false;
231+
}
141232
}
142-
if(offset == len){
143-
str_array.push_back("");
144-
}
145-
else {
146-
str_array.push_back(str.substring(offset, len - offset));
147-
}
233+
// On ajoute le dernier morceau.
234+
str_view_array[index] = str_view.subView(offset, len - offset);
148235
}
149236

237+
/*---------------------------------------------------------------------------*/
238+
/*---------------------------------------------------------------------------*/
239+
240+
/*!
241+
* \brief Méthode permettant de compter le nombre de caractères séparateurs
242+
* dans une chaine de caractères.
243+
*
244+
* \param str_view La chaine de caractère.
245+
* \param c Le caractère séparant les morceaux.
246+
* \param count_c Le nombre total de caractère \a c
247+
* \param count_c_with_escape Le nombre de caractères \a c ayant un backslash avant.
248+
*/
249+
void StringVariableReplace::
250+
_countChar(StringView str_view, char c, Integer& count_c, Integer& count_c_with_escape)
251+
{
252+
count_c = 0;
253+
count_c_with_escape = 0;
254+
bool previous_backslash = false;
150255

151-
//template<typename StringContainer>
152-
//void StringVariableReplace::split(const String& str, StringContainer& str_array, char c)
153-
//{
154-
// Span<const Byte> str_str = str.bytes();
155-
//
156-
// Int64 offset = 0;
157-
// Int64 len = str.length();
158-
//
159-
//
160-
// for( Int64 i = 0; i < len; ++i ) {
161-
// if (str_str[i] == c) {
162-
// str_array.push_back(str.substring(offset, i - offset));
163-
// offset = i+1;
164-
//
165-
// }
166-
// }
167-
// str_array.push_back(str.substring(offset, len - offset));
168-
//}
256+
for (const Byte byte : str_view.bytes()) {
257+
if (byte == c) {
258+
if (previous_backslash) {
259+
count_c_with_escape++;
260+
previous_backslash = false;
261+
}
262+
else {
263+
count_c++;
264+
}
265+
}
266+
else if (byte == '\\') {
267+
// On avait déjà trouvé un backslash, on n'y touche pas.
268+
if (previous_backslash)
269+
previous_backslash = false;
270+
else
271+
previous_backslash = true;
272+
}
273+
else {
274+
previous_backslash = false;
275+
}
276+
}
277+
}
169278

170279
/*---------------------------------------------------------------------------*/
171280
/*---------------------------------------------------------------------------*/

0 commit comments

Comments
 (0)