44import org .jsoup .helper .Validate ;
55
66import java .io .IOException ;
7- import java .util .*;
7+ import java .util .AbstractMap ;
8+ import java .util .AbstractSet ;
9+ import java .util .ArrayList ;
10+ import java .util .Collections ;
11+ import java .util .Iterator ;
12+ import java .util .LinkedHashMap ;
13+ import java .util .List ;
14+ import java .util .Map ;
15+ import java .util .Set ;
816
917/**
1018 * The attributes of an Element.
1119 * <p>
12- * Attributes are treated as a map: there can be only one value associated with an attribute key.
20+ * Attributes are treated as a map: there can be only one value associated with an attribute key/name .
1321 * </p>
1422 * <p>
15- * Attribute key and value comparisons are done case insensitively, and keys are normalised to
16- * lower-case.
23+ * Attribute name and value comparisons are <b>case sensitive</b>. By default for HTML, attribute names are
24+ * normalized to lower-case on parsing. That means you should use lower-case strings when referring to attributes by
25+ * name.
1726 * </p>
18- *
27+ *
1928 * @author Jonathan Hedley, [email protected] 2029 */
2130public class Attributes implements Iterable <Attribute >, Cloneable {
2231 protected static final String dataPrefix = "data-" ;
23-
32+
2433 private LinkedHashMap <String , Attribute > attributes = null ;
2534 // linked hash map to preserve insertion order.
2635 // null be default as so many elements have no attributes -- saves a good chunk of memory
2736
2837 /**
2938 Get an attribute value by key.
30- @param key the attribute key
39+ @param key the (case-sensitive) attribute key
3140 @return the attribute value if set; or empty string if not set.
3241 @see #hasKey(String)
3342 */
@@ -37,10 +46,27 @@ public String get(String key) {
3746 if (attributes == null )
3847 return "" ;
3948
40- Attribute attr = attributes .get (key . toLowerCase () );
49+ Attribute attr = attributes .get (key );
4150 return attr != null ? attr .getValue () : "" ;
4251 }
4352
53+ /**
54+ * Get an attribute's value by case-insensitive key
55+ * @param key the attribute name
56+ * @return the first matching attribute value if set; or empty string if not set.
57+ */
58+ public String getIgnoreCase (String key ) {
59+ Validate .notEmpty (key );
60+ if (attributes == null )
61+ return "" ;
62+
63+ for (String attrKey : attributes .keySet ()) {
64+ if (attrKey .equalsIgnoreCase (key ))
65+ return attributes .get (attrKey ).getValue ();
66+ }
67+ return "" ;
68+ }
69+
4470 /**
4571 Set a new attribute, or replace an existing one by key.
4672 @param key attribute key
@@ -50,7 +76,7 @@ public void put(String key, String value) {
5076 Attribute attr = new Attribute (key , value );
5177 put (attr );
5278 }
53-
79+
5480 /**
5581 Set a new boolean attribute, remove attribute if value is false.
5682 @param key attribute key
@@ -75,23 +101,52 @@ public void put(Attribute attribute) {
75101 }
76102
77103 /**
78- Remove an attribute by key.
104+ Remove an attribute by key. <b>Case sensitive.</b>
79105 @param key attribute key to remove
80106 */
81107 public void remove (String key ) {
82108 Validate .notEmpty (key );
83109 if (attributes == null )
84110 return ;
85- attributes .remove (key .toLowerCase ());
111+ attributes .remove (key );
112+ }
113+
114+ /**
115+ Remove an attribute by key. <b>Case insensitive.</b>
116+ @param key attribute key to remove
117+ */
118+ public void removeIgnoreCase (String key ) {
119+ Validate .notEmpty (key );
120+ if (attributes == null )
121+ return ;
122+ for (String attrKey : attributes .keySet ()) {
123+ if (attrKey .equalsIgnoreCase (key ))
124+ attributes .remove (attrKey );
125+ }
86126 }
87127
88128 /**
89129 Tests if these attributes contain an attribute with this key.
90- @param key key to check for
130+ @param key case-sensitive key to check for
91131 @return true if key exists, false otherwise
92132 */
93133 public boolean hasKey (String key ) {
94- return attributes != null && attributes .containsKey (key .toLowerCase ());
134+ return attributes != null && attributes .containsKey (key );
135+ }
136+
137+ /**
138+ Tests if these attributes contain an attribute with this key.
139+ @param key key to check for
140+ @return true if key exists, false otherwise
141+ */
142+ public boolean hasKeyIgnoreCase (String key ) {
143+ if (attributes == null )
144+ return false ;
145+ for (String attrKey : attributes .keySet ()) {
146+ if (attrKey .equalsIgnoreCase (key ))
147+ return true ;
148+ }
149+ return false ;
95150 }
96151
97152 /**
@@ -115,7 +170,7 @@ public void addAll(Attributes incoming) {
115170 attributes = new LinkedHashMap <String , Attribute >(incoming .size ());
116171 attributes .putAll (incoming .attributes );
117172 }
118-
173+
119174 public Iterator <Attribute > iterator () {
120175 return asList ().iterator ();
121176 }
@@ -159,18 +214,18 @@ public String html() {
159214 }
160215 return accum .toString ();
161216 }
162-
217+
163218 void html (Appendable accum , Document .OutputSettings out ) throws IOException {
164219 if (attributes == null )
165220 return ;
166-
221+
167222 for (Map .Entry <String , Attribute > entry : attributes .entrySet ()) {
168223 Attribute attribute = entry .getValue ();
169224 accum .append (" " );
170225 attribute .html (accum , out );
171226 }
172227 }
173-
228+
174229 @ Override
175230 public String toString () {
176231 return html ();
@@ -185,9 +240,9 @@ public String toString() {
185240 public boolean equals (Object o ) {
186241 if (this == o ) return true ;
187242 if (!(o instanceof Attributes )) return false ;
188-
243+
189244 Attributes that = (Attributes ) o ;
190-
245+
191246 return !(attributes != null ? !attributes .equals (that .attributes ) : that .attributes != null );
192247 }
193248
0 commit comments