We are looking for a way to keep two types of texts for a JTextField
And finally the actual text will save inside our database NOT the Display text
We have created the text converter for the Display BUT we don't know how to apply this Scenario to the JTextField
I went through this and I came up with this solution.
First we need to show the user persian/arabic digits when they have chosen Persian/Arabic keyboard in their OS and they're typing numbers inside their JTextField. To achieve this I wrote this custom DocumentFilter :
public class PersianNumberDocumentFilter extends DocumentFilter {
private static final String REPLACE_CHARS = "0123456789.";
@Override
public void insertString(FilterBypass fb, int offset, String text,
AttributeSet attr) throws BadLocationException {
if (text != null && !text.isEmpty() && REPLACE_CHARS.contains(text)) {
text = doSwap(text);
}
super.insertString(fb, offset, text, attr);
}
@Override
public void replace(FilterBypass fb, int offset, int length, String text,
AttributeSet attrs) throws BadLocationException {
if (text != null && !text.isEmpty() && REPLACE_CHARS.contains(text)) {
text = doSwap(text);
}
super.replace(fb, offset, length, text, attrs);
}
@Override
public void remove(FilterBypass fb, int offset, int length)
throws BadLocationException {
super.remove(fb, offset, length);
}
public String doSwap(String text) {
InputContext context = InputContext.getInstance();
String windowsKeyboardSelected = context.getLocale().toString();
if (!windowsKeyboardSelected.contains("fa") && !windowsKeyboardSelected.contains("ar")) {
return text;
}
StringBuilder sb = new StringBuilder();
for (char c : text.toCharArray()) {
if (REPLACE_CHARS.contains(String.valueOf(c))) {
if (c == '.') {
c = ',';
} else {
c = (char) ('\u06F0' - '0' + c);
//c = (char) ('\u0660' - '0' + c); Arabic Number Digits
}
}
sb.append(c);
}
return sb.toString();
}
}
Next we need a utility class for number and text conversion from and to Persian/Arabic. I wrote this utility class for this matter:
public class PersianNumber {
private static HashMap<Character, String> unicodeMap = new HashMap<>();
private static HashMap<Character, String> unicodeMapArabic = new HashMap<>();
private static HashMap<Character, String> unicodeMapViceVera = new HashMap<>();
static {
//English/Persian Numbers Map
unicodeMap.put('0', "\u06F0");
unicodeMap.put('1', "\u06F1");
unicodeMap.put('2', "\u06F2");
unicodeMap.put('3', "\u06F3");
unicodeMap.put('4', "\u06F4");
unicodeMap.put('5', "\u06F5");
unicodeMap.put('6', "\u06F6");
unicodeMap.put('7', "\u06F7");
unicodeMap.put('8', "\u06F8");
unicodeMap.put('9', "\u06F9");
//English/Arabic Numbers Map
unicodeMapArabic.put('0',"\u0660");
unicodeMapArabic.put('1',"\u0661");
unicodeMapArabic.put('2',"\u0662");
unicodeMapArabic.put('3',"\u0663");
unicodeMapArabic.put('4',"\u0664");
unicodeMapArabic.put('5',"\u0665");
unicodeMapArabic.put('6',"\u0666");
unicodeMapArabic.put('7',"\u0667");
unicodeMapArabic.put('8',"\u0668");
unicodeMapArabic.put('9',"\u0669");
//Persian English Numbers Map
unicodeMapViceVera.put('\u06F0', "0");
unicodeMapViceVera.put('\u06F1', "1");
unicodeMapViceVera.put('\u06F2', "2");
unicodeMapViceVera.put('\u06F3', "3");
unicodeMapViceVera.put('\u06F4', "4");
unicodeMapViceVera.put('\u06F5', "5");
unicodeMapViceVera.put('\u06F6', "6");
unicodeMapViceVera.put('\u06F7', "7");
unicodeMapViceVera.put('\u06F8', "8");
unicodeMapViceVera.put('\u06F9', "9");
//Arabic English Numbers Map
unicodeMapViceVera.put('\u0660', "0");
unicodeMapViceVera.put('\u0661', "1");
unicodeMapViceVera.put('\u0662', "2");
unicodeMapViceVera.put('\u0663', "3");
unicodeMapViceVera.put('\u0664', "4");
unicodeMapViceVera.put('\u0665', "5");
unicodeMapViceVera.put('\u0666', "6");
unicodeMapViceVera.put('\u0667', "7");
unicodeMapViceVera.put('\u0668', "8");
unicodeMapViceVera.put('\u0669', "9");
}
public static String getPersianNumber(String englishNumber) {
if (!englishNumber.matches("-?\\d+(\\.\\d+)?")) {
throw new NumberFormatException("the input [" + englishNumber + "] is not a number!");
}
char[] numChars = englishNumber.toCharArray();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < numChars.length; i++) {
builder.append(unicodeMap.get(numChars[i]));
}
return builder.toString();
}
public static String getEnglishNumber(String persianNumber) {
char[] numChars = persianNumber.toCharArray();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < numChars.length; i++) {
String value = unicodeMapViceVera.get(numChars[i]);
if(value == null){
builder.setLength(0);
builder.append(persianNumber);
break;
}
builder.append(unicodeMapViceVera.get(numChars[i]));
}
return builder.toString();
}
public static boolean isAPersianNumber(String persianNumber){
char[] numChars = persianNumber.toCharArray();
for (int i = 0; i < numChars.length; i++) {
String value = unicodeMapViceVera.get(numChars[i]);
if(value == null){
return false;
}
}
return true;
}
public static String convertToStringWithPersianNumber(String input){
StringBuilder sb = new StringBuilder();
for(char c : input.toCharArray()){
String ch = unicodeMap.get(c);
if(ch != null){
sb.append(ch);
continue;
}
sb.append(c);
}
return sb.toString();
}
public static String convertToStringWithEnglishNumber(String input){
StringBuilder sb = new StringBuilder();
for(char c : input.toCharArray()){
String ch = unicodeMapViceVera.get(c);
if(ch != null){
sb.append(ch);
continue;
}
sb.append(c);
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(getEnglishNumber("۹۶۵۹۴۵۴"));
System.out.println(getPersianNumber("9999999999999651221"));
System.out.println(getPersianNumber(59683399623213L + ""));
}
}
Finally We need a Custom JTextField because we want control our inputs and outputs and convert them accordingly from-to our needs:
public class JPersianTextField2 extends JTextField {
public JPersianTextField2() {
// Hopefully someday (Oracle Love Iranians) and create This Locale for us
Locale locale = new Locale("fa", "IR");
// Locale locale = new Locale("ar", "KW");
ComponentOrientation farsiOrientation = ComponentOrientation.getOrientation(locale);
super.applyComponentOrientation(farsiOrientation);
PlainDocument doc = (PlainDocument) super.getDocument();
doc.setDocumentFilter(new PersianNumberDocumentFilter());
super.setColumns(25);
}
@Override
public String getText() {
String tmp = super.getText();
if (tmp != null && !tmp.isEmpty())
return PersianNumber2.convertToStringWithEnglishNumber(tmp);
else
return super.getText();
}
@Override
public void setText(String t) {
if (t != null && !t.isEmpty())
super.setText(PersianNumber2.convertToStringWithPersianNumber(t));
}
}
With this custom JTextField in place you can convert to English digit when you want to fetch the result of the JTextField and convert to Persian digit when you want to set a String which has English digits.
And Finally here is the test for the component :
public class MyFormatterTest extends JFrame {
public MyFormatterTest() {
setTitle("Example");
JPanel panel = new JPanel();
JLabel label = new JLabel("text :");
JPersianTextField2 tf = new JPersianTextField2();
tf.setText("سلام من مهدی هستم برنامه نویس ارشد 31 ساله"); //Hi This is mehdi, 31 years old Sr. software developer
panel.add(label);
panel.add(tf);
JButton button2 = new JButton();
button2.setText("getText");
button2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println(tf.getText());
}
});
panel.add(button2);
getContentPane().add(panel, BorderLayout.SOUTH);
pack();
}
public static void main(String[] args) {
//Available Locales inside Your JVM
for (Locale l : Locale.getAvailableLocales()) {
System.out.println(l.getCountry() + ":" + l.getLanguage());
}
MyFormatterTest mfe = new MyFormatterTest();
mfe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mfe.setVisible(true);
}
}
Hope this is useful for you ...