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 gnu.classpath.NotImplementedException;
  42: 
  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;
  55: 
  56: import javax.swing.event.ChangeEvent;
  57: import javax.swing.event.ChangeListener;
  58: import javax.swing.event.EventListenerList;
  59: 
  60: public class StyleContext 
  61:     implements Serializable, AbstractDocument.AttributeContext
  62: {
  63:   /** The serialization UID (compatible with JDK1.5). */
  64:   private static final long serialVersionUID = 8042858831190784241L;
  65: 
  66:   public class NamedStyle
  67:     implements Serializable, Style
  68:   {
  69:     /** The serialization UID (compatible with JDK1.5). */
  70:     private static final long serialVersionUID = -6690628971806226374L;
  71: 
  72:     protected ChangeEvent changeEvent;
  73:     protected EventListenerList listenerList;
  74:       
  75:     AttributeSet attributes;
  76:     String name;
  77: 
  78:     public NamedStyle()
  79:     {
  80:       this(null, null);
  81:     }
  82: 
  83:     public NamedStyle(Style parent)
  84:     {
  85:       this(null, parent);
  86:     }
  87: 
  88:     public NamedStyle(String name, Style parent)
  89:     {
  90:       this.name = name;
  91:       this.attributes = getEmptySet();
  92:       this.changeEvent = new ChangeEvent(this);
  93:       this.listenerList = new EventListenerList();
  94:       setResolveParent(parent);
  95:     }
  96: 
  97:     public String getName()
  98:     {
  99:       return name;
 100:     }
 101: 
 102:     public void setName(String n)
 103:     {
 104:       name = n;
 105:       fireStateChanged();
 106:     }
 107: 
 108:     public void addChangeListener(ChangeListener l)
 109:     {
 110:       listenerList.add(ChangeListener.class, l);
 111:     }
 112:       
 113:     public void removeChangeListener(ChangeListener l)
 114:     {
 115:       listenerList.remove(ChangeListener.class, l);
 116:     }
 117:       
 118:     public EventListener[] getListeners(Class listenerType)
 119:     {
 120:       return listenerList.getListeners(listenerType);
 121:     }
 122: 
 123:     public ChangeListener[] getChangeListeners()
 124:     {
 125:       return (ChangeListener[]) getListeners(ChangeListener.class);
 126:     }
 127: 
 128:     protected  void fireStateChanged()
 129:     {
 130:       ChangeListener[] listeners = getChangeListeners();
 131:       for (int i = 0; i < listeners.length; ++i)
 132:         {
 133:           listeners[i].stateChanged(changeEvent);
 134:         }
 135:     }
 136: 
 137:     public void addAttribute(Object name, Object value)
 138:     {
 139:       attributes = StyleContext.this.addAttribute(attributes, name, value);
 140:       fireStateChanged();
 141:     }
 142: 
 143:     public void addAttributes(AttributeSet attr)
 144:     {
 145:       attributes = StyleContext.this.addAttributes(attributes, attr);
 146:       fireStateChanged();
 147:     }
 148: 
 149:     public boolean containsAttribute(Object name, Object value)
 150:     {
 151:       return attributes.containsAttribute(name, value);
 152:     }
 153:       
 154:     public boolean containsAttributes(AttributeSet attrs)
 155:     {
 156:       return attributes.containsAttributes(attrs);
 157:     }
 158: 
 159:     public AttributeSet copyAttributes()
 160:     {
 161:       return attributes.copyAttributes();
 162:     }
 163:             
 164:     public Object getAttribute(Object attrName)
 165:     {
 166:       return attributes.getAttribute(attrName);
 167:     }
 168: 
 169:     public int getAttributeCount()
 170:     {
 171:       return attributes.getAttributeCount();
 172:     }
 173: 
 174:     public Enumeration getAttributeNames()
 175:     {
 176:       return attributes.getAttributeNames();
 177:     }
 178:       
 179:     public boolean isDefined(Object attrName)
 180:     {
 181:       return attributes.isDefined(attrName);        
 182:     }
 183: 
 184:     public boolean isEqual(AttributeSet attr)
 185:     {
 186:       return attributes.isEqual(attr);
 187:     }
 188: 
 189:     public void removeAttribute(Object name)
 190:     {
 191:       attributes = StyleContext.this.removeAttribute(attributes, name);
 192:       fireStateChanged();
 193:     }
 194: 
 195:     public void removeAttributes(AttributeSet attrs)
 196:     {
 197:       attributes = StyleContext.this.removeAttributes(attributes, attrs);
 198:       fireStateChanged();
 199:     }
 200: 
 201:     public void removeAttributes(Enumeration names)
 202:     {
 203:       attributes = StyleContext.this.removeAttributes(attributes, names);
 204:       fireStateChanged();
 205:     }
 206: 
 207: 
 208:     public AttributeSet getResolveParent()
 209:     {
 210:       return attributes.getResolveParent();        
 211:     }
 212: 
 213:     public void setResolveParent(AttributeSet parent)
 214:     {
 215:       if (parent != null)
 216:         {
 217:           attributes = StyleContext.this.addAttribute
 218:             (attributes, ResolveAttribute, parent);
 219:         }
 220:       fireStateChanged();
 221:     }
 222:       
 223:     public String toString()
 224:     {
 225:       return ("[NamedStyle: name=" + name + ", attrs=" + attributes.toString() + "]");
 226:     }      
 227:   }
 228:   
 229:   public class SmallAttributeSet
 230:     implements AttributeSet
 231:   {
 232:     final Object [] attrs;
 233:     public SmallAttributeSet(AttributeSet a)
 234:     {
 235:       if (a == null)
 236:         attrs = new Object[0];
 237:       else
 238:         {
 239:           int n = a.getAttributeCount();
 240:           int i = 0;
 241:           attrs = new Object[n * 2];
 242:           Enumeration e = a.getAttributeNames();
 243:           while (e.hasMoreElements())
 244:             {
 245:               Object name = e.nextElement();
 246:               attrs[i++] = name;
 247:               attrs[i++] = a.getAttribute(name);
 248:             }
 249:         }
 250:     }
 251: 
 252:     public SmallAttributeSet(Object [] a)
 253:     {
 254:       if (a == null)
 255:         attrs = new Object[0];
 256:       else
 257:         {
 258:           attrs = new Object[a.length];
 259:           System.arraycopy(a, 0, attrs, 0, a.length);
 260:         }
 261:     }
 262: 
 263:     public Object clone()
 264:     {
 265:       return new SmallAttributeSet(this.attrs);
 266:     }
 267: 
 268:     public boolean containsAttribute(Object name, Object value)
 269:     {
 270:       for (int i = 0; i < attrs.length; i += 2)
 271:         {
 272:           if (attrs[i].equals(name) &&
 273:               attrs[i+1].equals(value))
 274:             return true;
 275:         }
 276:       return false;
 277:     }
 278: 
 279:     public boolean containsAttributes(AttributeSet a)
 280:     {
 281:       Enumeration e = a.getAttributeNames();
 282:       while (e.hasMoreElements())
 283:         {
 284:           Object name = e.nextElement();
 285:           Object val = a.getAttribute(name);
 286:           if (!containsAttribute(name, val))
 287:             return false;
 288:         }
 289:       return true;            
 290:     }
 291: 
 292:     public AttributeSet copyAttributes()
 293:     {
 294:       return (AttributeSet) clone();
 295:     }
 296: 
 297:     public boolean equals(Object obj)
 298:     {
 299:       return 
 300:         (obj instanceof AttributeSet)
 301:         && this.isEqual((AttributeSet)obj);
 302:     }
 303:  
 304:     public Object getAttribute(Object key)
 305:     {
 306:       for (int i = 0; i < attrs.length; i += 2)
 307:         {
 308:           if (attrs[i].equals(key))
 309:             return attrs[i+1];
 310:         }
 311:             
 312:       // Check the resolve parent, unless we're looking for the 
 313:       // ResolveAttribute, which would cause an infinite loop
 314:       if (!(key.equals(ResolveAttribute)))
 315:           {
 316:             Object p = getResolveParent();
 317:             if (p != null && p instanceof AttributeSet)
 318:               return (((AttributeSet)p).getAttribute(key));
 319:           }
 320:       
 321:       return null;
 322:     }
 323: 
 324:     public int getAttributeCount()
 325:     {
 326:       return attrs.length / 2;
 327:     }
 328: 
 329:     public Enumeration getAttributeNames()
 330:     {      
 331:       return new Enumeration() 
 332:         {
 333:           int i = 0;
 334:           public boolean hasMoreElements() 
 335:           { 
 336:             return i < attrs.length; 
 337:           }
 338:           public Object nextElement() 
 339:           { 
 340:             i += 2; 
 341:             return attrs[i-2]; 
 342:           }
 343:         };
 344:     }
 345: 
 346:     public AttributeSet getResolveParent()
 347:     {
 348:       return (AttributeSet) getAttribute(ResolveAttribute);
 349:     }
 350: 
 351:     public int hashCode()
 352:     {
 353:       return java.util.Arrays.asList(attrs).hashCode();
 354:     }
 355: 
 356:     public boolean isDefined(Object key)
 357:     {
 358:       for (int i = 0; i < attrs.length; i += 2)
 359:         {
 360:           if (attrs[i].equals(key))
 361:             return true;
 362:         }
 363:       return false;
 364:     }
 365:     
 366:     public boolean isEqual(AttributeSet attr)
 367:     {
 368:       return getAttributeCount() == attr.getAttributeCount()
 369:              && this.containsAttributes(attr);
 370:     }
 371:     
 372:     public String toString()
 373:     {
 374:       StringBuffer sb = new StringBuffer();
 375:       sb.append("[StyleContext.SmallattributeSet:");
 376:       for (int i = 0; i < attrs.length - 1; ++i)
 377:         {
 378:           sb.append(" (");
 379:           sb.append(attrs[i].toString());
 380:           sb.append("=");
 381:           sb.append(attrs[i+1].toString());
 382:           sb.append(")");
 383:         }
 384:       sb.append("]");
 385:       return sb.toString();
 386:     }
 387:   }
 388: 
 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.
 392:   //
 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.
 396:   
 397:   /**
 398:    * The name of the default style.
 399:    */
 400:   public static final String DEFAULT_STYLE = "default";
 401:   
 402:   /**
 403:    * The default style for this style context.
 404:    */
 405:   NamedStyle defaultStyle = new NamedStyle(DEFAULT_STYLE, null);
 406:   
 407:   static Hashtable sharedAttributeSets = new Hashtable();
 408:   static Hashtable sharedFonts = new Hashtable();
 409: 
 410:   static StyleContext defaultStyleContext = new StyleContext();
 411:   static final int compressionThreshold = 9;
 412: 
 413:   /**
 414:    * These attribute keys are handled specially in serialization.
 415:    */
 416:   private static HashSet staticAttributeKeys = new HashSet();
 417: 
 418:   EventListenerList listenerList;
 419:   Hashtable styleTable;
 420:   
 421:   /**
 422:    * Creates a new instance of the style context. Add the default style
 423:    * to the style table.
 424:    */
 425:   public StyleContext()
 426:   {
 427:     listenerList = new EventListenerList();
 428:     styleTable = new Hashtable();
 429:     styleTable.put(DEFAULT_STYLE, defaultStyle);
 430:   }
 431: 
 432:   protected SmallAttributeSet createSmallAttributeSet(AttributeSet a)
 433:   {
 434:     return new SmallAttributeSet(a);
 435:   }
 436:   
 437:   protected MutableAttributeSet createLargeAttributeSet(AttributeSet a)
 438:   {
 439:     return new SimpleAttributeSet(a);
 440:   }
 441: 
 442:   public void addChangeListener(ChangeListener listener)
 443:   {
 444:     listenerList.add(ChangeListener.class, listener);
 445:   }
 446: 
 447:   public void removeChangeListener(ChangeListener listener)
 448:   {
 449:     listenerList.remove(ChangeListener.class, listener);
 450:   }
 451: 
 452:   public ChangeListener[] getChangeListeners()
 453:   {
 454:     return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
 455:   }
 456:     
 457:   public Style addStyle(String name, Style parent)
 458:   {
 459:     Style newStyle = new NamedStyle(name, parent);
 460:     if (name != null)
 461:       styleTable.put(name, newStyle);
 462:     return newStyle;
 463:   }
 464: 
 465:   public void removeStyle(String name)
 466:   {
 467:     styleTable.remove(name);
 468:   }
 469: 
 470:   /**
 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.
 475:    *
 476:    * @param name the name of the style.
 477:    *
 478:    * @return the style with the given name or null if no such defined.
 479:    */
 480:   public Style getStyle(String name)
 481:   {
 482:     return (Style) styleTable.get(name);
 483:   }
 484:   
 485:   /**
 486:    * Get the names of the style. The returned enumeration always
 487:    * contains at least one member, the default style.
 488:    */
 489:   public Enumeration getStyleNames()
 490:   {
 491:     return styleTable.keys();
 492:   }
 493: 
 494:   //
 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.
 500:   //
 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).
 504:   //
 505:   // We keep a static cache mapping SimpleFontSpecs to java.awt.Fonts, so
 506:   // that we reuse Fonts between styles and style contexts.
 507:   // 
 508: 
 509:   private static class SimpleFontSpec
 510:   {
 511:     String family;
 512:     int style;
 513:     int size;
 514:     public SimpleFontSpec(String family,
 515:                           int style,
 516:                           int size)
 517:     {
 518:       this.family = family;
 519:       this.style = style;
 520:       this.size = size;
 521:     }
 522:     public boolean equals(Object obj)
 523:     {
 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);
 529:     }
 530:     public int hashCode()
 531:     {
 532:       return family.hashCode() + style + size;
 533:     }
 534:   }
 535:   
 536:   public Font getFont(AttributeSet attr)
 537:   {
 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);
 546:   }
 547: 
 548:   public Font getFont(String family, int style, int size)
 549:   {
 550:     SimpleFontSpec spec = new SimpleFontSpec(family, style, size);
 551:     if (sharedFonts.containsKey(spec))
 552:       return (Font) sharedFonts.get(spec);
 553:     else
 554:       {
 555:         Font tmp = new Font(family, style, size);
 556:         sharedFonts.put(spec, tmp);
 557:         return tmp;
 558:       }
 559:   }
 560:   
 561:   public FontMetrics getFontMetrics(Font f)
 562:   {
 563:     return Toolkit.getDefaultToolkit().getFontMetrics(f);
 564:   }
 565: 
 566:   public Color getForeground(AttributeSet a)
 567:   {
 568:     return StyleConstants.getForeground(a);
 569:   }
 570: 
 571:   public Color getBackground(AttributeSet a)
 572:   {
 573:     return StyleConstants.getBackground(a);
 574:   }
 575: 
 576:   protected int getCompressionThreshold() 
 577:   {
 578:     return compressionThreshold;
 579:   }
 580: 
 581:   public static StyleContext getDefaultStyleContext()
 582:   {
 583:     return defaultStyleContext;
 584:   }
 585: 
 586:   public AttributeSet addAttribute(AttributeSet old, Object name, Object value)
 587:   {
 588:     if (old instanceof MutableAttributeSet)
 589:       {
 590:         ((MutableAttributeSet)old).addAttribute(name, value);
 591:         return old;
 592:       }
 593:     else 
 594:       {
 595:         MutableAttributeSet mutable = createLargeAttributeSet(old);
 596:         mutable.addAttribute(name, value);
 597:         if (mutable.getAttributeCount() >= getCompressionThreshold())
 598:           return mutable;
 599:         else
 600:           {
 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;
 607:           }
 608:       }
 609:   }
 610: 
 611:   public AttributeSet addAttributes(AttributeSet old, AttributeSet attributes)
 612:   {
 613:     if (old instanceof MutableAttributeSet)
 614:       {
 615:         ((MutableAttributeSet)old).addAttributes(attributes);
 616:         return old;
 617:       }
 618:     else 
 619:       {
 620:         MutableAttributeSet mutable = createLargeAttributeSet(old);
 621:         mutable.addAttributes(attributes);
 622:         if (mutable.getAttributeCount() >= getCompressionThreshold())
 623:           return mutable;
 624:         else
 625:           {
 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;
 632:           }
 633:       }
 634:   }
 635: 
 636:   public AttributeSet getEmptySet()
 637:   {
 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;
 644:   }
 645: 
 646:   public void reclaim(AttributeSet attributes)
 647:   {
 648:     if (sharedAttributeSets.containsKey(attributes))
 649:       sharedAttributeSets.remove(attributes);
 650:   }
 651: 
 652:   public AttributeSet removeAttribute(AttributeSet old, Object name)
 653:   {
 654:     if (old instanceof MutableAttributeSet)
 655:       {
 656:         ((MutableAttributeSet)old).removeAttribute(name);
 657:         if (old.getAttributeCount() < getCompressionThreshold())
 658:           {
 659:             SmallAttributeSet small = createSmallAttributeSet(old);
 660:             if (!sharedAttributeSets.containsKey(small))
 661:               sharedAttributeSets.put(small,small);
 662:             old = (AttributeSet) sharedAttributeSets.get(small);
 663:           }
 664:         return old;
 665:       }
 666:     else 
 667:       {          
 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;
 676:       }
 677:   }
 678: 
 679:   public AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes)
 680:   {
 681:     return removeAttributes(old, attributes.getAttributeNames());
 682:   }
 683: 
 684:   public AttributeSet removeAttributes(AttributeSet old, Enumeration names)
 685:   {
 686:     if (old instanceof MutableAttributeSet)
 687:       {
 688:         ((MutableAttributeSet)old).removeAttributes(names);
 689:         if (old.getAttributeCount() < getCompressionThreshold())
 690:           {
 691:             SmallAttributeSet small = createSmallAttributeSet(old);
 692:             if (!sharedAttributeSets.containsKey(small))
 693:               sharedAttributeSets.put(small,small);
 694:             old = (AttributeSet) sharedAttributeSets.get(small);
 695:           }
 696:         return old;
 697:       }
 698:     else 
 699:       {          
 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;
 708:       }    
 709:   }
 710: 
 711: 
 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.
 714: 
 715:   public static Object getStaticAttribute(Object key)
 716:     throws NotImplementedException
 717:   {
 718:     throw new InternalError("not implemented");
 719:   }
 720:   
 721:   public static Object getStaticAttributeKey(Object key)
 722:     throws NotImplementedException
 723:   {
 724:     throw new InternalError("not implemented");
 725:   }
 726: 
 727:   public static void readAttributeSet(ObjectInputStream in, MutableAttributeSet a)
 728:     throws ClassNotFoundException, IOException, NotImplementedException
 729:   {
 730:     throw new InternalError("not implemented");
 731:   }
 732:   
 733:   public static void writeAttributeSet(ObjectOutputStream out, AttributeSet a)
 734:     throws IOException, NotImplementedException
 735:   {
 736:     throw new InternalError("not implemented");
 737:   }
 738: 
 739:   public void readAttributes(ObjectInputStream in, MutableAttributeSet a)
 740:     throws ClassNotFoundException, IOException, NotImplementedException 
 741:   {
 742:     throw new InternalError("not implemented");
 743:   }
 744: 
 745:   public void writeAttributes(ObjectOutputStream out, AttributeSet a)
 746:     throws IOException, NotImplementedException
 747:   {
 748:     throw new InternalError("not implemented");
 749:   }
 750: 
 751:   /**
 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.
 758:    *
 759:    * @param key the key to register as static attribute key
 760:    */
 761:   public static void registerStaticAttributeKey(Object key)
 762:   {
 763:     staticAttributeKeys.add(key);
 764:   }
 765: }