4

that's going to be a long question so bear with me :)

My Application

I'm developing a Java (with JFrame GUI) desktop application that does the following:

  1. Scan (.txt) files.
  2. Parses some numbers from these files, performs some calculations on them and finally stores the results in String variables.
  3. Outputs these numbers in a special (table) format. (Note: the format includes some Unicode (Arabic) Characters.)

Problem

The first two parts went smoothly. However when I came to the 3th part (the formatted output) I didn't know how to display this special format so,

  • What is the best way to display a special formatted output (table) in Java?

Note: Formatter is not going to help because it has no proper support for tables.

Solution One:

I did my research and found that I could use JEditorPane, since it can display special formats such as "html". So I decided to create an "html" page with the needed (table) format and then display this page on [JEditorPane][4]. I did that and it went smoothly until I wanted to change some html elements' values to the parsed numbers from those (.txt) files.

  • How can I have an access to an html element(e.g. <td></td>) and change its value?

Note that the (.html) is loaded inside JEditorPane using setPage(url).

The Unicode characters are displayed properly but I couldn't change some of the elements values (e.g. I want to change the value of <td> 000,000,000 </td> to <td> MainController.getCurrentTotalPayment() </td>

Solution Two:

I've found a workaround to this which involves using HTMLDocument and HTMLEditorKit, That way I can create the (.html) using HTMLEditorKit from scratch and display it on the JEditorPane using kit.insertHTML.

I have successfully added the content using the above method and I also was able to add the parsed numbers from (.txt) files because I have them stored in my (MainController) class. Unfortunately, the Unicode Arabic characters were not displayed properly.

  • How can I display these Unicode characters properly?

So the first solution lacks the access to html elements and the second lacks the Unicode support!

My colleagues advised me to use JSP code in the html document that can have an access to my MainController.java class. Therefore, loading the page into JEditorPane with the html elements changed already. Isn't there a way to do that without the help of JSP?

Some other people recommended the use of JTidy but isn't there a way to do it within Java's JDK?

I'm open to all possible solutions. Please help.

My Code: Some code content were omitted because they are not relevant

MainController.java

class MainController 
{
    private static String currentTotalPayment;

    public static void main(String[] args) 
    {
        CheckBankFilesView cbfView = new CheckBankFilesView();
        cbfView.setVisible(true);
    }

    public static void setCurrentTotalPayment(String totalPayment) {
        MainController.currentTotalPayment = totalPayment;
    }

    public static String getCurrentTotalPayment() {
        return currentTotalPayment;
    }
}

MyFormattedOuputSolutionOne.java:

public class MyFormattedOuputSolutionOne extends javax.swing.JFrame {

    private void MyFormattedOuputSolutionOne() {

        jPanel1 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        myFormattedOuput = new javax.swing.JEditorPane();

        myFormattedOuput.setContentType("text/html");
        //myFormattedOuput.setContentType("text/html; charset=UTF-8"); //Doesn't seem to work

        myFormattedOuput.setEditable(false);

        jScrollPane1.setViewportView(myFormattedOuput);

        myFormattedOuput.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);

        try{
            myFormattedOuput.setPage(getClass().getResource("resources/emailFormat2.html"));

            //How can I edit/change html elements loaded in 'myFormattedOuput'?
        }catch(Exception e){
        }
    }
}

MyFormattedOuputSolutionTwo.java:

public class MyFormattedOuputSolutionTwo extends javax.swing.JFrame {

    private void MyFormattedOuputSolutionTwo() {

        jPanel1 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        myFormattedOuput = new javax.swing.JEditorPane();

        myFormattedOuput.setContentType("text/html");
        //myFormattedOuput.setContentType("text/html; charset=UTF-8"); //Doesn't seem to work

        myFormattedOuput.setEditable(false);

        jScrollPane1.setViewportView(myFormattedOuput);

        HTMLEditorKit kit = new HTMLEditorKit();

        HTMLDocument doc = new HTMLDocument();

        myFormattedOuput.setEditorKit(kit);

        myFormattedOuput.setDocument(doc);

        myFormattedOuput.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);

        try{
            // Tried to set the charset in <head> but it doesn't work!
            //kit.insertHTML(doc, 1, "<meta http-equiv = \"Content-Type\" content = \"text/html; charset=UTF-8\">", 0, 0, HTML.Tag.META);

            kit.insertHTML(doc, doc.getLength(), "<label> السلام عليكم ورحمة الله وبركاته ,,, </label>", 0, 0, null); //Encoding problem
            kit.insertHTML(doc, doc.getLength(), "<br/>", 0, 0, null); // works fine
            kit.insertHTML(doc, doc.getLength(), MainController.getCurrentTotalPayment(), 0, 0, null); // works fine

            //How can I solve the Unicode problem above?
        }catch(Exception e){
        }
    }
}

htmlFormatTable.html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">

<html>

    <head>

        <meta http-equiv = "Content-Type" content = "text/html; charset=UTF-8">

    </head>

    <body>

        <label> السلام عليكم ورحمة الله وبركاته ,,, </label>
        <br/>
        <label>  الأخوة الكرام نفيدكم بتفاصيل المدفوعات لشهر  </label> XX/143X </label>  هـ كما هو موضح ادناه  </label>
        <br/>
        <table align="right"  border="1" width="600" cellpadding="5" cellspacing="0">
            <tr char="utf-8" bgcolor="cccccc" align="center">
                <td colspan="3">   <label> تفاصيل مدفوعات بنك الرياض </label>  <img src="..\images\riyadh.gif" width="65" height="15"/> </td>
            </tr>
            <tr align="center">
                <td></td>
                <td id="cell1">0,000,000.00</td>
                <td align="right"> معاشات </td>
            </tr>
            <tr align="center">
                <td></td>
                <td id="cell2">0,000,000.00</td>
                <td align="right"> أخطار </td>
            </tr>
            <tr align="center">
                <td bgcolor="cccccc"> المجموع </td>
                <td bgcolor="cccccc">   0,000,000.00 <label> ريال سعودي </label> </td>
                <td></td>
            </tr>
        </table>
        <br/>
        <label> شاكرين لكم حسن تعاونكم ...... </label>
        <br/>
        <label> فريق العمليات بقسم الحاسب الآلي </label>

    </body>

</html>

Thank you for reading my long multiple questions thread and cannot wait for your answer.

Update:

Thanks to @Howard for this insight, if I replace the arabic character with its corresponding unicode (e.g. ب = \u0628) it works fine but there must be a way to do it without the need to replace each character, right?

RyanSF
  • 73
  • 1
  • 6
  • Can you test your code `MyFormattedOuputSolutionTwo.java` with escaped unicode strings, e.g. "\u069B\u06B2"? – Howard Jul 10 '11 at 14:50
  • @Howard I just did, that's what I got: [GeneratedOutput](http://i.min.us/idRT7q.png). is it the correct encoding? (note: the '?'s are my arabic characters) – RyanSF Jul 10 '11 at 16:29
  • When I googled these characters I found that they are in fact correct and they are Arabic but I don't think that it is efficient to replace every character with its corresponding charset, right? Anyways thank you for this amazing insight. I'll add it to the question. – RyanSF Jul 10 '11 at 16:39

1 Answers1

3

Solution One

It is possible to edit HTML loaded into JEditorPane. Here's the complete code based on your MyFormattedOuputSolutionOne.java:

import java.awt.ComponentOrientation;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JEditorPane;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.SimpleAttributeSet;

public class MyFormattedOuputSolutionOne extends javax.swing.JFrame {

    private MyFormattedOuputSolutionOne() {
        super("MyFormattedOuputSolutionOne");
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);

        JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
        final JEditorPane myFormattedOuput = new javax.swing.JEditorPane();

        getContentPane().add(jScrollPane1);

        myFormattedOuput.setContentType("text/html");
        //myFormattedOuput.setContentType("text/html; charset=UTF-8"); //Doesn't seem to work

        myFormattedOuput.setEditable(false);

        jScrollPane1.setViewportView(myFormattedOuput);

        myFormattedOuput.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);

        try{
            myFormattedOuput.setPage(getClass().getResource("htmlFormatTable.html"));
            myFormattedOuput.addPropertyChangeListener(new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    if ("page".equals(evt.getPropertyName())) {
                        Document doc = myFormattedOuput.getDocument();
                        Element html = doc.getRootElements()[0];
                        Element body = html.getElement(1);
                        Element table = body.getElement(1);
                        try {
                            Element tr2 = table.getElement(1);
                            Element tr2td1 = tr2.getElement(0);
                            doc.insertString(tr2td1.getStartOffset(), "1: 123,456",
                                             SimpleAttributeSet.EMPTY);

                            Element tr3 = table.getElement(2);
                            Element tr3td1 = tr3.getElement(0);
                            doc.insertString(tr3td1.getStartOffset(), "2: 765.123",
                                             SimpleAttributeSet.EMPTY);
                        } catch (BadLocationException e) {
                            e.printStackTrace();
                        }
                        myFormattedOuput.removePropertyChangeListener(this);
                    }
                }

            });

            //How can I edit/change html elements loaded in 'myFormattedOuput'?

        } catch(Exception e){
            e.printStackTrace();
        }

        pack();
        setSize(700, 400);
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new MyFormattedOuputSolutionOne();
            }
        });
    }
}

It loads document asynchronously and waits for page to be loaded. When page is loaded, it accesses the elements of the document to search for elements and inserts text into the first <td> in the 2nd and 3rd row of the table.

By the way your HTML is not valid! You should clean it up. When you do it, the indexes of the document elements will change and you'll have to adjust code which finds the insertion points.

The window looks this way: Solution One window screen shot

Solution Two

I've found no issues with encoding. The characters display correctly. Yet I had to set the encoding of Java files to UTF-8 in the Eclipse project.

Solution Two with correctly displayed Arabic

Solution Three

Have you considered using JTable to display table of results in the UI?


The HTML might look this way:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">

<html>
    <head>
        <meta http-equiv = "Content-Type" content = "text/html; charset=UTF-8">
    </head>
    <body>
        <p> السلام عليكم ورحمة الله وبركاته ,,, </p>

        <p>  الأخوة الكرام نفيدكم بتفاصيل المدفوعات لشهر  </p>
        <p>XX/143X </p>
        <p>  هـ كما هو موضح ادناه  </p>

        <table align="right"  border="1" width="600" cellpadding="5" cellspacing="0">
            <tr bgcolor="cccccc" align="center">
                <td colspan="3">تفاصيل مدفوعات بنك الرياض <img src="..\images\riyadh.gif" width="65" height="15"/></td>
            </tr>
            <tr align="center">
                <td></td>
                <td id="cell1">0,000,000.00</td>
                <td align="right">معاشات</td>
            </tr>
            <tr align="center">
                <td></td>
                <td id="cell2">0,000,000.00</td>
                <td align="right">أخطار</td>
            </tr>
            <tr align="center">
                <td bgcolor="cccccc">المجموع</td>
                <td bgcolor="cccccc">0,000,000.00 ريال سعودي</td>
                <td></td>
            </tr>
        </table>
        <p> شاكرين لكم حسن تعاونكم ...... </p>
        <p> فريق العمليات بقسم الحاسب الآلي </p>
    </body>
</html>

Since I don't understand a word, I cannot propose a better formatting. First of all, <label> elements are allowed only in <form>. You had a sequence of three <label>s above the table where only one of them had opening <label> tag, there were three closing </label> tags. I made them all into <p>; however if you meant them to be headers for table columns, you should have used a table row with three <th> elements.

With this structure of the HTML, <table> element in the HTML tree would be at index 4, i.e. you should change the line

Element table = body.getElement(1);

to

Element table = body.getElement(4);

The indexes 0–3 are now <p> elements.


As a side note, instead of editing HTML after loading it into JEditorPane, which loads it into text model of HTMLDocument, you could edit your HTML document before passing to setPage so that it already contains the correct data in <td> elements. Since JEditorPane.setPage method accepts only URL, then your choice would be read which accepts an InputStream and Object which describes the model (should be instance of HTMLDocument in your case). StringBufferInputStream is the best candidate for this task yet it was deprecated because it cannot correctly read UTF-8 characters. Having this in mind, you would rather use String.getBytes("UTF-8") function (since J2SE 6), and ByteArrayInputStream, your HTML declares the encoding and JEditorPane would respect it when reading.

Alexey Ivanov
  • 11,541
  • 4
  • 39
  • 68
  • Thank you @Alexey Ivanov I've done some workaround regarding this problem and if I have had your answer earlier I would've tried it! Regarding the JTable, it's a bit risky to do it in JTable because I'll be sending this format using the user's default email client and I'm not sure if it will copy it nicely to the email. One more thing, can you tell me of ways to clean up my html code?! – RyanSF Aug 13 '11 at 04:26
  • @RyanSF I update my answer with cleaned up HTML. As I say, the structure could be changed depending on the semantics, which I cannot induce because I don't understand what's written. As for `JTable` is for displaying table data in the UI, you didn't say in your question that you needed to send this HTML by mail. The only thing I don't understand is why you need `JEditorPane` if you want to send HTML via mail. Just prepare your HTML: take your template and add the data you got extracted from the .txt files into the HTML, and the send it in an email. – Alexey Ivanov Aug 14 '11 at 08:19
  • 1
    @Alexey Brilliant answer. I'd up-vote except I already did that before. 1 (tiny) suggestion re. the screenshots. If you drag them over a white page (e.g. a new tab in your browser would show a white content area) before making the screen shot, it'll end up with colors for the BG of the rounded corners that match the 'white' BG of SO/SE. For more tips on screen shots, see the [How do I create a screenshot to illustrate a post?](http://meta.stackexchange.com/questions/99734/how-do-i-create-a-screenshot-to-illustrate-a-post) FAQ. – Andrew Thompson Aug 14 '11 at 08:34
  • @Andrew Thanks! That's how I did them, except for a larger one where background windows can be seen through the window title bar. I made both of them using Alt+PrtSc, which grab the active window only; for some reason Windows fills the pixels in the corners of windows with black. As for experience, I agree with you: If I had made the corners white or transparent, the windows on the screenshots would have looked better. I'll try to that part better next time :) – Alexey Ivanov Aug 14 '11 at 13:33
  • @Andrew Yes, it does. And I usually take the whole screen into the clipboard and then crop the image. Nevertheless, thanks for the pointer: it was a nice reading. (At work I tried hard to make people save their screenshots in PNG instead of JPEG.) – Alexey Ivanov Aug 14 '11 at 14:25
  • OK, I'll clean up my screenshots to make the answer *brilliant*. By the way, in your screenshot the first string I insert into the HTMLDocument is not in the table but in the introductory text before the table. I wonder why it's in the wrong place. – Alexey Ivanov Aug 14 '11 at 16:08
  • @Andrew Now it looks much better without dark corners (which actually contain Aero shadow). Additionally I resized both screen shots, and the first one is crisper: SO proved to resize my original image and fonts did not look as clear as they were on the original image. Thanks for urging me to do that ;), this late-night answer was not as good as it should have been. – Alexey Ivanov Aug 14 '11 at 18:25
  • *"I resized both screen shots, and the first one is crisper:"* Very nice! *"..this late-night answer was not as good as it should have been."* Deserves another 6 or 7 up-votes IMO. – Andrew Thompson Aug 14 '11 at 18:33