001 /* SimpleAttributeSet.java --
002 Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package javax.swing.text;
040
041 import java.io.Serializable;
042 import java.util.Enumeration;
043 import java.util.Hashtable;
044
045 /**
046 * A set of attributes.
047 */
048 public class SimpleAttributeSet
049 implements MutableAttributeSet, Serializable, Cloneable
050 {
051 /** The serialization UID (compatible with JDK1.5). */
052 private static final long serialVersionUID = 8267656273837665219L;
053
054 /**
055 * An empty attribute set.
056 */
057 public static final AttributeSet EMPTY = new EmptyAttributeSet();
058
059 /** Storage for the attributes. */
060 Hashtable tab;
061
062 /**
063 * Creates a new attribute set that is initially empty.
064 */
065 public SimpleAttributeSet()
066 {
067 tab = new Hashtable();
068 }
069
070 /**
071 * Creates a new <code>SimpleAttributeSet</code> with the same attributes
072 * and resolve parent as the specified set.
073 *
074 * @param a the attributes (<code>null</code> not permitted).
075 *
076 * @throws NullPointerException if <code>a</code> is <code>null</code>.
077 */
078 public SimpleAttributeSet(AttributeSet a)
079 {
080 tab = new Hashtable();
081 addAttributes(a);
082 }
083
084 /**
085 * Adds an attribute with the given <code>name</code> and <code>value</code>
086 * to the set. If the set already contains an attribute with the given
087 * <code>name</code>, the attribute value is updated.
088 *
089 * @param name the attribute name (<code>null</code> not permitted).
090 * @param value the value (<code>null</code> not permitted).
091 *
092 * @throws NullPointerException if either argument is <code>null</code>.
093 */
094 public void addAttribute(Object name, Object value)
095 {
096 tab.put(name, value);
097 }
098
099 /**
100 * Adds all the attributes from <code>attributes</code> to this set.
101 *
102 * @param attributes the set of attributes to add (<code>null</code> not
103 * permitted).
104 *
105 * @throws NullPointerException if <code>attributes</code> is
106 * <code>null</code>.
107 */
108 public void addAttributes(AttributeSet attributes)
109 {
110 Enumeration e = attributes.getAttributeNames();
111 while (e.hasMoreElements())
112 {
113 Object name = e.nextElement();
114 Object val = attributes.getAttribute(name);
115 tab.put(name, val);
116 }
117 }
118
119 /**
120 * Returns a clone of the attribute set.
121 *
122 * @return A clone of the attribute set.
123 */
124 public Object clone()
125 {
126 SimpleAttributeSet attr = null;
127 try
128 {
129 attr = (SimpleAttributeSet) super.clone();
130 attr.tab = (Hashtable) tab.clone();
131 }
132 catch (CloneNotSupportedException ex)
133 {
134 assert false;
135 }
136 return attr;
137 }
138
139 /**
140 * Returns true if the given name and value represent an attribute
141 * found either in this AttributeSet or in its resolve parent hierarchy.
142 * @param name the key for the attribute
143 * @param value the value for the attribute
144 * @return true if the attribute is found here or in this set's resolve
145 * parent hierarchy
146 */
147 public boolean containsAttribute(Object name, Object value)
148 {
149 if (value == null)
150 throw new NullPointerException("Null 'value' argument.");
151 if (tab.containsKey(name))
152 return tab.get(name).equals(value);
153 else
154 {
155 AttributeSet p = getResolveParent();
156 if (p != null)
157 return p.containsAttribute(name, value);
158 else
159 return false;
160 }
161 }
162
163 /**
164 * Returns true if the given name and value are found in this AttributeSet.
165 * Does not check the resolve parent.
166 * @param name the key for the attribute
167 * @param value the value for the attribute
168 * @return true if the attribute is found in this AttributeSet
169 */
170 boolean containsAttributeLocally(Object name, Object value)
171 {
172 return tab.containsKey(name)
173 && tab.get(name).equals(value);
174 }
175
176 /**
177 * Returns <code>true</code> of this <code>AttributeSet</code> contains all
178 * of the specified <code>attributes</code>.
179 *
180 * @param attributes the requested attributes
181 *
182 * @return <code>true</code> of this <code>AttributeSet</code> contains all
183 * of the specified <code>attributes</code>
184 */
185 public boolean containsAttributes(AttributeSet attributes)
186 {
187 Enumeration e = attributes.getAttributeNames();
188 while (e.hasMoreElements())
189 {
190 Object name = e.nextElement();
191 Object val = attributes.getAttribute(name);
192 if (! containsAttribute(name, val))
193 return false;
194 }
195 return true;
196 }
197
198 /**
199 * Creates and returns a copy of this <code>AttributeSet</code>.
200 *
201 * @return a copy of this <code>AttributeSet</code>
202 */
203 public AttributeSet copyAttributes()
204 {
205 return (AttributeSet) clone();
206 }
207
208 /**
209 * Checks this set for equality with an arbitrary object.
210 *
211 * @param obj the object (<code>null</code> permitted).
212 *
213 * @return <code>true</code> if this set is equal to <code>obj</code>, and
214 * <code>false</code> otherwise.
215 */
216 public boolean equals(Object obj)
217 {
218 return
219 (obj instanceof AttributeSet)
220 && this.isEqual((AttributeSet) obj);
221 }
222
223 /**
224 * Returns the value of the specified attribute, or <code>null</code> if
225 * there is no attribute with that name. If the attribute is not defined
226 * directly in this set, the parent hierarchy (if there is one) will be
227 * used.
228 *
229 * @param name the attribute (<code>null</code> not permitted).
230 *
231 * @throws NullPointerException if <code>name</code> is <code>null</code>.
232 */
233 public Object getAttribute(Object name)
234 {
235 Object val = tab.get(name);
236 if (val != null)
237 return val;
238
239 AttributeSet p = getResolveParent();
240 if (p != null)
241 return p.getAttribute(name);
242
243 return null;
244 }
245
246 /**
247 * Returns the number of attributes stored in this set, plus 1 if a parent
248 * has been specified (the reference to the parent is stored as a special
249 * attribute). The attributes stored in the parent do NOT contribute
250 * to the count.
251 *
252 * @return The attribute count.
253 */
254 public int getAttributeCount()
255 {
256 return tab.size();
257 }
258
259 /**
260 * Returns an enumeration of the attribute names.
261 *
262 * @return An enumeration of the attribute names.
263 */
264 public Enumeration<?> getAttributeNames()
265 {
266 return tab.keys();
267 }
268
269 /**
270 * Returns the resolving parent.
271 *
272 * @return The resolving parent (possibly <code>null</code>).
273 *
274 * @see #setResolveParent(AttributeSet)
275 */
276 public AttributeSet getResolveParent()
277 {
278 return (AttributeSet) tab.get(ResolveAttribute);
279 }
280
281 /**
282 * Returns a hash code for this instance.
283 *
284 * @return A hash code.
285 */
286 public int hashCode()
287 {
288 return tab.hashCode();
289 }
290
291 /**
292 * Returns <code>true</code> if the given attribute is defined in this set,
293 * and <code>false</code> otherwise. The parent attribute set is not
294 * checked.
295 *
296 * @param attrName the attribute name (<code>null</code> not permitted).
297 */
298 public boolean isDefined(Object attrName)
299 {
300 return tab.containsKey(attrName);
301 }
302
303 /**
304 * Returns <code>true</code> if the set contains no attributes, and
305 * <code>false</code> otherwise. Note that the resolving parent is
306 * stored as an attribute, so this method will return <code>false</code> if
307 * a resolving parent is set.
308 *
309 * @return <code>true</code> if the set contains no attributes, and
310 * <code>false</code> otherwise.
311 */
312 public boolean isEmpty()
313 {
314 return tab.isEmpty();
315 }
316
317 /**
318 * Returns true if the given set has the same number of attributes
319 * as this set and <code>containsAttributes(attr)</code> returns
320 * <code>true</code>.
321 *
322 * @param attr the attribute set (<code>null</code> not permitted).
323 *
324 * @return A boolean.
325 *
326 * @throws NullPointerException if <code>attr</code> is <code>null</code>.
327 */
328 public boolean isEqual(AttributeSet attr)
329 {
330 return getAttributeCount() == attr.getAttributeCount()
331 && this.containsAttributes(attr);
332 }
333
334 /**
335 * Removes the attribute with the specified <code>name</code>, if this
336 * attribute is defined. This method will only remove an attribute from
337 * this set, not from the resolving parent.
338 *
339 * @param name the attribute name (<code>null</code> not permitted).
340 *
341 * @throws NullPointerException if <code>name</code> is <code>null</code>.
342 */
343 public void removeAttribute(Object name)
344 {
345 tab.remove(name);
346 }
347
348 /**
349 * Removes attributes from this set if they are found in the
350 * given set. Only attributes whose key AND value are removed.
351 * Removes attributes only from this set, not from the resolving parent.
352 * Since the resolving parent is stored as an attribute, if
353 * <code>attributes</code> has the same resolving parent as this set, the
354 * parent will be removed from this set.
355 *
356 * @param attributes the attributes (<code>null</code> not permitted).
357 */
358 public void removeAttributes(AttributeSet attributes)
359 {
360 Enumeration e = attributes.getAttributeNames();
361 while (e.hasMoreElements())
362 {
363 Object name = e.nextElement();
364 Object val = attributes.getAttribute(name);
365 if (containsAttributeLocally(name, val))
366 removeAttribute(name);
367 }
368 }
369
370 /**
371 * Removes the attributes listed in <code>names</code>.
372 *
373 * @param names the attribute names (<code>null</code> not permitted).
374 *
375 * @throws NullPointerException if <code>names</code> is <code>null</code>
376 * or contains any <code>null</code> values.
377 */
378 public void removeAttributes(Enumeration<?> names)
379 {
380 while (names.hasMoreElements())
381 {
382 removeAttribute(names.nextElement());
383 }
384 }
385
386 /**
387 * Sets the reolving parent for this set. When looking up an attribute, if
388 * it is not found in this set, then the resolving parent is also used for
389 * the lookup.
390 * <p>
391 * Note that the parent is stored as an attribute, and will contribute 1 to
392 * the count returned by {@link #getAttributeCount()}.
393 *
394 * @param parent the parent attribute set (<code>null</code> not permitted).
395 *
396 * @throws NullPointerException if <code>parent</code> is <code>null</code>.
397 *
398 * @see #setResolveParent(AttributeSet)
399 */
400 public void setResolveParent(AttributeSet parent)
401 {
402 addAttribute(ResolveAttribute, parent);
403 }
404
405 /**
406 * Returns a string representation of this instance, typically used for
407 * debugging purposes.
408 *
409 * @return A string representation of this instance.
410 */
411 public String toString()
412 {
413 return tab.toString();
414 }
415 }