src/share/classes/javax/swing/text/rtf/RTFReader.java

Print this page




 203     return Color.black;
 204 }
 205 
 206 /** Called by the superclass when a new RTF group is begun.
 207  *  This implementation saves the current <code>parserState</code>, and gives
 208  *  the current destination a chance to save its own state.
 209  * @see RTFParser#begingroup
 210  */
 211 public void begingroup()
 212 {
 213     if (skippingCharacters > 0) {
 214         /* TODO this indicates an error in the RTF. Log it? */
 215         skippingCharacters = 0;
 216     }
 217 
 218     /* we do this little dance to avoid cloning the entire state stack and
 219        immediately throwing it away. */
 220     Object oldSaveState = parserState.get("_savedState");
 221     if (oldSaveState != null)
 222         parserState.remove("_savedState");

 223     Dictionary<String, Object> saveState = (Dictionary<String, Object>)((Hashtable)parserState).clone();
 224     if (oldSaveState != null)
 225         saveState.put("_savedState", oldSaveState);
 226     parserState.put("_savedState", saveState);
 227 
 228     if (rtfDestination != null)
 229         rtfDestination.begingroup();
 230 }
 231 
 232 /** Called by the superclass when the current RTF group is closed.
 233  *  This restores the parserState saved by <code>begingroup()</code>
 234  *  as well as invoking the endgroup method of the current
 235  *  destination.
 236  * @see RTFParser#endgroup
 237  */
 238 public void endgroup()
 239 {
 240     if (skippingCharacters > 0) {
 241         /* NB this indicates an error in the RTF. Log it? */
 242         skippingCharacters = 0;
 243     }
 244 

 245     Dictionary<Object, Object> restoredState = (Dictionary<Object, Object>)parserState.get("_savedState");
 246     Destination restoredDestination = (Destination)restoredState.get("dst");
 247     if (restoredDestination != rtfDestination) {
 248         rtfDestination.close(); /* allow the destination to clean up */
 249         rtfDestination = restoredDestination;
 250     }
 251     Dictionary oldParserState = parserState;
 252     parserState = restoredState;
 253     if (rtfDestination != null)
 254         rtfDestination.endgroup(oldParserState);
 255 }
 256 
 257 protected void setRTFDestination(Destination newDestination)
 258 {
 259     /* Check that setting the destination won't close the
 260        current destination (should never happen) */
 261     Dictionary previousState = (Dictionary)parserState.get("_savedState");

 262     if (previousState != null) {
 263         if (rtfDestination != previousState.get("dst")) {
 264             warning("Warning, RTF destination overridden, invalid RTF.");
 265             rtfDestination.close();
 266         }
 267     }
 268     rtfDestination = newDestination;
 269     parserState.put("dst", rtfDestination);
 270 }
 271 
 272 /** Called by the user when there is no more input (<i>i.e.</i>,
 273  * at the end of the RTF file.)
 274  *
 275  * @see OutputStream#close
 276  */
 277 public void close()
 278     throws IOException
 279 {
 280     Enumeration docProps = documentAttributes.getAttributeNames();
 281     while(docProps.hasMoreElements()) {
 282         Object propName = docProps.nextElement();
 283         target.putProperty(propName,
 284                            documentAttributes.getAttribute(propName));
 285     }
 286 
 287     /* RTFParser should have ensured that all our groups are closed */
 288 
 289     warning("RTF filter done.");
 290 
 291     super.close();
 292 }
 293 
 294 /**
 295  * Handles a parameterless RTF keyword. This is called by the superclass
 296  * (RTFParser) when a keyword is found in the input stream.
 297  *
 298  * @returns <code>true</code> if the keyword is recognized and handled;
 299  *          <code>false</code> otherwise
 300  * @see RTFParser#handleKeyword


 611 
 612 static char[] readCharset(java.net.URL href)
 613      throws IOException
 614 {
 615     return readCharset(href.openStream());
 616 }
 617 
 618 /** An interface (could be an entirely abstract class) describing
 619  *  a destination. The RTF reader always has a current destination
 620  *  which is where text is sent.
 621  *
 622  *  @see RTFReader
 623  */
 624 interface Destination {
 625     void handleBinaryBlob(byte[] data);
 626     void handleText(String text);
 627     boolean handleKeyword(String keyword);
 628     boolean handleKeyword(String keyword, int parameter);
 629 
 630     void begingroup();
 631     void endgroup(Dictionary oldState);
 632 
 633     void close();
 634 }
 635 
 636 /** This data-sink class is used to implement ignored destinations
 637  *  (e.g. {\*\blegga blah blah blah} )
 638  *  It accepts all keywords and text but does nothing with them. */
 639 class DiscardingDestination implements Destination
 640 {
 641     public void handleBinaryBlob(byte[] data)
 642     {
 643         /* Discard binary blobs. */
 644     }
 645 
 646     public void handleText(String text)
 647     {
 648         /* Discard text. */
 649     }
 650 
 651     public boolean handleKeyword(String text)
 652     {
 653         /* Accept and discard keywords. */
 654         return true;
 655     }
 656 
 657     public boolean handleKeyword(String text, int parameter)
 658     {
 659         /* Accept and discard parameterized keywords. */
 660         return true;
 661     }
 662 
 663     public void begingroup()
 664     {
 665         /* Ignore groups --- the RTFReader will keep track of the
 666            current group level as necessary */
 667     }
 668 
 669     public void endgroup(Dictionary oldState)
 670     {
 671         /* Ignore groups */
 672     }
 673 
 674     public void close()
 675     {
 676         /* No end-of-destination cleanup needed */
 677     }
 678 }
 679 
 680 /** Reads the fonttbl group, inserting fonts into the RTFReader's
 681  *  fontTable dictionary. */
 682 class FonttblDestination implements Destination
 683 {
 684     int nextFontNumber;
 685     Integer fontNumberKey = null;
 686     String nextFontFamily;
 687 
 688     public void handleBinaryBlob(byte[] data)
 689     { /* Discard binary blobs. */ }


 719         if (keyword.charAt(0) == 'f') {
 720             nextFontFamily = keyword.substring(1);
 721             return true;
 722         }
 723 
 724         return false;
 725     }
 726 
 727     public boolean handleKeyword(String keyword, int parameter)
 728     {
 729         if (keyword.equals("f")) {
 730             nextFontNumber = parameter;
 731             return true;
 732         }
 733 
 734         return false;
 735     }
 736 
 737     /* Groups are irrelevant. */
 738     public void begingroup() {}
 739     public void endgroup(Dictionary oldState) {}
 740 
 741     /* currently, the only thing we do when the font table ends is
 742        dump its contents to the debugging log. */
 743     public void close()
 744     {
 745         Enumeration<Integer> nums = fontTable.keys();
 746         warning("Done reading font table.");
 747         while(nums.hasMoreElements()) {
 748             Integer num = nums.nextElement();
 749             warning("Number " + num + ": " + fontTable.get(num));
 750         }
 751     }
 752 }
 753 
 754 /** Reads the colortbl group. Upon end-of-group, the RTFReader's
 755  *  color table is set to an array containing the read colors. */
 756 class ColortblDestination implements Destination
 757 {
 758     int red, green, blue;
 759     Vector<Color> proTemTable;


 789 
 790     public boolean handleKeyword(String keyword, int parameter)
 791     {
 792         if (keyword.equals("red"))
 793             red = parameter;
 794         else if (keyword.equals("green"))
 795             green = parameter;
 796         else if (keyword.equals("blue"))
 797             blue = parameter;
 798         else
 799             return false;
 800 
 801         return true;
 802     }
 803 
 804     /* Colortbls don't understand any parameterless keywords */
 805     public boolean handleKeyword(String keyword) { return false; }
 806 
 807     /* Groups are irrelevant. */
 808     public void begingroup() {}
 809     public void endgroup(Dictionary oldState) {}
 810 
 811     /* Shouldn't see any binary blobs ... */
 812     public void handleBinaryBlob(byte[] data) {}
 813 }
 814 
 815 /** Handles the stylesheet keyword. Styles are read and sorted
 816  *  into the three style arrays in the RTFReader. */
 817 class StylesheetDestination
 818     extends DiscardingDestination
 819     implements Destination
 820 {
 821     Dictionary<Integer, StyleDefiningDestination> definedStyles;
 822 
 823     public StylesheetDestination()
 824     {
 825         definedStyles = new Hashtable<Integer, StyleDefiningDestination>();
 826     }
 827 
 828     public void begingroup()
 829     {


1081 
1082         /* It would probably be more efficient to use the
1083          * resolver property of the attributes set for
1084          * implementing rtf groups,
1085          * but that's needed for styles. */
1086 
1087         /* update the cached attribute dictionaries */
1088         characterAttributes = new SimpleAttributeSet();
1089         characterAttributes.addAttributes(characterParent);
1090         parserState.put("chr", characterAttributes);
1091 
1092         paragraphAttributes = new SimpleAttributeSet();
1093         paragraphAttributes.addAttributes(paragraphParent);
1094         parserState.put("pgf", paragraphAttributes);
1095 
1096         sectionAttributes = new SimpleAttributeSet();
1097         sectionAttributes.addAttributes(sectionParent);
1098         parserState.put("sec", sectionAttributes);
1099     }
1100 
1101     public void endgroup(Dictionary oldState)
1102     {
1103         characterAttributes = (MutableAttributeSet)parserState.get("chr");
1104         paragraphAttributes = (MutableAttributeSet)parserState.get("pgf");
1105         sectionAttributes   = (MutableAttributeSet)parserState.get("sec");
1106     }
1107 
1108     public void close()
1109     {
1110     }
1111 
1112     public boolean handleKeyword(String keyword)
1113     {
1114         if (keyword.equals("ulnone")) {
1115             return handleKeyword("ul", 0);
1116         }
1117 
1118         {
1119             RTFAttribute attr = straightforwardAttributes.get(keyword);
1120             if (attr != null) {
1121                 boolean ok;


1245             Number item;
1246 
1247             tabAlignment = TabStop.ALIGN_LEFT;
1248             item = (Number)(parserState.get("tab_alignment"));
1249             if (item != null)
1250                 tabAlignment = item.intValue();
1251             tabLeader = TabStop.LEAD_NONE;
1252             item = (Number)(parserState.get("tab_leader"));
1253             if (item != null)
1254                 tabLeader = item.intValue();
1255             if (keyword.equals("tb"))
1256                 tabAlignment = TabStop.ALIGN_BAR;
1257 
1258             parserState.remove("tab_alignment");
1259             parserState.remove("tab_leader");
1260 
1261             TabStop newStop = new TabStop(tabPosition, tabAlignment, tabLeader);
1262             Dictionary<Object, Object> tabs;
1263             Integer stopCount;
1264 
1265             tabs = (Dictionary<Object, Object>)parserState.get("_tabs");


1266             if (tabs == null) {
1267                 tabs = new Hashtable<Object, Object>();
1268                 parserState.put("_tabs", tabs);
1269                 stopCount = Integer.valueOf(1);
1270             } else {
1271                 stopCount = (Integer)tabs.get("stop count");
1272                 stopCount = Integer.valueOf(1 + stopCount.intValue());
1273             }
1274             tabs.put(stopCount, newStop);
1275             tabs.put("stop count", stopCount);
1276             parserState.remove("_tabs_immutable");
1277 
1278             return true;
1279         }
1280 
1281         if (keyword.equals("s") &&
1282             paragraphStyles != null) {
1283             parserState.put("paragraphStyle", paragraphStyles[parameter]);
1284             return true;
1285         }


1403 
1404     /**
1405      * Calculates the current paragraph attributes (with keys
1406      * as given in StyleConstants) from the current parser state.
1407      *
1408      * @returns a newly created MutableAttributeSet.
1409      * @see StyleConstants
1410      */
1411     MutableAttributeSet currentParagraphAttributes()
1412     {
1413         /* NB if there were a mutableCopy() method we should use it */
1414         MutableAttributeSet bld = new SimpleAttributeSet(paragraphAttributes);
1415 
1416         Integer stateItem;
1417 
1418         /*** Tab stops ***/
1419         TabStop tabs[];
1420 
1421         tabs = (TabStop[])parserState.get("_tabs_immutable");
1422         if (tabs == null) {
1423             Dictionary workingTabs = (Dictionary)parserState.get("_tabs");

1424             if (workingTabs != null) {
1425                 int count = ((Integer)workingTabs.get("stop count")).intValue();
1426                 tabs = new TabStop[count];
1427                 for (int ix = 1; ix <= count; ix ++)
1428                     tabs[ix-1] = (TabStop)workingTabs.get(Integer.valueOf(ix));
1429                 parserState.put("_tabs_immutable", tabs);
1430             }
1431         }
1432         if (tabs != null)
1433             bld.addAttribute(Constants.Tabs, tabs);
1434 
1435         Style paragraphStyle = (Style)parserState.get("paragraphStyle");
1436         if (paragraphStyle != null)
1437             bld.setResolveParent(paragraphStyle);
1438 
1439         return bld;
1440     }
1441 
1442     /**
1443      * Calculates the current section attributes




 203     return Color.black;
 204 }
 205 
 206 /** Called by the superclass when a new RTF group is begun.
 207  *  This implementation saves the current <code>parserState</code>, and gives
 208  *  the current destination a chance to save its own state.
 209  * @see RTFParser#begingroup
 210  */
 211 public void begingroup()
 212 {
 213     if (skippingCharacters > 0) {
 214         /* TODO this indicates an error in the RTF. Log it? */
 215         skippingCharacters = 0;
 216     }
 217 
 218     /* we do this little dance to avoid cloning the entire state stack and
 219        immediately throwing it away. */
 220     Object oldSaveState = parserState.get("_savedState");
 221     if (oldSaveState != null)
 222         parserState.remove("_savedState");
 223     @SuppressWarnings("unchecked")
 224     Dictionary<String, Object> saveState = (Dictionary<String, Object>)((Hashtable)parserState).clone();
 225     if (oldSaveState != null)
 226         saveState.put("_savedState", oldSaveState);
 227     parserState.put("_savedState", saveState);
 228 
 229     if (rtfDestination != null)
 230         rtfDestination.begingroup();
 231 }
 232 
 233 /** Called by the superclass when the current RTF group is closed.
 234  *  This restores the parserState saved by <code>begingroup()</code>
 235  *  as well as invoking the endgroup method of the current
 236  *  destination.
 237  * @see RTFParser#endgroup
 238  */
 239 public void endgroup()
 240 {
 241     if (skippingCharacters > 0) {
 242         /* NB this indicates an error in the RTF. Log it? */
 243         skippingCharacters = 0;
 244     }
 245 
 246     @SuppressWarnings("unchecked")
 247     Dictionary<Object, Object> restoredState = (Dictionary<Object, Object>)parserState.get("_savedState");
 248     Destination restoredDestination = (Destination)restoredState.get("dst");
 249     if (restoredDestination != rtfDestination) {
 250         rtfDestination.close(); /* allow the destination to clean up */
 251         rtfDestination = restoredDestination;
 252     }
 253     Dictionary<Object, Object> oldParserState = parserState;
 254     parserState = restoredState;
 255     if (rtfDestination != null)
 256         rtfDestination.endgroup(oldParserState);
 257 }
 258 
 259 protected void setRTFDestination(Destination newDestination)
 260 {
 261     /* Check that setting the destination won't close the
 262        current destination (should never happen) */
 263     @SuppressWarnings("unchecked")
 264     Dictionary<Object, Object> previousState = (Dictionary)parserState.get("_savedState");
 265     if (previousState != null) {
 266         if (rtfDestination != previousState.get("dst")) {
 267             warning("Warning, RTF destination overridden, invalid RTF.");
 268             rtfDestination.close();
 269         }
 270     }
 271     rtfDestination = newDestination;
 272     parserState.put("dst", rtfDestination);
 273 }
 274 
 275 /** Called by the user when there is no more input (<i>i.e.</i>,
 276  * at the end of the RTF file.)
 277  *
 278  * @see OutputStream#close
 279  */
 280 public void close()
 281     throws IOException
 282 {
 283     Enumeration<?> docProps = documentAttributes.getAttributeNames();
 284     while(docProps.hasMoreElements()) {
 285         Object propName = docProps.nextElement();
 286         target.putProperty(propName,
 287                            documentAttributes.getAttribute(propName));
 288     }
 289 
 290     /* RTFParser should have ensured that all our groups are closed */
 291 
 292     warning("RTF filter done.");
 293 
 294     super.close();
 295 }
 296 
 297 /**
 298  * Handles a parameterless RTF keyword. This is called by the superclass
 299  * (RTFParser) when a keyword is found in the input stream.
 300  *
 301  * @returns <code>true</code> if the keyword is recognized and handled;
 302  *          <code>false</code> otherwise
 303  * @see RTFParser#handleKeyword


 614 
 615 static char[] readCharset(java.net.URL href)
 616      throws IOException
 617 {
 618     return readCharset(href.openStream());
 619 }
 620 
 621 /** An interface (could be an entirely abstract class) describing
 622  *  a destination. The RTF reader always has a current destination
 623  *  which is where text is sent.
 624  *
 625  *  @see RTFReader
 626  */
 627 interface Destination {
 628     void handleBinaryBlob(byte[] data);
 629     void handleText(String text);
 630     boolean handleKeyword(String keyword);
 631     boolean handleKeyword(String keyword, int parameter);
 632 
 633     void begingroup();
 634     void endgroup(Dictionary<Object, Object> oldState);
 635 
 636     void close();
 637 }
 638 
 639 /** This data-sink class is used to implement ignored destinations
 640  *  (e.g. {\*\blegga blah blah blah} )
 641  *  It accepts all keywords and text but does nothing with them. */
 642 class DiscardingDestination implements Destination
 643 {
 644     public void handleBinaryBlob(byte[] data)
 645     {
 646         /* Discard binary blobs. */
 647     }
 648 
 649     public void handleText(String text)
 650     {
 651         /* Discard text. */
 652     }
 653 
 654     public boolean handleKeyword(String text)
 655     {
 656         /* Accept and discard keywords. */
 657         return true;
 658     }
 659 
 660     public boolean handleKeyword(String text, int parameter)
 661     {
 662         /* Accept and discard parameterized keywords. */
 663         return true;
 664     }
 665 
 666     public void begingroup()
 667     {
 668         /* Ignore groups --- the RTFReader will keep track of the
 669            current group level as necessary */
 670     }
 671 
 672     public void endgroup(Dictionary<Object, Object> oldState)
 673     {
 674         /* Ignore groups */
 675     }
 676 
 677     public void close()
 678     {
 679         /* No end-of-destination cleanup needed */
 680     }
 681 }
 682 
 683 /** Reads the fonttbl group, inserting fonts into the RTFReader's
 684  *  fontTable dictionary. */
 685 class FonttblDestination implements Destination
 686 {
 687     int nextFontNumber;
 688     Integer fontNumberKey = null;
 689     String nextFontFamily;
 690 
 691     public void handleBinaryBlob(byte[] data)
 692     { /* Discard binary blobs. */ }


 722         if (keyword.charAt(0) == 'f') {
 723             nextFontFamily = keyword.substring(1);
 724             return true;
 725         }
 726 
 727         return false;
 728     }
 729 
 730     public boolean handleKeyword(String keyword, int parameter)
 731     {
 732         if (keyword.equals("f")) {
 733             nextFontNumber = parameter;
 734             return true;
 735         }
 736 
 737         return false;
 738     }
 739 
 740     /* Groups are irrelevant. */
 741     public void begingroup() {}
 742     public void endgroup(Dictionary<Object, Object> oldState) {}
 743 
 744     /* currently, the only thing we do when the font table ends is
 745        dump its contents to the debugging log. */
 746     public void close()
 747     {
 748         Enumeration<Integer> nums = fontTable.keys();
 749         warning("Done reading font table.");
 750         while(nums.hasMoreElements()) {
 751             Integer num = nums.nextElement();
 752             warning("Number " + num + ": " + fontTable.get(num));
 753         }
 754     }
 755 }
 756 
 757 /** Reads the colortbl group. Upon end-of-group, the RTFReader's
 758  *  color table is set to an array containing the read colors. */
 759 class ColortblDestination implements Destination
 760 {
 761     int red, green, blue;
 762     Vector<Color> proTemTable;


 792 
 793     public boolean handleKeyword(String keyword, int parameter)
 794     {
 795         if (keyword.equals("red"))
 796             red = parameter;
 797         else if (keyword.equals("green"))
 798             green = parameter;
 799         else if (keyword.equals("blue"))
 800             blue = parameter;
 801         else
 802             return false;
 803 
 804         return true;
 805     }
 806 
 807     /* Colortbls don't understand any parameterless keywords */
 808     public boolean handleKeyword(String keyword) { return false; }
 809 
 810     /* Groups are irrelevant. */
 811     public void begingroup() {}
 812     public void endgroup(Dictionary<Object, Object> oldState) {}
 813 
 814     /* Shouldn't see any binary blobs ... */
 815     public void handleBinaryBlob(byte[] data) {}
 816 }
 817 
 818 /** Handles the stylesheet keyword. Styles are read and sorted
 819  *  into the three style arrays in the RTFReader. */
 820 class StylesheetDestination
 821     extends DiscardingDestination
 822     implements Destination
 823 {
 824     Dictionary<Integer, StyleDefiningDestination> definedStyles;
 825 
 826     public StylesheetDestination()
 827     {
 828         definedStyles = new Hashtable<Integer, StyleDefiningDestination>();
 829     }
 830 
 831     public void begingroup()
 832     {


1084 
1085         /* It would probably be more efficient to use the
1086          * resolver property of the attributes set for
1087          * implementing rtf groups,
1088          * but that's needed for styles. */
1089 
1090         /* update the cached attribute dictionaries */
1091         characterAttributes = new SimpleAttributeSet();
1092         characterAttributes.addAttributes(characterParent);
1093         parserState.put("chr", characterAttributes);
1094 
1095         paragraphAttributes = new SimpleAttributeSet();
1096         paragraphAttributes.addAttributes(paragraphParent);
1097         parserState.put("pgf", paragraphAttributes);
1098 
1099         sectionAttributes = new SimpleAttributeSet();
1100         sectionAttributes.addAttributes(sectionParent);
1101         parserState.put("sec", sectionAttributes);
1102     }
1103 
1104     public void endgroup(Dictionary<Object, Object> oldState)
1105     {
1106         characterAttributes = (MutableAttributeSet)parserState.get("chr");
1107         paragraphAttributes = (MutableAttributeSet)parserState.get("pgf");
1108         sectionAttributes   = (MutableAttributeSet)parserState.get("sec");
1109     }
1110 
1111     public void close()
1112     {
1113     }
1114 
1115     public boolean handleKeyword(String keyword)
1116     {
1117         if (keyword.equals("ulnone")) {
1118             return handleKeyword("ul", 0);
1119         }
1120 
1121         {
1122             RTFAttribute attr = straightforwardAttributes.get(keyword);
1123             if (attr != null) {
1124                 boolean ok;


1248             Number item;
1249 
1250             tabAlignment = TabStop.ALIGN_LEFT;
1251             item = (Number)(parserState.get("tab_alignment"));
1252             if (item != null)
1253                 tabAlignment = item.intValue();
1254             tabLeader = TabStop.LEAD_NONE;
1255             item = (Number)(parserState.get("tab_leader"));
1256             if (item != null)
1257                 tabLeader = item.intValue();
1258             if (keyword.equals("tb"))
1259                 tabAlignment = TabStop.ALIGN_BAR;
1260 
1261             parserState.remove("tab_alignment");
1262             parserState.remove("tab_leader");
1263 
1264             TabStop newStop = new TabStop(tabPosition, tabAlignment, tabLeader);
1265             Dictionary<Object, Object> tabs;
1266             Integer stopCount;
1267 
1268             @SuppressWarnings("unchecked")
1269             Dictionary<Object, Object>tmp = (Dictionary)parserState.get("_tabs");
1270             tabs = tmp;
1271             if (tabs == null) {
1272                 tabs = new Hashtable<Object, Object>();
1273                 parserState.put("_tabs", tabs);
1274                 stopCount = Integer.valueOf(1);
1275             } else {
1276                 stopCount = (Integer)tabs.get("stop count");
1277                 stopCount = Integer.valueOf(1 + stopCount.intValue());
1278             }
1279             tabs.put(stopCount, newStop);
1280             tabs.put("stop count", stopCount);
1281             parserState.remove("_tabs_immutable");
1282 
1283             return true;
1284         }
1285 
1286         if (keyword.equals("s") &&
1287             paragraphStyles != null) {
1288             parserState.put("paragraphStyle", paragraphStyles[parameter]);
1289             return true;
1290         }


1408 
1409     /**
1410      * Calculates the current paragraph attributes (with keys
1411      * as given in StyleConstants) from the current parser state.
1412      *
1413      * @returns a newly created MutableAttributeSet.
1414      * @see StyleConstants
1415      */
1416     MutableAttributeSet currentParagraphAttributes()
1417     {
1418         /* NB if there were a mutableCopy() method we should use it */
1419         MutableAttributeSet bld = new SimpleAttributeSet(paragraphAttributes);
1420 
1421         Integer stateItem;
1422 
1423         /*** Tab stops ***/
1424         TabStop tabs[];
1425 
1426         tabs = (TabStop[])parserState.get("_tabs_immutable");
1427         if (tabs == null) {
1428             @SuppressWarnings("unchecked")
1429             Dictionary<Object, Object> workingTabs = (Dictionary)parserState.get("_tabs");
1430             if (workingTabs != null) {
1431                 int count = ((Integer)workingTabs.get("stop count")).intValue();
1432                 tabs = new TabStop[count];
1433                 for (int ix = 1; ix <= count; ix ++)
1434                     tabs[ix-1] = (TabStop)workingTabs.get(Integer.valueOf(ix));
1435                 parserState.put("_tabs_immutable", tabs);
1436             }
1437         }
1438         if (tabs != null)
1439             bld.addAttribute(Constants.Tabs, tabs);
1440 
1441         Style paragraphStyle = (Style)parserState.get("paragraphStyle");
1442         if (paragraphStyle != null)
1443             bld.setResolveParent(paragraphStyle);
1444 
1445         return bld;
1446     }
1447 
1448     /**
1449      * Calculates the current section attributes