aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/org/jsoup/nodes/Attributes.java
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/org/jsoup/nodes/Attributes.java')
-rw-r--r--server/src/org/jsoup/nodes/Attributes.java249
1 files changed, 249 insertions, 0 deletions
diff --git a/server/src/org/jsoup/nodes/Attributes.java b/server/src/org/jsoup/nodes/Attributes.java
new file mode 100644
index 0000000000..9436750fc9
--- /dev/null
+++ b/server/src/org/jsoup/nodes/Attributes.java
@@ -0,0 +1,249 @@
+package org.jsoup.nodes;
+
+import org.jsoup.helper.Validate;
+
+import java.util.*;
+
+/**
+ * The attributes of an Element.
+ * <p/>
+ * Attributes are treated as a map: there can be only one value associated with an attribute key.
+ * <p/>
+ * Attribute key and value comparisons are done case insensitively, and keys are normalised to
+ * lower-case.
+ *
+ * @author Jonathan Hedley, jonathan@hedley.net
+ */
+public class Attributes implements Iterable<Attribute>, Cloneable {
+ protected static final String dataPrefix = "data-";
+
+ private LinkedHashMap<String, Attribute> attributes = null;
+ // linked hash map to preserve insertion order.
+ // null be default as so many elements have no attributes -- saves a good chunk of memory
+
+ /**
+ Get an attribute value by key.
+ @param key the attribute key
+ @return the attribute value if set; or empty string if not set.
+ @see #hasKey(String)
+ */
+ public String get(String key) {
+ Validate.notEmpty(key);
+
+ if (attributes == null)
+ return "";
+
+ Attribute attr = attributes.get(key.toLowerCase());
+ return attr != null ? attr.getValue() : "";
+ }
+
+ /**
+ Set a new attribute, or replace an existing one by key.
+ @param key attribute key
+ @param value attribute value
+ */
+ public void put(String key, String value) {
+ Attribute attr = new Attribute(key, value);
+ put(attr);
+ }
+
+ /**
+ Set a new attribute, or replace an existing one by key.
+ @param attribute attribute
+ */
+ public void put(Attribute attribute) {
+ Validate.notNull(attribute);
+ if (attributes == null)
+ attributes = new LinkedHashMap<String, Attribute>(2);
+ attributes.put(attribute.getKey(), attribute);
+ }
+
+ /**
+ Remove an attribute by key.
+ @param key attribute key to remove
+ */
+ public void remove(String key) {
+ Validate.notEmpty(key);
+ if (attributes == null)
+ return;
+ attributes.remove(key.toLowerCase());
+ }
+
+ /**
+ Tests if these attributes contain an attribute with this key.
+ @param key key to check for
+ @return true if key exists, false otherwise
+ */
+ public boolean hasKey(String key) {
+ return attributes != null && attributes.containsKey(key.toLowerCase());
+ }
+
+ /**
+ Get the number of attributes in this set.
+ @return size
+ */
+ public int size() {
+ if (attributes == null)
+ return 0;
+ return attributes.size();
+ }
+
+ /**
+ Add all the attributes from the incoming set to this set.
+ @param incoming attributes to add to these attributes.
+ */
+ public void addAll(Attributes incoming) {
+ if (incoming.size() == 0)
+ return;
+ if (attributes == null)
+ attributes = new LinkedHashMap<String, Attribute>(incoming.size());
+ attributes.putAll(incoming.attributes);
+ }
+
+ public Iterator<Attribute> iterator() {
+ return asList().iterator();
+ }
+
+ /**
+ Get the attributes as a List, for iteration. Do not modify the keys of the attributes via this view, as changes
+ to keys will not be recognised in the containing set.
+ @return an view of the attributes as a List.
+ */
+ public List<Attribute> asList() {
+ if (attributes == null)
+ return Collections.emptyList();
+
+ List<Attribute> list = new ArrayList<Attribute>(attributes.size());
+ for (Map.Entry<String, Attribute> entry : attributes.entrySet()) {
+ list.add(entry.getValue());
+ }
+ return Collections.unmodifiableList(list);
+ }
+
+ /**
+ * Retrieves a filtered view of attributes that are HTML5 custom data attributes; that is, attributes with keys
+ * starting with {@code data-}.
+ * @return map of custom data attributes.
+ */
+ public Map<String, String> dataset() {
+ return new Dataset();
+ }
+
+ /**
+ Get the HTML representation of these attributes.
+ @return HTML
+ */
+ public String html() {
+ StringBuilder accum = new StringBuilder();
+ html(accum, (new Document("")).outputSettings()); // output settings a bit funky, but this html() seldom used
+ return accum.toString();
+ }
+
+ void html(StringBuilder accum, Document.OutputSettings out) {
+ if (attributes == null)
+ return;
+
+ for (Map.Entry<String, Attribute> entry : attributes.entrySet()) {
+ Attribute attribute = entry.getValue();
+ accum.append(" ");
+ attribute.html(accum, out);
+ }
+ }
+
+ public String toString() {
+ return html();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Attributes)) return false;
+
+ Attributes that = (Attributes) o;
+
+ if (attributes != null ? !attributes.equals(that.attributes) : that.attributes != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return attributes != null ? attributes.hashCode() : 0;
+ }
+
+ @Override
+ public Attributes clone() {
+ if (attributes == null)
+ return new Attributes();
+
+ Attributes clone;
+ try {
+ clone = (Attributes) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException(e);
+ }
+ clone.attributes = new LinkedHashMap<String, Attribute>(attributes.size());
+ for (Attribute attribute: this)
+ clone.attributes.put(attribute.getKey(), attribute.clone());
+ return clone;
+ }
+
+ private class Dataset extends AbstractMap<String, String> {
+
+ private Dataset() {
+ if (attributes == null)
+ attributes = new LinkedHashMap<String, Attribute>(2);
+ }
+
+ public Set<Entry<String, String>> entrySet() {
+ return new EntrySet();
+ }
+
+ @Override
+ public String put(String key, String value) {
+ String dataKey = dataKey(key);
+ String oldValue = hasKey(dataKey) ? attributes.get(dataKey).getValue() : null;
+ Attribute attr = new Attribute(dataKey, value);
+ attributes.put(dataKey, attr);
+ return oldValue;
+ }
+
+ private class EntrySet extends AbstractSet<Map.Entry<String, String>> {
+ public Iterator<Map.Entry<String, String>> iterator() {
+ return new DatasetIterator();
+ }
+
+ public int size() {
+ int count = 0;
+ Iterator iter = new DatasetIterator();
+ while (iter.hasNext())
+ count++;
+ return count;
+ }
+ }
+
+ private class DatasetIterator implements Iterator<Map.Entry<String, String>> {
+ private Iterator<Attribute> attrIter = attributes.values().iterator();
+ private Attribute attr;
+ public boolean hasNext() {
+ while (attrIter.hasNext()) {
+ attr = attrIter.next();
+ if (attr.isDataAttribute()) return true;
+ }
+ return false;
+ }
+
+ public Entry<String, String> next() {
+ return new Attribute(attr.getKey().substring(dataPrefix.length()), attr.getValue());
+ }
+
+ public void remove() {
+ attributes.remove(attr.getKey());
+ }
+ }
+ }
+
+ private static String dataKey(String key) {
+ return dataPrefix + key;
+ }
+}