diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 6337b23e11..9666ce0822 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -9,7 +9,10 @@
- DOMTokenList is now iterable, keys(), entries() method implemented.
+ DOMTokenList simplify impl and fix removal of duplicate entries.
+
+
+ DOMTokenList is now iterable, keys(), entries(), and replace() method implemented.
Implementation of DOMTokenList.forEach() added.
diff --git a/src/main/java/org/htmlunit/javascript/host/dom/DOMTokenList.java b/src/main/java/org/htmlunit/javascript/host/dom/DOMTokenList.java
index 67d4040fee..c6d1dd6669 100644
--- a/src/main/java/org/htmlunit/javascript/host/dom/DOMTokenList.java
+++ b/src/main/java/org/htmlunit/javascript/host/dom/DOMTokenList.java
@@ -14,8 +14,8 @@
*/
package org.htmlunit.javascript.host.dom;
-import java.util.Arrays;
-import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.htmlunit.WebClient;
@@ -88,10 +88,7 @@ public int getLength() {
return 0;
}
- final String[] parts = StringUtils.split(value, WHITESPACE_CHARS);
- final HashSet elements = new HashSet<>(parts.length);
- elements.addAll(Arrays.asList(parts));
- return elements.size();
+ return split(value).size();
}
private String getAttribValue() {
@@ -134,24 +131,12 @@ public void add(final String token) {
throw JavaScriptEngine.reportRuntimeError("Empty input not allowed");
}
- String value = getAttribValue();
- if (StringUtils.isEmpty(value)) {
- value = token;
- }
- else {
- value = String.join(" ", StringUtils.split(value, WHITESPACE_CHARS));
- if (position(value, token) < 0) {
- if (value.length() != 0 && !isWhitespace(value.charAt(value.length() - 1))) {
- value = value + " ";
- }
- value = value + token;
- }
- else {
- value = String.join(" ", StringUtils.split(value, WHITESPACE_CHARS));
- }
+ final List parts = split(getAttribValue());
+ if (!parts.contains(token)) {
+ parts.add(token);
}
+ updateAttribute(String.join(" ", parts));
- updateAttribute(value);
}
/**
@@ -167,39 +152,43 @@ public void remove(final String token) {
throw JavaScriptEngine.reportRuntimeError("Empty input not allowed");
}
- final String oldValue = getAttribValue();
- if (oldValue == null) {
- return;
- }
-
- String value = String.join(" ", StringUtils.split(oldValue, WHITESPACE_CHARS));
- int pos = position(value, token);
- while (pos != -1) {
- int from = pos;
- int to = pos + token.length();
+ final List parts = split(getAttribValue());
+ parts.remove(token);
+ updateAttribute(String.join(" ", parts));
+ }
- while (from > 0 && isWhitespace(value.charAt(from - 1))) {
- from = from - 1;
- }
- while (to < value.length() - 1 && isWhitespace(value.charAt(to))) {
- to = to + 1;
- }
+ /**
+ * Replaces an existing token with a new token. If the first token doesn't exist, replace()
+ * returns false immediately, without adding the new token to the token list.
+ * @param oldToken a string representing the token you want to replace
+ * @param newToken a string representing the token you want to replace oldToken with
+ * @return true if oldToken was successfully replaced, or false if not
+ */
+ @JsxFunction
+ public boolean replace(final String oldToken, final String newToken) {
+ if (StringUtils.isEmpty(oldToken)) {
+ throw JavaScriptEngine.reportRuntimeError("Empty oldToken not allowed");
+ }
+ if (StringUtils.containsAny(oldToken, WHITESPACE_CHARS)) {
+ throw JavaScriptEngine.reportRuntimeError("oldToken contains whitespace");
+ }
- final StringBuilder result = new StringBuilder();
- if (from > 0) {
- result.append(value, 0, from);
- if (to < value.length()) {
- result.append(' ');
- }
- }
- result.append(value, to, value.length());
- value = result.toString();
+ if (StringUtils.isEmpty(newToken)) {
+ throw JavaScriptEngine.reportRuntimeError("Empty newToken not allowed");
+ }
+ if (StringUtils.containsAny(newToken, WHITESPACE_CHARS)) {
+ throw JavaScriptEngine.reportRuntimeError("newToken contains whitespace");
+ }
- pos = position(value, token);
+ final List parts = split(getAttribValue());
+ final int pos = parts.indexOf(oldToken);
+ while (pos == -1) {
+ return false;
}
- value = String.join(" ", StringUtils.split(value, WHITESPACE_CHARS));
- updateAttribute(value);
+ parts.set(pos, newToken);
+ updateAttribute(String.join(" ", parts));
+ return true;
}
/**
@@ -235,13 +224,8 @@ public boolean contains(final String token) {
throw JavaScriptEngine.reportRuntimeError("Empty input not allowed");
}
- String value = getAttribValue();
- if (StringUtils.isEmpty(value)) {
- return false;
- }
-
- value = String.join(" ", StringUtils.split(value, WHITESPACE_CHARS));
- return position(value, token) > -1;
+ final List parts = split(getAttribValue());
+ return parts.contains(token);
}
/**
@@ -260,9 +244,9 @@ public Object item(final int index) {
return null;
}
- final String[] values = StringUtils.split(value, WHITESPACE_CHARS);
- if (index < values.length) {
- return values[index];
+ final List parts = split(getAttribValue());
+ if (index < parts.size()) {
+ return parts.get(index);
}
return null;
@@ -289,12 +273,12 @@ public Object[] getIds() {
return normalIds;
}
- final String[] values = StringUtils.split(value, WHITESPACE_CHARS);
- final Object[] ids = new Object[values.length + normalIds.length];
- for (int i = 0; i < values.length; i++) {
+ final List parts = split(getAttribValue());
+ final Object[] ids = new Object[parts.size() + normalIds.length];
+ for (int i = 0; i < parts.size(); i++) {
ids[i] = i;
}
- System.arraycopy(normalIds, 0, ids, values.length, normalIds.length);
+ System.arraycopy(normalIds, 0, ids, parts.size(), normalIds.length);
return ids;
}
@@ -340,9 +324,9 @@ public void forEach(final Object callback) {
final ContextAction