Merge from mainline.
[official-gcc.git] / libjava / classpath / javax / swing / text / StyleContext.java
blob8ef34400d29538d96decd7dfdbb54dfd8e9c0674
1 /* StyleContext.java --
2 Copyright (C) 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.text;
41 import gnu.classpath.NotImplementedException;
43 import java.awt.Color;
44 import java.awt.Font;
45 import java.awt.FontMetrics;
46 import java.awt.Toolkit;
47 import java.io.IOException;
48 import java.io.ObjectInputStream;
49 import java.io.ObjectOutputStream;
50 import java.io.Serializable;
51 import java.util.Enumeration;
52 import java.util.EventListener;
53 import java.util.HashSet;
54 import java.util.Hashtable;
56 import javax.swing.event.ChangeEvent;
57 import javax.swing.event.ChangeListener;
58 import javax.swing.event.EventListenerList;
60 public class StyleContext
61 implements Serializable, AbstractDocument.AttributeContext
63 /** The serialization UID (compatible with JDK1.5). */
64 private static final long serialVersionUID = 8042858831190784241L;
66 public class NamedStyle
67 implements Serializable, Style
69 /** The serialization UID (compatible with JDK1.5). */
70 private static final long serialVersionUID = -6690628971806226374L;
72 protected ChangeEvent changeEvent;
73 protected EventListenerList listenerList;
75 AttributeSet attributes;
76 String name;
78 public NamedStyle()
80 this(null, null);
83 public NamedStyle(Style parent)
85 this(null, parent);
88 public NamedStyle(String name, Style parent)
90 this.name = name;
91 this.attributes = getEmptySet();
92 this.changeEvent = new ChangeEvent(this);
93 this.listenerList = new EventListenerList();
94 setResolveParent(parent);
97 public String getName()
99 return name;
102 public void setName(String n)
104 name = n;
105 fireStateChanged();
108 public void addChangeListener(ChangeListener l)
110 listenerList.add(ChangeListener.class, l);
113 public void removeChangeListener(ChangeListener l)
115 listenerList.remove(ChangeListener.class, l);
118 public EventListener[] getListeners(Class listenerType)
120 return listenerList.getListeners(listenerType);
123 public ChangeListener[] getChangeListeners()
125 return (ChangeListener[]) getListeners(ChangeListener.class);
128 protected void fireStateChanged()
130 ChangeListener[] listeners = getChangeListeners();
131 for (int i = 0; i < listeners.length; ++i)
133 listeners[i].stateChanged(changeEvent);
137 public void addAttribute(Object name, Object value)
139 attributes = StyleContext.this.addAttribute(attributes, name, value);
140 fireStateChanged();
143 public void addAttributes(AttributeSet attr)
145 attributes = StyleContext.this.addAttributes(attributes, attr);
146 fireStateChanged();
149 public boolean containsAttribute(Object name, Object value)
151 return attributes.containsAttribute(name, value);
154 public boolean containsAttributes(AttributeSet attrs)
156 return attributes.containsAttributes(attrs);
159 public AttributeSet copyAttributes()
161 return attributes.copyAttributes();
164 public Object getAttribute(Object attrName)
166 return attributes.getAttribute(attrName);
169 public int getAttributeCount()
171 return attributes.getAttributeCount();
174 public Enumeration getAttributeNames()
176 return attributes.getAttributeNames();
179 public boolean isDefined(Object attrName)
181 return attributes.isDefined(attrName);
184 public boolean isEqual(AttributeSet attr)
186 return attributes.isEqual(attr);
189 public void removeAttribute(Object name)
191 attributes = StyleContext.this.removeAttribute(attributes, name);
192 fireStateChanged();
195 public void removeAttributes(AttributeSet attrs)
197 attributes = StyleContext.this.removeAttributes(attributes, attrs);
198 fireStateChanged();
201 public void removeAttributes(Enumeration names)
203 attributes = StyleContext.this.removeAttributes(attributes, names);
204 fireStateChanged();
208 public AttributeSet getResolveParent()
210 return attributes.getResolveParent();
213 public void setResolveParent(AttributeSet parent)
215 if (parent != null)
217 attributes = StyleContext.this.addAttribute
218 (attributes, ResolveAttribute, parent);
220 fireStateChanged();
223 public String toString()
225 return ("[NamedStyle: name=" + name + ", attrs=" + attributes.toString() + "]");
229 public class SmallAttributeSet
230 implements AttributeSet
232 final Object [] attrs;
233 public SmallAttributeSet(AttributeSet a)
235 if (a == null)
236 attrs = new Object[0];
237 else
239 int n = a.getAttributeCount();
240 int i = 0;
241 attrs = new Object[n * 2];
242 Enumeration e = a.getAttributeNames();
243 while (e.hasMoreElements())
245 Object name = e.nextElement();
246 attrs[i++] = name;
247 attrs[i++] = a.getAttribute(name);
252 public SmallAttributeSet(Object [] a)
254 if (a == null)
255 attrs = new Object[0];
256 else
258 attrs = new Object[a.length];
259 System.arraycopy(a, 0, attrs, 0, a.length);
263 public Object clone()
265 return new SmallAttributeSet(this.attrs);
268 public boolean containsAttribute(Object name, Object value)
270 for (int i = 0; i < attrs.length; i += 2)
272 if (attrs[i].equals(name) &&
273 attrs[i+1].equals(value))
274 return true;
276 return false;
279 public boolean containsAttributes(AttributeSet a)
281 Enumeration e = a.getAttributeNames();
282 while (e.hasMoreElements())
284 Object name = e.nextElement();
285 Object val = a.getAttribute(name);
286 if (!containsAttribute(name, val))
287 return false;
289 return true;
292 public AttributeSet copyAttributes()
294 return (AttributeSet) clone();
297 public boolean equals(Object obj)
299 return
300 (obj instanceof AttributeSet)
301 && this.isEqual((AttributeSet)obj);
304 public Object getAttribute(Object key)
306 for (int i = 0; i < attrs.length; i += 2)
308 if (attrs[i].equals(key))
309 return attrs[i+1];
312 // Check the resolve parent, unless we're looking for the
313 // ResolveAttribute, which would cause an infinite loop
314 if (!(key.equals(ResolveAttribute)))
316 Object p = getResolveParent();
317 if (p != null && p instanceof AttributeSet)
318 return (((AttributeSet)p).getAttribute(key));
321 return null;
324 public int getAttributeCount()
326 return attrs.length / 2;
329 public Enumeration getAttributeNames()
331 return new Enumeration()
333 int i = 0;
334 public boolean hasMoreElements()
336 return i < attrs.length;
338 public Object nextElement()
340 i += 2;
341 return attrs[i-2];
346 public AttributeSet getResolveParent()
348 return (AttributeSet) getAttribute(ResolveAttribute);
351 public int hashCode()
353 return java.util.Arrays.asList(attrs).hashCode();
356 public boolean isDefined(Object key)
358 for (int i = 0; i < attrs.length; i += 2)
360 if (attrs[i].equals(key))
361 return true;
363 return false;
366 public boolean isEqual(AttributeSet attr)
368 return getAttributeCount() == attr.getAttributeCount()
369 && this.containsAttributes(attr);
372 public String toString()
374 StringBuffer sb = new StringBuffer();
375 sb.append("[StyleContext.SmallattributeSet:");
376 for (int i = 0; i < attrs.length - 1; ++i)
378 sb.append(" (");
379 sb.append(attrs[i].toString());
380 sb.append("=");
381 sb.append(attrs[i+1].toString());
382 sb.append(")");
384 sb.append("]");
385 return sb.toString();
389 // FIXME: official javadocs suggest that these might be more usefully
390 // implemented using a WeakHashMap, but not sure if that works most
391 // places or whether it really matters anyways.
393 // FIXME: also not sure if these tables ought to be static (singletons),
394 // shared across all StyleContexts. I think so, but it's not clear in
395 // docs. revert to non-shared if you think it matters.
398 * The name of the default style.
400 public static final String DEFAULT_STYLE = "default";
403 * The default style for this style context.
405 NamedStyle defaultStyle = new NamedStyle(DEFAULT_STYLE, null);
407 static Hashtable sharedAttributeSets = new Hashtable();
408 static Hashtable sharedFonts = new Hashtable();
410 static StyleContext defaultStyleContext = new StyleContext();
411 static final int compressionThreshold = 9;
414 * These attribute keys are handled specially in serialization.
416 private static HashSet staticAttributeKeys = new HashSet();
418 EventListenerList listenerList;
419 Hashtable styleTable;
422 * Creates a new instance of the style context. Add the default style
423 * to the style table.
425 public StyleContext()
427 listenerList = new EventListenerList();
428 styleTable = new Hashtable();
429 styleTable.put(DEFAULT_STYLE, defaultStyle);
432 protected SmallAttributeSet createSmallAttributeSet(AttributeSet a)
434 return new SmallAttributeSet(a);
437 protected MutableAttributeSet createLargeAttributeSet(AttributeSet a)
439 return new SimpleAttributeSet(a);
442 public void addChangeListener(ChangeListener listener)
444 listenerList.add(ChangeListener.class, listener);
447 public void removeChangeListener(ChangeListener listener)
449 listenerList.remove(ChangeListener.class, listener);
452 public ChangeListener[] getChangeListeners()
454 return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
457 public Style addStyle(String name, Style parent)
459 Style newStyle = new NamedStyle(name, parent);
460 if (name != null)
461 styleTable.put(name, newStyle);
462 return newStyle;
465 public void removeStyle(String name)
467 styleTable.remove(name);
471 * Get the style from the style table. If the passed name
472 * matches {@link #DEFAULT_STYLE}, returns the default style.
473 * Otherwise returns the previously defined style of
474 * <code>null</code> if the style with the given name is not defined.
476 * @param name the name of the style.
478 * @return the style with the given name or null if no such defined.
480 public Style getStyle(String name)
482 return (Style) styleTable.get(name);
486 * Get the names of the style. The returned enumeration always
487 * contains at least one member, the default style.
489 public Enumeration getStyleNames()
491 return styleTable.keys();
495 // StyleContexts only understand the "simple" model of fonts present in
496 // pre-java2d systems: fonts are a family name, a size (integral number
497 // of points), and a mask of style parameters (plain, bold, italic, or
498 // bold|italic). We have an inner class here called SimpleFontSpec which
499 // holds such triples.
501 // A SimpleFontSpec can be built for *any* AttributeSet because the size,
502 // family, and style keys in an AttributeSet have default values (defined
503 // over in StyleConstants).
505 // We keep a static cache mapping SimpleFontSpecs to java.awt.Fonts, so
506 // that we reuse Fonts between styles and style contexts.
509 private static class SimpleFontSpec
511 String family;
512 int style;
513 int size;
514 public SimpleFontSpec(String family,
515 int style,
516 int size)
518 this.family = family;
519 this.style = style;
520 this.size = size;
522 public boolean equals(Object obj)
524 return (obj != null)
525 && (obj instanceof SimpleFontSpec)
526 && (((SimpleFontSpec)obj).family.equals(this.family))
527 && (((SimpleFontSpec)obj).style == this.style)
528 && (((SimpleFontSpec)obj).size == this.size);
530 public int hashCode()
532 return family.hashCode() + style + size;
536 public Font getFont(AttributeSet attr)
538 String family = StyleConstants.getFontFamily(attr);
539 int style = Font.PLAIN;
540 if (StyleConstants.isBold(attr))
541 style += Font.BOLD;
542 if (StyleConstants.isItalic(attr))
543 style += Font.ITALIC;
544 int size = StyleConstants.getFontSize(attr);
545 return getFont(family, style, size);
548 public Font getFont(String family, int style, int size)
550 SimpleFontSpec spec = new SimpleFontSpec(family, style, size);
551 if (sharedFonts.containsKey(spec))
552 return (Font) sharedFonts.get(spec);
553 else
555 Font tmp = new Font(family, style, size);
556 sharedFonts.put(spec, tmp);
557 return tmp;
561 public FontMetrics getFontMetrics(Font f)
563 return Toolkit.getDefaultToolkit().getFontMetrics(f);
566 public Color getForeground(AttributeSet a)
568 return StyleConstants.getForeground(a);
571 public Color getBackground(AttributeSet a)
573 return StyleConstants.getBackground(a);
576 protected int getCompressionThreshold()
578 return compressionThreshold;
581 public static StyleContext getDefaultStyleContext()
583 return defaultStyleContext;
586 public AttributeSet addAttribute(AttributeSet old, Object name, Object value)
588 if (old instanceof MutableAttributeSet)
590 ((MutableAttributeSet)old).addAttribute(name, value);
591 return old;
593 else
595 MutableAttributeSet mutable = createLargeAttributeSet(old);
596 mutable.addAttribute(name, value);
597 if (mutable.getAttributeCount() >= getCompressionThreshold())
598 return mutable;
599 else
601 SmallAttributeSet small = createSmallAttributeSet(mutable);
602 if (sharedAttributeSets.containsKey(small))
603 small = (SmallAttributeSet) sharedAttributeSets.get(small);
604 else
605 sharedAttributeSets.put(small,small);
606 return small;
611 public AttributeSet addAttributes(AttributeSet old, AttributeSet attributes)
613 if (old instanceof MutableAttributeSet)
615 ((MutableAttributeSet)old).addAttributes(attributes);
616 return old;
618 else
620 MutableAttributeSet mutable = createLargeAttributeSet(old);
621 mutable.addAttributes(attributes);
622 if (mutable.getAttributeCount() >= getCompressionThreshold())
623 return mutable;
624 else
626 SmallAttributeSet small = createSmallAttributeSet(mutable);
627 if (sharedAttributeSets.containsKey(small))
628 small = (SmallAttributeSet) sharedAttributeSets.get(small);
629 else
630 sharedAttributeSets.put(small,small);
631 return small;
636 public AttributeSet getEmptySet()
638 AttributeSet e = createSmallAttributeSet(null);
639 if (sharedAttributeSets.containsKey(e))
640 e = (AttributeSet) sharedAttributeSets.get(e);
641 else
642 sharedAttributeSets.put(e, e);
643 return e;
646 public void reclaim(AttributeSet attributes)
648 if (sharedAttributeSets.containsKey(attributes))
649 sharedAttributeSets.remove(attributes);
652 public AttributeSet removeAttribute(AttributeSet old, Object name)
654 if (old instanceof MutableAttributeSet)
656 ((MutableAttributeSet)old).removeAttribute(name);
657 if (old.getAttributeCount() < getCompressionThreshold())
659 SmallAttributeSet small = createSmallAttributeSet(old);
660 if (!sharedAttributeSets.containsKey(small))
661 sharedAttributeSets.put(small,small);
662 old = (AttributeSet) sharedAttributeSets.get(small);
664 return old;
666 else
668 MutableAttributeSet mutable = createLargeAttributeSet(old);
669 mutable.removeAttribute(name);
670 SmallAttributeSet small = createSmallAttributeSet(mutable);
671 if (sharedAttributeSets.containsKey(small))
672 small = (SmallAttributeSet) sharedAttributeSets.get(small);
673 else
674 sharedAttributeSets.put(small,small);
675 return small;
679 public AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes)
681 return removeAttributes(old, attributes.getAttributeNames());
684 public AttributeSet removeAttributes(AttributeSet old, Enumeration names)
686 if (old instanceof MutableAttributeSet)
688 ((MutableAttributeSet)old).removeAttributes(names);
689 if (old.getAttributeCount() < getCompressionThreshold())
691 SmallAttributeSet small = createSmallAttributeSet(old);
692 if (!sharedAttributeSets.containsKey(small))
693 sharedAttributeSets.put(small,small);
694 old = (AttributeSet) sharedAttributeSets.get(small);
696 return old;
698 else
700 MutableAttributeSet mutable = createLargeAttributeSet(old);
701 mutable.removeAttributes(names);
702 SmallAttributeSet small = createSmallAttributeSet(mutable);
703 if (sharedAttributeSets.containsKey(small))
704 small = (SmallAttributeSet) sharedAttributeSets.get(small);
705 else
706 sharedAttributeSets.put(small,small);
707 return small;
712 // FIXME: there's some sort of quasi-serialization stuff in here which I
713 // have left incomplete; I'm not sure I understand the intent properly.
715 public static Object getStaticAttribute(Object key)
716 throws NotImplementedException
718 throw new InternalError("not implemented");
721 public static Object getStaticAttributeKey(Object key)
722 throws NotImplementedException
724 throw new InternalError("not implemented");
727 public static void readAttributeSet(ObjectInputStream in, MutableAttributeSet a)
728 throws ClassNotFoundException, IOException, NotImplementedException
730 throw new InternalError("not implemented");
733 public static void writeAttributeSet(ObjectOutputStream out, AttributeSet a)
734 throws IOException, NotImplementedException
736 throw new InternalError("not implemented");
739 public void readAttributes(ObjectInputStream in, MutableAttributeSet a)
740 throws ClassNotFoundException, IOException, NotImplementedException
742 throw new InternalError("not implemented");
745 public void writeAttributes(ObjectOutputStream out, AttributeSet a)
746 throws IOException, NotImplementedException
748 throw new InternalError("not implemented");
752 * Registers an attribute key as a well-known keys. When an attribute with
753 * such a key is written to a stream,, a special syntax is used so that it
754 * can be recognized when it is read back in. All attribute keys defined
755 * in <code>StyleContext</code> are registered as static keys. If you define
756 * additional attribute keys that you want to exist as nonreplicated objects,
757 * then you should register them using this method.
759 * @param key the key to register as static attribute key
761 public static void registerStaticAttributeKey(Object key)
763 staticAttributeKeys.add(key);