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 getAttributeCount() == attr.getAttributeCount()
 366:              && this.containsAttributes(attr);
 367:     }
 368:     
 369:     public String toString()
 370:     {
 371:       StringBuffer sb = new StringBuffer();
 372:       sb.append("[StyleContext.SmallattributeSet:");
 373:       for (int i = 0; i < attrs.length; ++i)
 374:         {
 375:           sb.append(" (");
 376:           sb.append(attrs[i].toString());
 377:           sb.append("=");
 378:           sb.append(attrs[i+1].toString());
 379:           sb.append(")");
 380:         }
 381:       sb.append("]");
 382:       return sb.toString();
 383:     }
 384:   }
 385: 
 386:   // FIXME: official javadocs suggest that these might be more usefully
 387:   // implemented using a WeakHashMap, but not sure if that works most
 388:   // places or whether it really matters anyways.
 389:   //
 390:   // FIXME: also not sure if these tables ought to be static (singletons),
 391:   // shared across all StyleContexts. I think so, but it's not clear in
 392:   // docs. revert to non-shared if you think it matters.
 393:   
 394:   /**
 395:    * The name of the default style.
 396:    */
 397:   public static final String DEFAULT_STYLE = "default";
 398:   
 399:   /**
 400:    * The default style for this style context.
 401:    */
 402:   NamedStyle defaultStyle = new NamedStyle(DEFAULT_STYLE, null);
 403:   
 404:   static Hashtable sharedAttributeSets = new Hashtable();
 405:   static Hashtable sharedFonts = new Hashtable();
 406: 
 407:   static StyleContext defaultStyleContext = new StyleContext();
 408:   static final int compressionThreshold = 9;
 409:   
 410:   EventListenerList listenerList;
 411:   Hashtable styleTable;
 412:   
 413:   /**
 414:    * Creates a new instance of the style context. Add the default style
 415:    * to the style table.
 416:    */
 417:   public StyleContext()
 418:   {
 419:     listenerList = new EventListenerList();
 420:     styleTable = new Hashtable();
 421:     styleTable.put(DEFAULT_STYLE, defaultStyle);
 422:   }
 423: 
 424:   protected SmallAttributeSet createSmallAttributeSet(AttributeSet a)
 425:   {
 426:     return new SmallAttributeSet(a);
 427:   }
 428:   
 429:   protected MutableAttributeSet createLargeAttributeSet(AttributeSet a)
 430:   {
 431:     return new SimpleAttributeSet(a);
 432:   }
 433: 
 434:   public void addChangeListener(ChangeListener listener)
 435:   {
 436:     listenerList.add(ChangeListener.class, listener);
 437:   }
 438: 
 439:   public void removeChangeListener(ChangeListener listener)
 440:   {
 441:     listenerList.remove(ChangeListener.class, listener);
 442:   }
 443: 
 444:   public ChangeListener[] getChangeListeners()
 445:   {
 446:     return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
 447:   }
 448:     
 449:   public Style addStyle(String name, Style parent)
 450:   {
 451:     Style newStyle = new NamedStyle(name, parent);
 452:     if (name != null)
 453:       styleTable.put(name, newStyle);
 454:     return newStyle;
 455:   }
 456: 
 457:   public void removeStyle(String name)
 458:   {
 459:     styleTable.remove(name);
 460:   }
 461: 
 462:   /**
 463:    * Get the style from the style table. If the passed name
 464:    * matches {@link #DEFAULT_STYLE}, returns the default style.
 465:    * Otherwise returns the previously defined style of
 466:    * <code>null</code> if the style with the given name is not defined.
 467:    *
 468:    * @param name the name of the style.
 469:    *
 470:    * @return the style with the given name or null if no such defined.
 471:    */
 472:   public Style getStyle(String name)
 473:   {
 474:     return (Style) styleTable.get(name);
 475:   }
 476:   
 477:   /**
 478:    * Get the names of the style. The returned enumeration always
 479:    * contains at least one member, the default style.
 480:    */
 481:   public Enumeration getStyleNames()
 482:   {
 483:     return styleTable.keys();
 484:   }
 485: 
 486:   //
 487:   // StyleContexts only understand the "simple" model of fonts present in
 488:   // pre-java2d systems: fonts are a family name, a size (integral number
 489:   // of points), and a mask of style parameters (plain, bold, italic, or
 490:   // bold|italic). We have an inner class here called SimpleFontSpec which
 491:   // holds such triples.
 492:   //
 493:   // A SimpleFontSpec can be built for *any* AttributeSet because the size,
 494:   // family, and style keys in an AttributeSet have default values (defined
 495:   // over in StyleConstants).
 496:   //
 497:   // We keep a static cache mapping SimpleFontSpecs to java.awt.Fonts, so
 498:   // that we reuse Fonts between styles and style contexts.
 499:   // 
 500: 
 501:   private static class SimpleFontSpec
 502:   {
 503:     String family;
 504:     int style;
 505:     int size;
 506:     public SimpleFontSpec(String family,
 507:                           int style,
 508:                           int size)
 509:     {
 510:       this.family = family;
 511:       this.style = style;
 512:       this.size = size;
 513:     }
 514:     public boolean equals(Object obj)
 515:     {
 516:       return (obj != null)
 517:         && (obj instanceof SimpleFontSpec)
 518:         && (((SimpleFontSpec)obj).family.equals(this.family))
 519:         && (((SimpleFontSpec)obj).style == this.style)
 520:         && (((SimpleFontSpec)obj).size == this.size);
 521:     }
 522:     public int hashCode()
 523:     {
 524:       return family.hashCode() + style + size;
 525:     }
 526:   }
 527:   
 528:   public Font getFont(AttributeSet attr)
 529:   {
 530:     String family = StyleConstants.getFontFamily(attr);
 531:     int style = Font.PLAIN;
 532:     if (StyleConstants.isBold(attr))
 533:       style += Font.BOLD;
 534:     if (StyleConstants.isItalic(attr))
 535:       style += Font.ITALIC;      
 536:     int size = StyleConstants.getFontSize(attr);
 537:     return getFont(family, style, size);
 538:   }
 539: 
 540:   public Font getFont(String family, int style, int size)
 541:   {
 542:     SimpleFontSpec spec = new SimpleFontSpec(family, style, size);
 543:     if (sharedFonts.containsKey(spec))
 544:       return (Font) sharedFonts.get(spec);
 545:     else
 546:       {
 547:         Font tmp = new Font(family, style, size);
 548:         sharedFonts.put(spec, tmp);
 549:         return tmp;
 550:       }
 551:   }
 552:   
 553:   public FontMetrics getFontMetrics(Font f)
 554:   {
 555:     return Toolkit.getDefaultToolkit().getFontMetrics(f);
 556:   }
 557: 
 558:   public Color getForeground(AttributeSet a)
 559:   {
 560:     return StyleConstants.getForeground(a);
 561:   }
 562: 
 563:   public Color getBackground(AttributeSet a)
 564:   {
 565:     return StyleConstants.getBackground(a);
 566:   }
 567: 
 568:   protected int getCompressionThreshold() 
 569:   {
 570:     return compressionThreshold;
 571:   }
 572: 
 573:   public static StyleContext getDefaultStyleContext()
 574:   {
 575:     return defaultStyleContext;
 576:   }
 577: 
 578:   public AttributeSet addAttribute(AttributeSet old, Object name, Object value)
 579:   {
 580:     if (old instanceof MutableAttributeSet)
 581:       {
 582:         ((MutableAttributeSet)old).addAttribute(name, value);
 583:         return old;
 584:       }
 585:     else 
 586:       {
 587:         MutableAttributeSet mutable = createLargeAttributeSet(old);
 588:         mutable.addAttribute(name, value);
 589:         if (mutable.getAttributeCount() >= getCompressionThreshold())
 590:           return mutable;
 591:         else
 592:           {
 593:             SmallAttributeSet small = createSmallAttributeSet(mutable);
 594:             if (sharedAttributeSets.containsKey(small))
 595:               small = (SmallAttributeSet) sharedAttributeSets.get(small);
 596:             else
 597:               sharedAttributeSets.put(small,small);
 598:             return small;
 599:           }
 600:       }
 601:   }
 602: 
 603:   public AttributeSet addAttributes(AttributeSet old, AttributeSet attributes)
 604:   {
 605:     if (old instanceof MutableAttributeSet)
 606:       {
 607:         ((MutableAttributeSet)old).addAttributes(attributes);
 608:         return old;
 609:       }
 610:     else 
 611:       {
 612:         MutableAttributeSet mutable = createLargeAttributeSet(old);
 613:         mutable.addAttributes(attributes);
 614:         if (mutable.getAttributeCount() >= getCompressionThreshold())
 615:           return mutable;
 616:         else
 617:           {
 618:             SmallAttributeSet small = createSmallAttributeSet(mutable);
 619:             if (sharedAttributeSets.containsKey(small))
 620:               small = (SmallAttributeSet) sharedAttributeSets.get(small);
 621:             else
 622:               sharedAttributeSets.put(small,small);
 623:             return small;
 624:           }
 625:       }
 626:   }
 627: 
 628:   public AttributeSet getEmptySet()
 629:   {
 630:     AttributeSet e = createSmallAttributeSet(null);
 631:     if (sharedAttributeSets.containsKey(e))
 632:       e = (AttributeSet) sharedAttributeSets.get(e);
 633:     else
 634:       sharedAttributeSets.put(e, e);
 635:     return e;
 636:   }
 637: 
 638:   public void reclaim(AttributeSet attributes)
 639:   {
 640:     if (sharedAttributeSets.containsKey(attributes))
 641:       sharedAttributeSets.remove(attributes);
 642:   }
 643: 
 644:   public AttributeSet removeAttribute(AttributeSet old, Object name)
 645:   {
 646:     if (old instanceof MutableAttributeSet)
 647:       {
 648:         ((MutableAttributeSet)old).removeAttribute(name);
 649:         if (old.getAttributeCount() < getCompressionThreshold())
 650:           {
 651:             SmallAttributeSet small = createSmallAttributeSet(old);
 652:             if (!sharedAttributeSets.containsKey(small))
 653:               sharedAttributeSets.put(small,small);
 654:             old = (AttributeSet) sharedAttributeSets.get(small);
 655:           }
 656:         return old;
 657:       }
 658:     else 
 659:       {          
 660:         MutableAttributeSet mutable = createLargeAttributeSet(old);
 661:         mutable.removeAttribute(name);
 662:         SmallAttributeSet small = createSmallAttributeSet(mutable);
 663:         if (sharedAttributeSets.containsKey(small))
 664:           small = (SmallAttributeSet) sharedAttributeSets.get(small);
 665:         else
 666:           sharedAttributeSets.put(small,small);
 667:         return small;
 668:       }
 669:   }
 670: 
 671:   public AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes)
 672:   {
 673:     return removeAttributes(old, attributes.getAttributeNames());
 674:   }
 675: 
 676:   public AttributeSet removeAttributes(AttributeSet old, Enumeration names)
 677:   {
 678:     if (old instanceof MutableAttributeSet)
 679:       {
 680:         ((MutableAttributeSet)old).removeAttributes(names);
 681:         if (old.getAttributeCount() < getCompressionThreshold())
 682:           {
 683:             SmallAttributeSet small = createSmallAttributeSet(old);
 684:             if (!sharedAttributeSets.containsKey(small))
 685:               sharedAttributeSets.put(small,small);
 686:             old = (AttributeSet) sharedAttributeSets.get(small);
 687:           }
 688:         return old;
 689:       }
 690:     else 
 691:       {          
 692:         MutableAttributeSet mutable = createLargeAttributeSet(old);
 693:         mutable.removeAttributes(names);
 694:         SmallAttributeSet small = createSmallAttributeSet(mutable);
 695:         if (sharedAttributeSets.containsKey(small))
 696:           small = (SmallAttributeSet) sharedAttributeSets.get(small);
 697:         else
 698:           sharedAttributeSets.put(small,small);
 699:         return small;
 700:       }    
 701:   }
 702: 
 703: 
 704:   // FIXME: there's some sort of quasi-serialization stuff in here which I
 705:   // have left incomplete; I'm not sure I understand the intent properly.
 706: 
 707:   public static Object getStaticAttribute(Object key)
 708:   {
 709:     throw new InternalError("not implemented");
 710:   }
 711:   
 712:   public static Object getStaticAttributeKey(Object key)
 713:   {
 714:     throw new InternalError("not implemented");
 715:   }
 716: 
 717:   public static void readAttributeSet(ObjectInputStream in, MutableAttributeSet a)
 718:     throws ClassNotFoundException, IOException
 719:   {
 720:     throw new InternalError("not implemented");
 721:   }
 722:   
 723:   public static void writeAttributeSet(ObjectOutputStream out, AttributeSet a)
 724:     throws IOException
 725:   {
 726:     throw new InternalError("not implemented");
 727:   }
 728: 
 729:   public void readAttributes(ObjectInputStream in, MutableAttributeSet a)
 730:     throws ClassNotFoundException, IOException 
 731:   {
 732:     throw new InternalError("not implemented");
 733:   }
 734: 
 735:   public void writeAttributes(ObjectOutputStream out, AttributeSet a)
 736:     throws IOException
 737:   {
 738:     throw new InternalError("not implemented");
 739:   }
 740: }