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