Using Document Filters with the Japanese character sets
Not sure if this belongs here or on the Swing Topic but here goes:
I have been requested to restrict entry in a JTextField to English alphaNumeric and Full-width Katakana.
The East Asian language support also allows Hiragana and Half-width Katakana.
I have tried to attach a DocumentFilter. The filter employs a ValidateString method which strips all non (Latin) alphaNumerics as well as anything in the Hiragana, or Half-width Katakana ranges. The code is pretty simple (Most of the code below is dedicated to debugging):
publicclass KatakanaInputFilterextends DocumentFilter
{
privatestaticint LOW_KATAKANA_RANGE = 0x30A0;
privatestaticint LOW_HALF_KATAKANA_RANGE = 0xFF66;
privatestaticint HIGH_HALF_KATAKANA_RANGE = 0xFFEE;
privatestaticint LOW_HIRAGANA_RANGE = 0x3041;
public KatakanaInputFilter()
{
super();
}
@Override
publicvoid replace(FilterBypass fb,int offset,int length, String text,
AttributeSet attrs)throws BadLocationException
{
super.replace(fb, offset, length, validateString(text, offset),null);
}
@Override
publicvoid remove(FilterBypass fb,int offset,int length)
throws BadLocationException
{
super.remove(fb, offset, length);
}
// @Override
publicvoid insertString(FilterBypass fb,int offset, String string,
AttributeSet attr)throws BadLocationException
{
String newString =new String();
for (int i = 0; i < string.length(); i++)
{
int unicodePoint = string.codePointAt(i);
newString += String.format("[%x] ", unicodePoint);
}
String oldString =new String();
int len = fb.getDocument().getLength();
if (len > 0)
{
String fbText = fb.getDocument().getText(0, len);
for (int i = 0; i < len; i++)
{
int unicodePoint = fbText.codePointAt(i);
oldString += String.format("[%x] ", unicodePoint);
}
}
System.out.format("insertString %s into %s at location %d\n",
newString, oldString, offset);
super.insertString(fb, offset, validateString(string, offset), attr);
len = fb.getDocument().getLength();
if (len > 0)
{
String fbText = fb.getDocument().getText(0, len);
for (int i = 0; i < len; i++)
{
int unicodePoint = fbText.codePointAt(i);
oldString += String.format("[%x] ", unicodePoint);
}
}
System.out.format("document changed to %s\n\n", oldString);
}
public String validateString(String text,int offset)
{
if (text ==null)
{
returnnew String();
}
String validText =new String();
for (int i = 0; i < text.length(); i++)
{
int unicodePoint = text.codePointAt(i);
boolean acceptChar =false;
if (unicodePoint < LOW_KATAKANA_RANGE)
{
if ((unicodePoint < 0x30 || unicodePoint > 0x7a)
|| (unicodePoint > 0x3a && unicodePoint < 0x41)
|| (unicodePoint > 0x59 && unicodePoint < 0x61))
{
acceptChar =false;
}
else
{
acceptChar =true;
}
}
else
{
if ((unicodePoint >= LOW_HALF_KATAKANA_RANGE && unicodePoint <= HIGH_HALF_KATAKANA_RANGE)
|| (unicodePoint >= LOW_HIRAGANA_RANGE && unicodePoint <= LOW_HIRAGANA_RANGE))
{
acceptChar =false;
}
else
{
acceptChar =true;
}
}
if (acceptChar ==true)
{
System.out.format("Accepted code point = %x\n",
unicodePoint);
validText += text.charAt(i);
}
else
{
System.out.format("Rejected code point = %x\n",
unicodePoint);
}
}
String newString ="";
for (int i = 0; i < validText.length(); i++)
{
int unicodePoint = validText.codePointAt(i);
newString += String.format("[%x] ", unicodePoint);
}
System.out.format("ValidatedString = %s\n", newString);
return validText;
}
/**
* @param args
*/
publicstaticvoid main(String[] args)
{
Runnable runner =new Runnable()
{
publicvoid run()
{
JFrame frame =new JFrame("Katakana Input Filter");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(2, 2));
frame.add(new JLabel("Text"));
JTextField textFieldOne =new JTextField();
Document textDocOne = textFieldOne.getDocument();
DocumentFilter filterOne =new KatakanaInputFilter();
((AbstractDocument) textDocOne).setDocumentFilter(filterOne);
textFieldOne.setDocument(textDocOne);
frame.add(textFieldOne);
frame.setSize(250, 90);
frame.setVisible(true);
}
};
EventQueue.invokeLater(runner);
}
}
I run this code, use the language bar to switch to Full-width Katakana and type "y" followed by "u" which forms a valid Katakana character. I then used the language bar to switch to Hiragana and retyped the "Y" followed by "u". When the code sees the Hiragana codepoint generated by this key combination it rejects it. My debugging statements show that the document is properly updated. However, when I type the next character, I find that the previously rejected codePoint is being sent back to my insert method. It appears that the text somehow got cached in the composedTextContent of the JTextField.
Here is the output of the program when I follow the steps I just outlined:
insertString [ff59] into at location 0<== typed y (Katakana)
Accepted code point = ff59
ValidatedString = [ff59]
document changed to [ff59]
insertString [30e6] into at location 0 <== typed u (Katakana)
Accepted code point = 30e6
ValidatedString = [30e6]
document changed to [30e6]
insertString [30e6] [ff59] into at location 0 <== typed y (Hiragna)
Accepted code point = 30e6
Accepted code point = ff59
ValidatedString = [30e6] [ff59]
document changed to [30e6] [ff59]
insertString [30e6] [3086] into at location 0 <== typed u (Hiragana)
Accepted code point = 30e6
Rejected code point = 3086
ValidatedString = [30e6]
document changed to [30e6]
insertString [30e6] [3086] [ff59] into at location 0 <== typed u (Hiragana)
Accepted code point = 30e6
Rejected code point = 3086
Accepted code point = ff59
ValidatedString = [30e6] [ff59]
document changed to [30e6] [ff59]
As far as I can tell, the data in the document looks fine. But the JTextField does not have the same data as the document. At this point it is not displaying the ff59 codePoint as a "y" (as it does when first entering the Hiragana character). but it has somehow combined it with another codePoint to form a complete Hiragana character.
Can anyone see what it is that I am doing wrong? Any help would be appreciated as I am baffled at this point.

