14
14
*/
15
15
package org .htmlunit .javascript .host .dom ;
16
16
17
- import java .util .Arrays ;
18
- import java .util .HashSet ;
17
+ import java .util .ArrayList ;
18
+ import java .util .List ;
19
19
20
20
import org .apache .commons .lang3 .StringUtils ;
21
21
import org .htmlunit .WebClient ;
@@ -88,10 +88,7 @@ public int getLength() {
88
88
return 0 ;
89
89
}
90
90
91
- final String [] parts = StringUtils .split (value , WHITESPACE_CHARS );
92
- final HashSet <String > elements = new HashSet <>(parts .length );
93
- elements .addAll (Arrays .asList (parts ));
94
- return elements .size ();
91
+ return split (value ).size ();
95
92
}
96
93
97
94
private String getAttribValue () {
@@ -134,24 +131,12 @@ public void add(final String token) {
134
131
throw JavaScriptEngine .reportRuntimeError ("Empty input not allowed" );
135
132
}
136
133
137
- String value = getAttribValue ();
138
- if (StringUtils .isEmpty (value )) {
139
- value = token ;
140
- }
141
- else {
142
- value = String .join (" " , StringUtils .split (value , WHITESPACE_CHARS ));
143
- if (position (value , token ) < 0 ) {
144
- if (value .length () != 0 && !isWhitespace (value .charAt (value .length () - 1 ))) {
145
- value = value + " " ;
146
- }
147
- value = value + token ;
148
- }
149
- else {
150
- value = String .join (" " , StringUtils .split (value , WHITESPACE_CHARS ));
151
- }
134
+ final List <String > parts = split (getAttribValue ());
135
+ if (!parts .contains (token )) {
136
+ parts .add (token );
152
137
}
138
+ updateAttribute (String .join (" " , parts ));
153
139
154
- updateAttribute (value );
155
140
}
156
141
157
142
/**
@@ -167,39 +152,43 @@ public void remove(final String token) {
167
152
throw JavaScriptEngine .reportRuntimeError ("Empty input not allowed" );
168
153
}
169
154
170
- final String oldValue = getAttribValue ();
171
- if (oldValue == null ) {
172
- return ;
173
- }
174
-
175
- String value = String .join (" " , StringUtils .split (oldValue , WHITESPACE_CHARS ));
176
- int pos = position (value , token );
177
- while (pos != -1 ) {
178
- int from = pos ;
179
- int to = pos + token .length ();
155
+ final List <String > parts = split (getAttribValue ());
156
+ parts .remove (token );
157
+ updateAttribute (String .join (" " , parts ));
158
+ }
180
159
181
- while (from > 0 && isWhitespace (value .charAt (from - 1 ))) {
182
- from = from - 1 ;
183
- }
184
- while (to < value .length () - 1 && isWhitespace (value .charAt (to ))) {
185
- to = to + 1 ;
186
- }
160
+ /**
161
+ * Replaces an existing token with a new token. If the first token doesn't exist, replace()
162
+ * returns false immediately, without adding the new token to the token list.
163
+ * @param oldToken a string representing the token you want to replace
164
+ * @param newToken a string representing the token you want to replace oldToken with
165
+ * @return true if oldToken was successfully replaced, or false if not
166
+ */
167
+ @ JsxFunction
168
+ public boolean replace (final String oldToken , final String newToken ) {
169
+ if (StringUtils .isEmpty (oldToken )) {
170
+ throw JavaScriptEngine .reportRuntimeError ("Empty oldToken not allowed" );
171
+ }
172
+ if (StringUtils .containsAny (oldToken , WHITESPACE_CHARS )) {
173
+ throw JavaScriptEngine .reportRuntimeError ("oldToken contains whitespace" );
174
+ }
187
175
188
- final StringBuilder result = new StringBuilder ();
189
- if (from > 0 ) {
190
- result .append (value , 0 , from );
191
- if (to < value .length ()) {
192
- result .append (' ' );
193
- }
194
- }
195
- result .append (value , to , value .length ());
196
- value = result .toString ();
176
+ if (StringUtils .isEmpty (newToken )) {
177
+ throw JavaScriptEngine .reportRuntimeError ("Empty newToken not allowed" );
178
+ }
179
+ if (StringUtils .containsAny (newToken , WHITESPACE_CHARS )) {
180
+ throw JavaScriptEngine .reportRuntimeError ("newToken contains whitespace" );
181
+ }
197
182
198
- pos = position (value , token );
183
+ final List <String > parts = split (getAttribValue ());
184
+ final int pos = parts .indexOf (oldToken );
185
+ while (pos == -1 ) {
186
+ return false ;
199
187
}
200
188
201
- value = String .join (" " , StringUtils .split (value , WHITESPACE_CHARS ));
202
- updateAttribute (value );
189
+ parts .set (pos , newToken );
190
+ updateAttribute (String .join (" " , parts ));
191
+ return true ;
203
192
}
204
193
205
194
/**
@@ -235,13 +224,8 @@ public boolean contains(final String token) {
235
224
throw JavaScriptEngine .reportRuntimeError ("Empty input not allowed" );
236
225
}
237
226
238
- String value = getAttribValue ();
239
- if (StringUtils .isEmpty (value )) {
240
- return false ;
241
- }
242
-
243
- value = String .join (" " , StringUtils .split (value , WHITESPACE_CHARS ));
244
- return position (value , token ) > -1 ;
227
+ final List <String > parts = split (getAttribValue ());
228
+ return parts .contains (token );
245
229
}
246
230
247
231
/**
@@ -260,9 +244,9 @@ public Object item(final int index) {
260
244
return null ;
261
245
}
262
246
263
- final String [] values = StringUtils . split (value , WHITESPACE_CHARS );
264
- if (index < values . length ) {
265
- return values [ index ] ;
247
+ final List < String > parts = split (getAttribValue () );
248
+ if (index < parts . size () ) {
249
+ return parts . get ( index ) ;
266
250
}
267
251
268
252
return null ;
@@ -289,12 +273,12 @@ public Object[] getIds() {
289
273
return normalIds ;
290
274
}
291
275
292
- final String [] values = StringUtils . split (value , WHITESPACE_CHARS );
293
- final Object [] ids = new Object [values . length + normalIds .length ];
294
- for (int i = 0 ; i < values . length ; i ++) {
276
+ final List < String > parts = split (getAttribValue () );
277
+ final Object [] ids = new Object [parts . size () + normalIds .length ];
278
+ for (int i = 0 ; i < parts . size () ; i ++) {
295
279
ids [i ] = i ;
296
280
}
297
- System .arraycopy (normalIds , 0 , ids , values . length , normalIds .length );
281
+ System .arraycopy (normalIds , 0 , ids , parts . size () , normalIds .length );
298
282
299
283
return ids ;
300
284
}
@@ -340,9 +324,9 @@ public void forEach(final Object callback) {
340
324
final ContextAction <Object > contextAction = cx -> {
341
325
final Function function = (Function ) callback ;
342
326
final Scriptable scope = getParentScope ();
343
- final String [] values = StringUtils . split (value , WHITESPACE_CHARS );
344
- for (int i = 0 ; i < values . length ; i ++) {
345
- function .call (cx , scope , this , new Object [] {values [ i ] , i , this });
327
+ final List < String > parts = split (getAttribValue () );
328
+ for (int i = 0 ; i < parts . size () ; i ++) {
329
+ function .call (cx , scope , this , new Object [] {parts . get ( i ) , i , this });
346
330
}
347
331
return null ;
348
332
};
@@ -371,26 +355,20 @@ private void updateAttribute(final String value) {
371
355
domNode .setAttributeNode (attr );
372
356
}
373
357
374
- private static int position (final String value , final String token ) {
375
- final int pos = value .indexOf (token );
376
- if (pos < 0 ) {
377
- return -1 ;
358
+ private static List <String > split (final String value ) {
359
+ if (StringUtils .isEmpty (value )) {
360
+ return new ArrayList <>();
378
361
}
379
362
380
- // whitespace before
381
- if (pos != 0 && !isWhitespace (value .charAt (pos - 1 ))) {
382
- return -1 ;
383
- }
363
+ final String [] parts = StringUtils .split (value , WHITESPACE_CHARS );
384
364
385
- // whitespace after
386
- final int end = pos + token .length ();
387
- if (end != value .length () && !isWhitespace (value .charAt (end ))) {
388
- return -1 ;
365
+ // usually a short list, no index needed
366
+ final List <String > result = new ArrayList <>();
367
+ for (final String part : parts ) {
368
+ if (!result .contains (part )) {
369
+ result .add (part );
370
+ }
389
371
}
390
- return pos ;
391
- }
392
-
393
- private static boolean isWhitespace (final int ch ) {
394
- return WHITESPACE_CHARS .indexOf (ch ) > -1 ;
372
+ return result ;
395
373
}
396
374
}
0 commit comments