Have I found a bug in Java? Regarding AttributeSet
I've been using a JTextPane (or my sub-classed version of one) in an application I've been writing, and so I've been working with Styles. My program wasn't acting the way I wanted, and I tracked the behavior to what I think is a bug in the AttributeSet.containsAttributes(AttributeSet attributes) method. I wrote the following short program to illustrate it:
import javax.swing.JTextPane;
import javax.swing.text.StyleConstants;
import javax.swing.text.Style;
public class StyleBug {
public static void main(String[] args) {
JTextPane textPane = new JTextPane();
textPane.setText("This is a test string");
Style bold = textPane.addStyle(BOLD, null);
StyleConstants.setBold(bold, true);
Style italic = textPane.addStyle(ITALIC, null);
StyleConstants.setItalic(italic, true);
int start = 5;
int end = 10;
textPane.getStyledDocument().setCharacterAttributes(start, end - start, textPane.getStyle(BOLD), false);
textPane.getStyledDocument().setCharacterAttributes(start, end - start, textPane.getStyle(ITALIC), false);
for(int i = start; i < end; i++)
System.out.println(textPane.getStyledDocument().getCharacterElement(i).getAttributes()
.containsAttributes(textPane.getStyle(BOLD))); //all print false
}
private static final S开发者_Go百科tring BOLD = "Bold";
private static final String ITALIC = "Italic";
}
Is this a bug, or am I missing something here?
If I comment out this line:
// textPane.getStyledDocument().setCharacterAttributes(start, end - start, textPane.getStyle(ITALIC), false);
Then it prints true for all of the elements. According to the Javadoc for setCharacterAttributes
it uses all of the Attributes from the Style you are passing in, so you are simply overriding the BOLD
selection with the ITALIC
selection.
EDIT:
I pulled up the debugger and got this array of attributes for getCharacterElement(5)
.
[0] javax.swing.text.StyleConstants$FontConstants "italic"
[1] java.lang.Boolean "true"
[2] javax.swing.text.StyleConstants "name"
[3] java.lang.String "Italic"
[4] javax.swing.text.StyleConstants$FontConstants "bold"
[5] java.lang.Boolean "true"
As you can see, the attributes are ordered in groups of 2. italic
is set to true, bold
is set to true, and the name
is set to "Italic"
. This likely means that only one name is allowed for a named attribute set for a character. Note that the unnamed attributes were merged correctly, so it is behaviourally what you want even if you can't see if a particular named attribute is applied to a character.
Bringer128 found the problem above, but I'll just clarify a little more.
When a Style is added to a JTextPane, the String that is passed as an argument is actually placed in the Style as an Attribute (not surprisingly, the NameAttribute). When the Style is applied to a range of characters, the NameAttribute is applied along with any other Attributes that have been set on the Style. So when my BOLD Style was applied, every character in that range had its BoldAttribute set to true and its NameAttribute set to "Bold". Then, when my ITALIC Style was applied, every character had its ItalicAttribute set to true, and then its NameAttribute set to "Italic". Then, when containsAttributes() checked if all Attributes in my BOLD Style were applied to those characters, it returned false because their NameAttributes had been changed from "Bold" to "Italic". I hope that didn't confuse anyone.
Here is my work around (I think it's actually simpler than the original code). The gist of it is I never use Styles or JTextPane.addStyle() at all; I just keep constant MutableAttributeSets.
import javax.swing.JTextPane;
import javax.swing.text.StyleConstants;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
public class StyleBugFix {
public static void main(String[] args) {
JTextPane textPane = new JTextPane();
textPane.setText("This is a test string");
StyleConstants.setBold(BOLD, true);
StyleConstants.setItalic(ITALIC, true);
int start = 5;
int end = 10;
textPane.getStyledDocument().setCharacterAttributes(start, end - start, BOLD, false);
textPane.getStyledDocument().setCharacterAttributes(start, end - start, ITALIC, false);
for(int i = start; i < end; i++)
System.out.println(textPane.getStyledDocument().getCharacterElement(i).getAttributes()
.containsAttributes(BOLD)); //all now print true
}
private static final MutableAttributeSet BOLD = new SimpleAttributeSet();
private static final MutableAttributeSet ITALIC = new SimpleAttributeSet();
}
Thanks again to Bringer128 for all his help.
精彩评论