1 /* 2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing.text; 26 27 import java.util.Hashtable; 28 import java.util.Enumeration; 29 import java.util.Collections; 30 import java.io.IOException; 31 import java.io.ObjectInputStream; 32 import java.io.Serializable; 33 import java.util.AbstractMap; 34 import java.util.LinkedHashMap; 35 36 /** 37 * A straightforward implementation of MutableAttributeSet using a 38 * hash table. 39 * <p> 40 * <strong>Warning:</strong> 41 * Serialized objects of this class will not be compatible with 42 * future Swing releases. The current serialization support is 43 * appropriate for short term storage or RMI between applications running 44 * the same version of Swing. As of 1.4, support for long term storage 45 * of all JavaBeans™ 46 * has been added to the <code>java.beans</code> package. 47 * Please see {@link java.beans.XMLEncoder}. 48 * 49 * @author Tim Prinzing 50 */ 51 @SuppressWarnings("serial") // Same-version serialization only 52 public class SimpleAttributeSet implements MutableAttributeSet, Serializable, Cloneable 53 { 54 private static final long serialVersionUID = -6631553454711782652L; 55 56 /** 57 * An empty attribute set. 58 */ 59 public static final AttributeSet EMPTY = new EmptyAttributeSet(); 60 61 private transient LinkedHashMap<Object, Object> table = new LinkedHashMap<>(3); 62 63 /** 64 * Creates a new attribute set. 65 */ 66 public SimpleAttributeSet() { 67 } 68 69 /** 70 * Creates a new attribute set based on a supplied set of attributes. 71 * 72 * @param source the set of attributes 73 */ 74 public SimpleAttributeSet(AttributeSet source) { 75 addAttributes(source); 76 } 77 78 /** 79 * Checks whether the set of attributes is empty. 80 * 81 * @return true if the set is empty else false 82 */ 83 public boolean isEmpty() 84 { 85 return table.isEmpty(); 86 } 87 88 /** 89 * Gets a count of the number of attributes. 90 * 91 * @return the count 92 */ 93 public int getAttributeCount() { 94 return table.size(); 95 } 96 97 /** 98 * Tells whether a given attribute is defined. 99 * 100 * @param attrName the attribute name 101 * @return true if the attribute is defined 102 */ 103 public boolean isDefined(Object attrName) { 104 return table.containsKey(attrName); 105 } 106 107 /** 108 * Compares two attribute sets. 109 * 110 * @param attr the second attribute set 111 * @return true if the sets are equal, false otherwise 112 */ 113 public boolean isEqual(AttributeSet attr) { 114 return ((getAttributeCount() == attr.getAttributeCount()) && 115 containsAttributes(attr)); 116 } 117 118 /** 119 * Makes a copy of the attributes. 120 * 121 * @return the copy 122 */ 123 public AttributeSet copyAttributes() { 124 return (AttributeSet) clone(); 125 } 126 127 /** 128 * Gets the names of the attributes in the set. 129 * 130 * @return the names as an <code>Enumeration</code> 131 */ 132 public Enumeration<?> getAttributeNames() { 133 return Collections.enumeration(table.keySet()); 134 } 135 136 /** 137 * Gets the value of an attribute. 138 * 139 * @param name the attribute name 140 * @return the value 141 */ 142 public Object getAttribute(Object name) { 143 Object value = table.get(name); 144 if (value == null) { 145 AttributeSet parent = getResolveParent(); 146 if (parent != null) { 147 value = parent.getAttribute(name); 148 } 149 } 150 return value; 151 } 152 153 /** 154 * Checks whether the attribute list contains a 155 * specified attribute name/value pair. 156 * 157 * @param name the name 158 * @param value the value 159 * @return true if the name/value pair is in the list 160 */ 161 public boolean containsAttribute(Object name, Object value) { 162 return value.equals(getAttribute(name)); 163 } 164 165 /** 166 * Checks whether the attribute list contains all the 167 * specified name/value pairs. 168 * 169 * @param attributes the attribute list 170 * @return true if the list contains all the name/value pairs 171 */ 172 public boolean containsAttributes(AttributeSet attributes) { 173 boolean result = true; 174 175 Enumeration names = attributes.getAttributeNames(); 176 while (result && names.hasMoreElements()) { 177 Object name = names.nextElement(); 178 result = attributes.getAttribute(name).equals(getAttribute(name)); 179 } 180 181 return result; 182 } 183 184 /** 185 * Adds an attribute to the list. 186 * 187 * @param name the attribute name 188 * @param value the attribute value 189 */ 190 public void addAttribute(Object name, Object value) { 191 table.put(name, value); 192 } 193 194 /** 195 * Adds a set of attributes to the list. 196 * 197 * @param attributes the set of attributes to add 198 */ 199 public void addAttributes(AttributeSet attributes) { 200 Enumeration names = attributes.getAttributeNames(); 201 while (names.hasMoreElements()) { 202 Object name = names.nextElement(); 203 addAttribute(name, attributes.getAttribute(name)); 204 } 205 } 206 207 /** 208 * Removes an attribute from the list. 209 * 210 * @param name the attribute name 211 */ 212 public void removeAttribute(Object name) { 213 table.remove(name); 214 } 215 216 /** 217 * Removes a set of attributes from the list. 218 * 219 * @param names the set of names to remove 220 */ 221 public void removeAttributes(Enumeration<?> names) { 222 while (names.hasMoreElements()) 223 removeAttribute(names.nextElement()); 224 } 225 226 /** 227 * Removes a set of attributes from the list. 228 * 229 * @param attributes the set of attributes to remove 230 */ 231 public void removeAttributes(AttributeSet attributes) { 232 if (attributes == this) { 233 table.clear(); 234 } 235 else { 236 Enumeration names = attributes.getAttributeNames(); 237 while (names.hasMoreElements()) { 238 Object name = names.nextElement(); 239 Object value = attributes.getAttribute(name); 240 if (value.equals(getAttribute(name))) 241 removeAttribute(name); 242 } 243 } 244 } 245 246 /** 247 * Gets the resolving parent. This is the set 248 * of attributes to resolve through if an attribute 249 * isn't defined locally. This is null if there 250 * are no other sets of attributes to resolve 251 * through. 252 * 253 * @return the parent 254 */ 255 public AttributeSet getResolveParent() { 256 return (AttributeSet) table.get(StyleConstants.ResolveAttribute); 257 } 258 259 /** 260 * Sets the resolving parent. 261 * 262 * @param parent the parent 263 */ 264 public void setResolveParent(AttributeSet parent) { 265 addAttribute(StyleConstants.ResolveAttribute, parent); 266 } 267 268 // --- Object methods --------------------------------- 269 270 /** 271 * Clones a set of attributes. 272 * 273 * @return the new set of attributes 274 */ 275 public Object clone() { 276 SimpleAttributeSet attr; 277 try { 278 attr = (SimpleAttributeSet) super.clone(); 279 attr.table = (LinkedHashMap) table.clone(); 280 } catch (CloneNotSupportedException cnse) { 281 attr = null; 282 } 283 return attr; 284 } 285 286 /** 287 * Returns a hashcode for this set of attributes. 288 * @return a hashcode value for this set of attributes. 289 */ 290 public int hashCode() { 291 return table.hashCode(); 292 } 293 294 /** 295 * Compares this object to the specified object. 296 * The result is <code>true</code> if the object is an equivalent 297 * set of attributes. 298 * @param obj the object to compare this attribute set with 299 * @return <code>true</code> if the objects are equal; 300 * <code>false</code> otherwise 301 */ 302 public boolean equals(Object obj) { 303 if (this == obj) { 304 return true; 305 } 306 if (obj instanceof AttributeSet) { 307 AttributeSet attrs = (AttributeSet) obj; 308 return isEqual(attrs); 309 } 310 return false; 311 } 312 313 /** 314 * Converts the attribute set to a String. 315 * 316 * @return the string 317 */ 318 public String toString() { 319 String s = ""; 320 Enumeration names = getAttributeNames(); 321 while (names.hasMoreElements()) { 322 Object key = names.nextElement(); 323 Object value = getAttribute(key); 324 if (value instanceof AttributeSet) { 325 // don't go recursive 326 s = s + key + "=**AttributeSet** "; 327 } else { 328 s = s + key + "=" + value + " "; 329 } 330 } 331 return s; 332 } 333 334 private void writeObject(java.io.ObjectOutputStream s) throws IOException { 335 s.defaultWriteObject(); 336 StyleContext.writeAttributeSet(s, this); 337 } 338 339 private void readObject(ObjectInputStream s) 340 throws ClassNotFoundException, IOException { 341 s.defaultReadObject(); 342 table = new LinkedHashMap<>(3); 343 StyleContext.readAttributeSet(s, this); 344 } 345 346 /** 347 * An AttributeSet that is always empty. 348 */ 349 static class EmptyAttributeSet implements AttributeSet, Serializable { 350 static final long serialVersionUID = -8714803568785904228L; 351 352 public int getAttributeCount() { 353 return 0; 354 } 355 public boolean isDefined(Object attrName) { 356 return false; 357 } 358 public boolean isEqual(AttributeSet attr) { 359 return (attr.getAttributeCount() == 0); 360 } 361 public AttributeSet copyAttributes() { 362 return this; 363 } 364 public Object getAttribute(Object key) { 365 return null; 366 } 367 public Enumeration getAttributeNames() { 368 return Collections.emptyEnumeration(); 369 } 370 public boolean containsAttribute(Object name, Object value) { 371 return false; 372 } 373 public boolean containsAttributes(AttributeSet attributes) { 374 return (attributes.getAttributeCount() == 0); 375 } 376 public AttributeSet getResolveParent() { 377 return null; 378 } 379 public boolean equals(Object obj) { 380 if (this == obj) { 381 return true; 382 } 383 return ((obj instanceof AttributeSet) && 384 (((AttributeSet)obj).getAttributeCount() == 0)); 385 } 386 public int hashCode() { 387 return 0; 388 } 389 } 390 }