Source for javax.swing.text.StyleContext

   1: /* StyleContext.java --
   2:    Copyright (C) 2004 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   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.
  10: 
  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.
  15: 
  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.
  20: 
  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.
  25: 
  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. */
  37: 
  38: 
  39: package javax.swing.text;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Font;
  43: import java.awt.FontMetrics;
  44: import java.awt.Toolkit;
  45: import java.io.IOException;
  46: import java.io.ObjectInputStream;
  47: import java.io.ObjectOutputStream;
  48: import java.io.Serializable;
  49: import java.util.Enumeration;
  50: import java.util.EventListener;
  51: import java.util.Hashtable;
  52: 
  53: import javax.swing.event.ChangeEvent;
  54: import javax.swing.event.ChangeListener;
  55: import javax.swing.event.EventListenerList;
  56: 
  57: public class StyleContext 
  58:     implements Serializable, AbstractDocument.AttributeContext
  59: {
  60:   /** The serialization UID (compatible with JDK1.5). */
  61:   private static final long serialVersionUID = 8042858831190784241L;
  62: 
  63:   public class NamedStyle
  64:     implements Serializable, Style
  65:   {
  66:     /** The serialization UID (compatible with JDK1.5). */
  67:     private static final long serialVersionUID = -6690628971806226374L;
  68: 
  69:     protected ChangeEvent changeEvent;
  70:     protected EventListenerList listenerList;
  71:       
  72:     AttributeSet attributes;
  73:     String name;
  74: 
  75:     public NamedStyle()
  76:     {
  77:       this(null, null);
  78:     }
  79: 
  80:     public NamedStyle(Style parent)
  81:     {
  82:       this(null, parent);
  83:     }
  84: 
  85:     public NamedStyle(String name, Style parent)
  86:     {
  87:       this.name = name;
  88:       this.attributes = getEmptySet();
  89:       this.changeEvent = new ChangeEvent(this);
  90:       this.listenerList = new EventListenerList();
  91:       setResolveParent(parent);
  92:     }
  93: 
  94:     public String getName()
  95:     {
  96:       return name;
  97:     }
  98: 
  99:     public void setName(String n)
 100:     {
 101:       name = n;
 102:       fireStateChanged();
 103:     }
 104: 
 105:     public void addChangeListener(ChangeListener l)
 106:     {
 107:       listenerList.add(ChangeListener.class, l);
 108:     }
 109:       
 110:     public void removeChangeListener(ChangeListener l)
 111:     {
 112:       listenerList.remove(ChangeListener.class, l);
 113:     }
 114:       
 115:     public EventListener[] getListeners(Class listenerType)
 116:     {
 117:       return listenerList.getListeners(listenerType);
 118:     }
 119: 
 120:     public ChangeListener[] getChangeListeners()
 121:     {
 122:       return (ChangeListener[]) getListeners(ChangeListener.class);
 123:     }
 124: 
 125:     protected  void fireStateChanged()
 126:     {
 127:       ChangeListener[] listeners = getChangeListeners();
 128:       for (int i = 0; i < listeners.length; ++i)
 129:         {
 130:           listeners[i].stateChanged(changeEvent);
 131:         }
 132:     }
 133: 
 134:     public void addAttribute(Object name, Object value)
 135:     {
 136:       attributes = StyleContext.this.addAttribute(attributes, name, value);
 137:       fireStateChanged();
 138:     }
 139: 
 140:     public void addAttributes(AttributeSet attr)
 141:     {
 142:       attributes = StyleContext.this.addAttributes(attributes, attr);
 143:       fireStateChanged();
 144:     }
 145: 
 146:     public boolean containsAttribute(Object name, Object value)
 147:     {
 148:       return attributes.containsAttribute(name, value);
 149:     }
 150:       
 151:     public boolean containsAttributes(AttributeSet attrs)
 152:     {
 153:       return attributes.containsAttributes(attrs);
 154:     }
 155: 
 156:     public AttributeSet copyAttributes()
 157:     {
 158:       return attributes.copyAttributes();
 159:     }
 160:             
 161:     public Object getAttribute(Object attrName)
 162:     {
 163:       return attributes.getAttribute(attrName);
 164:     }
 165: 
 166:     public int getAttributeCount()
 167:     {
 168:       return attributes.getAttributeCount();
 169:     }
 170: 
 171:     public Enumeration getAttributeNames()
 172:     {
 173:       return attributes.getAttributeNames();
 174:     }
 175:       
 176:     public boolean isDefined(Object attrName)
 177:     {
 178:       return attributes.isDefined(attrName);        
 179:     }
 180: 
 181:     public boolean isEqual(AttributeSet attr)
 182:     {
 183:       return attributes.isEqual(attr);
 184:     }
 185: 
 186:     public void removeAttribute(Object name)
 187:     {
 188:       attributes = StyleContext.this.removeAttribute(attributes, name);
 189:       fireStateChanged();
 190:     }
 191: 
 192:     public void removeAttributes(AttributeSet attrs)
 193:     {
 194:       attributes = StyleContext.this.removeAttributes(attributes, attrs);
 195:       fireStateChanged();
 196:     }
 197: 
 198:     public void removeAttributes(Enumeration names)
 199:     {
 200:       attributes = StyleContext.this.removeAttributes(attributes, names);
 201:       fireStateChanged();
 202:     }
 203: 
 204: 
 205:     public AttributeSet getResolveParent()
 206:     {
 207:       return attributes.getResolveParent();        
 208:     }
 209: 
 210:     public void setResolveParent(AttributeSet parent)
 211:     {
 212:       if (parent != null)
 213:         {
 214:           attributes = StyleContext.this.addAttribute
 215:             (attributes, ResolveAttribute, parent);
 216:         }
 217:       fireStateChanged();
 218:     }
 219:       
 220:     public String toString()
 221:     {
 222:       return ("[NamedStyle: name=" + name + ", attrs=" + attributes.toString() + "]");
 223:     }      
 224:   }
 225:   
 226:   public class SmallAttributeSet
 227:     implements AttributeSet
 228:   {
 229:     final Object [] attrs;
 230:     public SmallAttributeSet(AttributeSet a)
 231:     {
 232:       if (a == null)
 233:         attrs = new Object[0];
 234:       else
 235:         {
 236:           int n = a.getAttributeCount();
 237:           int i = 0;
 238:           attrs = new Object[n * 2];
 239:           Enumeration e = a.getAttributeNames();
 240:           while (e.hasMoreElements())
 241:             {
 242:               Object name = e.nextElement();
 243:               attrs[i++] = name;
 244:               attrs[i++] = a.getAttribute(name);
 245:             }
 246:         }
 247:     }
 248: 
 249:     public SmallAttributeSet(Object [] a)
 250:     {
 251:       if (a == null)
 252:         attrs = new Object[0];
 253:       else
 254:         {
 255:           attrs = new Object[a.length];
 256:           System.arraycopy(a, 0, attrs, 0, a.length);
 257:         }
 258:     }
 259: 
 260:     public Object clone()
 261:     {
 262:       return new SmallAttributeSet(this.attrs);
 263:     }
 264: 
 265:     public boolean containsAttribute(Object name, Object value)
 266:     {
 267:       for (int i = 0; i < attrs.length; i += 2)
 268:         {
 269:           if (attrs[i].equals(name) &&
 270:               attrs[i+1].equals(value))
 271:             return true;
 272:         }
 273:       return false;
 274:     }
 275: 
 276:     public boolean containsAttributes(AttributeSet a)
 277:     {
 278:       Enumeration e = a.getAttributeNames();
 279:       while (e.hasMoreElements())
 280:         {
 281:           Object name = e.nextElement();
 282:           Object val = a.getAttribute(name);
 283:           if (!containsAttribute(name, val))
 284:             return false;
 285:         }
 286:       return true;            
 287:     }
 288: 
 289:     public AttributeSet copyAttributes()
 290:     {
 291:       return (AttributeSet) clone();
 292:     }
 293: 
 294:     public boolean equals(Object obj)
 295:     {
 296:       return 
 297:         (obj instanceof AttributeSet)
 298:         && this.isEqual((AttributeSet)obj);
 299:     }
 300:  
 301:     public Object getAttribute(Object key)
 302:     {
 303:       for (int i = 0; i < attrs.length; i += 2)
 304:         {
 305:           if (attrs[i].equals(key))
 306:             return attrs[i+1];
 307:         }
 308:             
 309:       // Check the resolve parent, unless we're looking for the 
 310:       // ResolveAttribute, which would cause an infinite loop
 311:       if (!(key.equals(ResolveAttribute)))
 312:           {
 313:             Object p = getResolveParent();
 314:             if (p != null && p instanceof AttributeSet)
 315:               return (((AttributeSet)p).getAttribute(key));
 316:           }
 317:       
 318:       return null;
 319:     }
 320: 
 321:     public int getAttributeCount()
 322:     {
 323:       return attrs.length / 2;
 324:     }
 325: 
 326:     public Enumeration getAttributeNames()
 327:     {      
 328:       return new Enumeration() 
 329:         {
 330:           int i = 0;
 331:           public boolean hasMoreElements() 
 332:           { 
 333:             return i < attrs.length; 
 334:           }
 335:           public Object nextElement() 
 336:           { 
 337:             i += 2; 
 338:             return attrs[i-2]; 
 339:           }
 340:         };
 341:     }
 342: 
 343:     public AttributeSet getResolveParent()
 344:     {
 345:       return (AttributeSet) getAttribute(ResolveAttribute);
 346:     }
 347: 
 348:     public int hashCode()
 349:     {
 350:       return java.util.Arrays.asList(attrs).hashCode();
 351:     }
 352: 
 353:     public boolean isDefined(Object key)
 354:     {
 355:       for (int i = 0; i < attrs.length; i += 2)
 356:         {
 357:           if (attrs[i].equals(key))
 358:             return true;
 359:         }
 360:       return false;
 361:     }
 362:     
 363:     public boolean isEqual(AttributeSet attr)
 364:     {
 365:       return attr != null 
 366:         && attr.containsAttributes(this)
 367:         && this.containsAttributes(attr);
 368:     }
 369:     
 370:     public String toString()
 371:     {
 372:       StringBuffer sb = new StringBuffer();
 373:       sb.append("[StyleContext.SmallattributeSet:");
 374:       for (int i = 0; i < attrs.length; ++i)
 375:         {
 376:           sb.append(" (");
 377:           sb.append(attrs[i].toString());
 378:           sb.append("=");
 379:           sb.append(attrs[i+1].toString());
 380:           sb.append(")");
 381:         }
 382:       sb.append("]");
 383:       return sb.toString();
 384:     }
 385:   }
 386: 
 387:   // FIXME: official javadocs suggest that these might be more usefully
 388:   // implemented using a WeakHashMap, but not sure if that works most
 389:   // places or whether it really matters anyways.
 390:   //
 391:   // FIXME: also not sure if these tables ought to be static (singletons),
 392:   // shared across all StyleContexts. I think so, but it's not clear in
 393:   // docs. revert to non-shared if you think it matters.
 394:   
 395:   /**
 396:    * The name of the default style.
 397:    */
 398:   public static final String DEFAULT_STYLE = "default";
 399:   
 400:   /**
 401:    * The default style for this style context.
 402:    */
 403:   NamedStyle defaultStyle = new NamedStyle(DEFAULT_STYLE, null);
 404:   
 405:   static Hashtable sharedAttributeSets = new Hashtable();
 406:   static Hashtable sharedFonts = new Hashtable();
 407: 
 408:   static StyleContext defaultStyleContext = new StyleContext();
 409:   static final int compressionThreshold = 9;
 410:   
 411:   EventListenerList listenerList;
 412:   Hashtable styleTable;
 413:   
 414:   /**
 415:    * Creates a new instance of the style context. Add the default style
 416:    * to the style table.
 417:    */
 418:   public StyleContext()
 419:   {
 420:     listenerList = new EventListenerList();
 421:     styleTable = new Hashtable();
 422:     styleTable.put(DEFAULT_STYLE, defaultStyle);
 423:   }
 424: 
 425:   protected SmallAttributeSet createSmallAttributeSet(AttributeSet a)
 426:   {
 427:     return new SmallAttributeSet(a);
 428:   }
 429:   
 430:   protected MutableAttributeSet createLargeAttributeSet(AttributeSet a)
 431:   {
 432:     return new SimpleAttributeSet(a);
 433:   }
 434: 
 435:   public void addChangeListener(ChangeListener listener)
 436:   {
 437:     listenerList.add(ChangeListener.class, listener);
 438:   }
 439: 
 440:   public void removeChangeListener(ChangeListener listener)
 441:   {
 442:     listenerList.remove(ChangeListener.class, listener);
 443:   }
 444: 
 445:   public ChangeListener[] getChangeListeners()
 446:   {
 447:     return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
 448:   }
 449:     
 450:   public Style addStyle(String name, Style parent)
 451:   {
 452:     Style newStyle = new NamedStyle(name, parent);
 453:     if (name != null)
 454:       styleTable.put(name, newStyle);
 455:     return newStyle;
 456:   }
 457: 
 458:   public void removeStyle(String name)
 459:   {
 460:     styleTable.remove(name);
 461:   }
 462: 
 463:   /**
 464:    * Get the style from the style table. If the passed name
 465:    * matches {@link #DEFAULT_STYLE}, returns the default style.
 466:    * Otherwise returns the previously defined style of
 467:    * <code>null</code> if the style with the given name is not defined.
 468:    *
 469:    * @param name the name of the style.
 470:    *
 471:    * @return the style with the given name or null if no such defined.
 472:    */
 473:   public Style getStyle(String name)
 474:   {
 475:     return (Style) styleTable.get(name);
 476:   }
 477:   
 478:   /**
 479:    * Get the names of the style. The returned enumeration always
 480:    * contains at least one member, the default style.
 481:    */
 482:   public Enumeration getStyleNames()
 483:   {
 484:     return styleTable.keys();
 485:   }
 486: 
 487:   //
 488:   // StyleContexts only understand the "simple" model of fonts present in
 489:   // pre-java2d systems: fonts are a family name, a size (integral number
 490:   // of points), and a mask of style parameters (plain, bold, italic, or
 491:   // bold|italic). We have an inner class here called SimpleFontSpec which
 492:   // holds such triples.
 493:   //
 494:   // A SimpleFontSpec can be built for *any* AttributeSet because the size,
 495:   // family, and style keys in an AttributeSet have default values (defined
 496:   // over in StyleConstants).
 497:   //
 498:   // We keep a static cache mapping SimpleFontSpecs to java.awt.Fonts, so
 499:   // that we reuse Fonts between styles and style contexts.
 500:   // 
 501: 
 502:   private static class SimpleFontSpec
 503:   {
 504:     String family;
 505:     int style;
 506:     int size;
 507:     public SimpleFontSpec(String family,
 508:                           int style,
 509:                           int size)
 510:     {
 511:       this.family = family;
 512:       this.style = style;
 513:       this.size = size;
 514:     }
 515:     public boolean equals(Object obj)
 516:     {
 517:       return (obj != null)
 518:         && (obj instanceof SimpleFontSpec)
 519:         && (((SimpleFontSpec)obj).family.equals(this.family))
 520:         && (((SimpleFontSpec)obj).style == this.style)
 521:         && (((SimpleFontSpec)obj).size == this.size);
 522:     }
 523:     public int hashCode()
 524:     {
 525:       return family.hashCode() + style + size;
 526:     }
 527:   }
 528:   
 529:   public Font getFont(AttributeSet attr)
 530:   {
 531:     String family = StyleConstants.getFontFamily(attr);
 532:     int style = Font.PLAIN;
 533:     if (StyleConstants.isBold(attr))
 534:       style += Font.BOLD;
 535:     if (StyleConstants.isItalic(attr))
 536:       style += Font.ITALIC;      
 537:     int size = StyleConstants.getFontSize(attr);
 538:     return getFont(family, style, size);
 539:   }
 540: 
 541:   public Font getFont(String family, int style, int size)
 542:   {
 543:     SimpleFontSpec spec = new SimpleFontSpec(family, style, size);
 544:     if (sharedFonts.containsKey(spec))
 545:       return (Font) sharedFonts.get(spec);
 546:     else
 547:       {
 548:         Font tmp = new Font(family, style, size);
 549:         sharedFonts.put(spec, tmp);
 550:         return tmp;
 551:       }
 552:   }
 553:   
 554:   public FontMetrics getFontMetrics(Font f)
 555:   {
 556:     return Toolkit.getDefaultToolkit().getFontMetrics(f);
 557:   }
 558: 
 559:   public Color getForeground(AttributeSet a)
 560:   {
 561:     return StyleConstants.getForeground(a);
 562:   }
 563: 
 564:   public Color getBackground(AttributeSet a)
 565:   {
 566:     return StyleConstants.getBackground(a);
 567:   }
 568: 
 569:   protected int getCompressionThreshold() 
 570:   {
 571:     return compressionThreshold;
 572:   }
 573: 
 574:   public static StyleContext getDefaultStyleContext()
 575:   {
 576:     return defaultStyleContext;
 577:   }
 578: 
 579:   public AttributeSet addAttribute(AttributeSet old, Object name, Object value)
 580:   {
 581:     if (old instanceof MutableAttributeSet)
 582:       {
 583:         ((MutableAttributeSet)old).addAttribute(name, value);
 584:         return old;
 585:       }
 586:     else 
 587:       {
 588:         MutableAttributeSet mutable = createLargeAttributeSet(old);
 589:         mutable.addAttribute(name, value);
 590:         if (mutable.getAttributeCount() >= getCompressionThreshold())
 591:           return mutable;
 592:         else
 593:           {
 594:             SmallAttributeSet small = createSmallAttributeSet(mutable);
 595:             if (sharedAttributeSets.containsKey(small))
 596:               small = (SmallAttributeSet) sharedAttributeSets.get(small);
 597:             else
 598:               sharedAttributeSets.put(small,small);
 599:             return small;
 600:           }
 601:       }
 602:   }
 603: 
 604:   public AttributeSet addAttributes(AttributeSet old, AttributeSet attributes)
 605:   {
 606:     if (old instanceof MutableAttributeSet)
 607:       {
 608:         ((MutableAttributeSet)old).addAttributes(attributes);
 609:         return old;
 610:       }
 611:     else 
 612:       {
 613:         MutableAttributeSet mutable = createLargeAttributeSet(old);
 614:         mutable.addAttributes(attributes);
 615:         if (mutable.getAttributeCount() >= getCompressionThreshold())
 616:           return mutable;
 617:         else
 618:           {
 619:             SmallAttributeSet small = createSmallAttributeSet(mutable);
 620:             if (sharedAttributeSets.containsKey(small))
 621:               small = (SmallAttributeSet) sharedAttributeSets.get(small);
 622:             else
 623:               sharedAttributeSets.put(small,small);
 624:             return small;
 625:           }
 626:       }
 627:   }
 628: 
 629:   public AttributeSet getEmptySet()
 630:   {
 631:     AttributeSet e = createSmallAttributeSet(null);
 632:     if (sharedAttributeSets.containsKey(e))
 633:       e = (AttributeSet) sharedAttributeSets.get(e);
 634:     else
 635:       sharedAttributeSets.put(e, e);
 636:     return e;
 637:   }
 638: 
 639:   public void reclaim(AttributeSet attributes)
 640:   {
 641:     if (sharedAttributeSets.containsKey(attributes))
 642:       sharedAttributeSets.remove(attributes);
 643:   }
 644: 
 645:   public AttributeSet removeAttribute(AttributeSet old, Object name)
 646:   {
 647:     if (old instanceof MutableAttributeSet)
 648:       {
 649:         ((MutableAttributeSet)old).removeAttribute(name);
 650:         if (old.getAttributeCount() < getCompressionThreshold())
 651:           {
 652:             SmallAttributeSet small = createSmallAttributeSet(old);
 653:             if (!sharedAttributeSets.containsKey(small))
 654:               sharedAttributeSets.put(small,small);
 655:             old = (AttributeSet) sharedAttributeSets.get(small);
 656:           }
 657:         return old;
 658:       }
 659:     else 
 660:       {          
 661:         MutableAttributeSet mutable = createLargeAttributeSet(old);
 662:         mutable.removeAttribute(name);
 663:         SmallAttributeSet small = createSmallAttributeSet(mutable);
 664:         if (sharedAttributeSets.containsKey(small))
 665:           small = (SmallAttributeSet) sharedAttributeSets.get(small);
 666:         else
 667:           sharedAttributeSets.put(small,small);
 668:         return small;
 669:       }
 670:   }
 671: 
 672:   public AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes)
 673:   {
 674:     return removeAttributes(old, attributes.getAttributeNames());
 675:   }
 676: 
 677:   public AttributeSet removeAttributes(AttributeSet old, Enumeration names)
 678:   {
 679:     if (old instanceof MutableAttributeSet)
 680:       {
 681:         ((MutableAttributeSet)old).removeAttributes(names);
 682:         if (old.getAttributeCount() < getCompressionThreshold())
 683:           {
 684:             SmallAttributeSet small = createSmallAttributeSet(old);
 685:             if (!sharedAttributeSets.containsKey(small))
 686:               sharedAttributeSets.put(small,small);
 687:             old = (AttributeSet) sharedAttributeSets.get(small);
 688:           }
 689:         return old;
 690:       }
 691:     else 
 692:       {          
 693:         MutableAttributeSet mutable = createLargeAttributeSet(old);
 694:         mutable.removeAttributes(names);
 695:         SmallAttributeSet small = createSmallAttributeSet(mutable);
 696:         if (sharedAttributeSets.containsKey(small))
 697:           small = (SmallAttributeSet) sharedAttributeSets.get(small);
 698:         else
 699:           sharedAttributeSets.put(small,small);
 700:         return small;
 701:       }    
 702:   }
 703: 
 704: 
 705:   // FIXME: there's some sort of quasi-serialization stuff in here which I
 706:   // have left incomplete; I'm not sure I understand the intent properly.
 707: 
 708:   public static Object getStaticAttribute(Object key)
 709:   {
 710:     throw new InternalError("not implemented");
 711:   }
 712:   
 713:   public static Object getStaticAttributeKey(Object key)
 714:   {
 715:     throw new InternalError("not implemented");
 716:   }
 717: 
 718:   public static void readAttributeSet(ObjectInputStream in, MutableAttributeSet a)
 719:     throws ClassNotFoundException, IOException
 720:   {
 721:     throw new InternalError("not implemented");
 722:   }
 723:   
 724:   public static void writeAttributeSet(ObjectOutputStream out, AttributeSet a)
 725:     throws IOException
 726:   {
 727:     throw new InternalError("not implemented");
 728:   }
 729: 
 730:   public void readAttributes(ObjectInputStream in, MutableAttributeSet a)
 731:     throws ClassNotFoundException, IOException 
 732:   {
 733:     throw new InternalError("not implemented");
 734:   }
 735: 
 736:   public void writeAttributes(ObjectOutputStream out, AttributeSet a)
 737:     throws IOException
 738:   {
 739:     throw new InternalError("not implemented");
 740:   }
 741: }