1
1
// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2
2
// -----------------------------------------------------------------------------
3
- // Copyright 2000-2023 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
3
+ // Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4
4
// See the top-level COPYRIGHT file for details.
5
5
// SPDX-License-Identifier: Apache-2.0
6
6
// -----------------------------------------------------------------------------
7
7
/* ---------------------------------------------------------------------------*/
8
- /* StringVariableReplace.cc (C) 2000-2023 */
8
+ /* StringVariableReplace.cc (C) 2000-2025 */
9
9
/* */
10
10
/* Classe permettant de remplacer les symboles d'une chaine de caractères */
11
11
/* par une autre chaine de caractères définie dans les arguments de */
14
14
/* Exemple : @mon_symbole@ */
15
15
/* ---------------------------------------------------------------------------*/
16
16
17
- #include " arcane/core/internal/StringVariableReplace.h"
18
17
#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"
19
22
20
23
/* ---------------------------------------------------------------------------*/
21
24
/* ---------------------------------------------------------------------------*/
@@ -27,28 +30,58 @@ namespace Arcane
27
30
/* ---------------------------------------------------------------------------*/
28
31
29
32
/* !
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.
32
36
*
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
34
38
* de deux "\@".
35
39
*
36
40
* Exemple : "\@mesh_dir\@/cube.msh"
37
41
* avec un paramètre "mesh_dir=~/mesh"
38
42
* donnera : "~/mesh/cube.msh".
39
43
*
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.
41
64
*
42
65
* Exemple : "\@mesh_dir\@/cube.msh"
43
66
* 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).
45
71
*
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.
49
82
*/
50
83
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 )
52
85
{
53
86
// Si la variable d'environnement ARCANE_REPLACE_SYMBOLS_IN_DATASET n'est pas
54
87
// définie, on ne touche pas à la chaine de caractères.
@@ -57,115 +90,191 @@ replaceWithCmdLineArgs(const ParameterList& parameter_list, const String& string
57
90
return string_with_symbols;
58
91
}
59
92
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;
61
119
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, ' @' );
65
122
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{};
69
124
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 ;
72
131
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);
78
136
}
79
137
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
+ }
81
147
}
82
148
}
83
149
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 ]);
98
154
}
99
155
100
156
return combined.toString ();
101
157
}
102
158
159
+ /* ---------------------------------------------------------------------------*/
160
+ /* ---------------------------------------------------------------------------*/
161
+
103
162
/* !
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.
105
164
* 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.
116
176
*/
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)
118
179
{
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
+ */
120
193
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 ();
125
195
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 ;
127
200
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 );
132
210
133
- if (is_symbol){
134
- int_array.add (str_array.size () - 1 );
135
- is_symbol = false ;
211
+ offset = i + 1 ;
212
+ previous_backslash = false ;
136
213
}
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 ;
139
219
}
140
220
}
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
+ }
141
232
}
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);
148
235
}
149
236
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 ;
150
255
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
+ }
169
278
170
279
/* ---------------------------------------------------------------------------*/
171
280
/* ---------------------------------------------------------------------------*/
0 commit comments