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 @SuppressWarnings("unchecked") // Cast of result of clone 276 public Object clone() { 277 SimpleAttributeSet attr; 278 try { 279 attr = (SimpleAttributeSet) super.clone(); 280 attr.table = (LinkedHashMap) table.clone(); 281 } catch (CloneNotSupportedException cnse) { 282 attr = null; 283 } 284 return attr; 285 } 286 287 /** 288 * Returns a hashcode for this set of attributes. 289 * @return a hashcode value for this set of attributes. 290 */ 291 public int hashCode() { 292 return table.hashCode(); 293 } 294 295 /** 296 * Compares this object to the specified object. 297 * The result is <code>true</code> if the object is an equivalent 298 * set of attributes. 299 * @param obj the object to compare this attribute set with 300 * @return <code>true</code> if the objects are equal; 301 * <code>false</code> otherwise 302 */ 303 public boolean equals(Object obj) { 304 if (this == obj) { 305 return true; 306 } 307 if (obj instanceof AttributeSet) { 308 AttributeSet attrs = (AttributeSet) obj; 309 return isEqual(attrs); 310 } 311 return false; 312 } 313 314 /** 315 * Converts the attribute set to a String. 316 * 317 * @return the string 318 */ 319 public String toString() { 320 String s = ""; 321 Enumeration<?> names = getAttributeNames(); 322 while (names.hasMoreElements()) { 323 Object key = names.nextElement(); 324 Object value = getAttribute(key); 325 if (value instanceof AttributeSet) { 326 // don't go recursive 327 s = s + key + "=**AttributeSet** "; 328 } else { 329 s = s + key + "=" + value + " "; 330 } 331 } 332 return s; 333 } 334 335 private void writeObject(java.io.ObjectOutputStream s) throws IOException { 336 s.defaultWriteObject(); 337 StyleContext.writeAttributeSet(s, this); 338 } 339 340 private void readObject(ObjectInputStream s) 341 throws ClassNotFoundException, IOException { 342 s.defaultReadObject(); 343 table = new LinkedHashMap<>(3); 344 StyleContext.readAttributeSet(s, this); 345 } 346 347 /** 348 * An AttributeSet that is always empty. 349 */ 350 static class EmptyAttributeSet implements AttributeSet, Serializable { 351 static final long serialVersionUID = -8714803568785904228L; 352 353 public int getAttributeCount() { 354 return 0; 355 } 356 public boolean isDefined(Object attrName) { 357 return false; 358 } 359 public boolean isEqual(AttributeSet attr) { 360 return (attr.getAttributeCount() == 0); 361 } 362 public AttributeSet copyAttributes() { 363 return this; 364 } 365 public Object getAttribute(Object key) { 366 return null; 367 } 368 public Enumeration<?> getAttributeNames() { 369 return Collections.emptyEnumeration(); 370 } 371 public boolean containsAttribute(Object name, Object value) { 372 return false; 373 } 374 public boolean containsAttributes(AttributeSet attributes) { 375 return (attributes.getAttributeCount() == 0); 376 } 377 public AttributeSet getResolveParent() { 378 return null; 379 } 380 public boolean equals(Object obj) { 381 if (this == obj) { 382 return true; 383 } 384 return ((obj instanceof AttributeSet) && 385 (((AttributeSet)obj).getAttributeCount() == 0)); 386 } 387 public int hashCode() { 388 return 0; 389 } 390 } 391 }