user3531731
user3531731

Reputation: 49

Automatically Coloring the Java Keywords when i am open a file in JTextpane

i want apply the colors to the java keywords. When i am open a file it raise the Attempt to mutate in notification exception and program not shown completely and colors also not applied. Here is my code,

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FileDialog;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

public class JavaKeywordsColor extends javax.swing.JFrame {

    private JTextPane textPane;
    private Color color = Color.BLACK;
    private int i = 0;
    private JPanel noWrapPanel;
    private JScrollPane scrollpane;
    private JTextField status;
    private StyledDocument d;
    private String[] keywords = {"import ", "class ", "int ", "public ", "private"};

    public JavaKeywordsColor() {
        initComponents();
    }

    private void initComponents() {
        tp = new javax.swing.JTabbedPane();
        jMenuBar1 = new javax.swing.JMenuBar();
        jMenu1 = new javax.swing.JMenu();
        Open = new javax.swing.JMenuItem();
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        jMenu1.setText("File");
        Open.setText("Open");
        Open.addActionListener(new java.awt.event.ActionListener() {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                OpenActionPerformed(evt);
            }
        });
        jMenu1.add(Open);
        jMenuBar1.add(jMenu1);
        setJMenuBar(jMenuBar1);
        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(tp, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE));
        layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(tp, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 279, Short.MAX_VALUE));
        pack();
    }// </editor-fold>                        

    private void OpenActionPerformed(java.awt.event.ActionEvent evt) {
        FileDialog fd = new FileDialog(JavaKeywordsColor.this, "Select File", FileDialog.LOAD);
        fd.setVisible(true);
        String title;
        String sts;
        if (fd.getFile() != null) {
            sts = fd.getDirectory() + fd.getFile();
            title = fd.getFile();
            BufferedReader br = null;
            StringBuffer str = new StringBuffer("");
            try {
                br = new BufferedReader(new FileReader(sts));
                String line;
                try {
                    while ((line = br.readLine()) != null) {
                        str.append(line + "\n");
                    }
                } catch (IOException ex) {
                    Logger.getLogger(JavaKeywordsColor.class.getName()).log(Level.SEVERE, null, ex);
                }
            } catch (FileNotFoundException ex) {
                Logger.getLogger(JavaKeywordsColor.class.getName()).log(Level.SEVERE, null, ex);
            }
            String t = str.toString();
            final JInternalFrame internalFrame = new JInternalFrame("", true, true);
            textPane = new JTextPane();
            // textPane.setEditorKit(new WrapEditorKit());
            d = textPane.getStyledDocument();
            d.addDocumentListener(new DocumentListener() {
                @Override
                public void insertUpdate(DocumentEvent de) {
                    jColorActionPerformed(de);
                }

                @Override
                public void removeUpdate(DocumentEvent de) {
                    jColorActionPerformed(de);
                }

                @Override
                public void changedUpdate(DocumentEvent de) {
                    jColorActionPerformed(de);
                }
            });
            Document doc = textPane.getDocument();
            textPane.setFont(new java.awt.Font("Miriam Fixed", 0, 13));
            internalFrame.add(textPane);
            i += 1;
            internalFrame.setName("Doc" + i);
            this.add(new javax.swing.JScrollPane(textPane));
            noWrapPanel = new JPanel(new BorderLayout());
            noWrapPanel.add(textPane);
            scrollpane = new JScrollPane(noWrapPanel);
            internalFrame.add(scrollpane);
            internalFrame.setTitle(title);
            tp.add(internalFrame);
            tp.setSelectedIndex(i - 1);
            internalFrame.setVisible(true);
            textPane.setText(t);
        }
    }

    private void jColorActionPerformed(DocumentEvent evt) {
        replace(textPane);
    }

    public void replace(javax.swing.JTextPane jp) {
        int cur = jp.getCaretPosition();
        StyleContext sc = StyleContext.getDefaultStyleContext();
        AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.BLUE);
        for (int ii = 0; ii < keywords.length; ii++) {
            int fromIndex = 0;
            String msg = keywords[ii];
            int nol = 0;     //number of lines upto keyword
            int len = 1;
            while (len != -1) {
                len = jp.getText().indexOf(msg, fromIndex);
                jp.setSelectedTextColor(Color.RED);
                if (len != -1) {
                    try {
                        nol = countLine(jp.getText(0, len + 1));
                    } catch (BadLocationException ex) {
                        break;
                    }
                    fromIndex = len + msg.length();
                    System.out.println("len = " + len + " nol=" + nol);
                    len -= nol;
                    jp.select(len, len + msg.length());
                    System.out.println("Selected Text = " + jp.getSelectedText());
                    jp.replaceSelection("");
                    jp.setCaretPosition(len);
                    jp.setCharacterAttributes(aset, false);
                    jp.replaceSelection(msg);
                }
            }
        }
        aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.BLACK);
        jp.setCharacterAttributes(aset, false);
        jp.setCaretPosition(cur);
    }

    public int countLine(String str) {
        int n = 0;
        for (int ii = 0; ii < str.length(); ii++) {
            if (str.charAt(ii) == '\n') {
                n++;
            }
        }
        return n;
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new JavaKeywordsColor().setVisible(true);
            }
        });
    }
    private javax.swing.JMenuItem Open;
    private javax.swing.JMenu jMenu1;
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JTabbedPane tp;
}

Upvotes: 0

Views: 259

Answers (1)

Holger
Holger

Reputation: 298359

The message of the exception makes it pretty clear. You are not allowed to mutate the document while the listener notification is in progress. The reason is quite clear: the modification would trigger new notifications and in programs like yours, either a StackOverflowError or an infinite loop would be provoked. It also implies that the might be reports for newer modifications at a time where other listeners not yet have learned about the old ones.

You have to schedule the modification to a later time and protect your code from reacting on your own modifications. Scheduling the update to a later time can also help regarding the performance when the user is typing fastly as you don’t want to parse an entire document on every inserted character.

So you should replace the part where you add the DocumentListener

d.addDocumentListener(new DocumentListener() {
    @Override
    public void insertUpdate(DocumentEvent de) {
        jColorActionPerformed(de);
    }

    @Override
    public void removeUpdate(DocumentEvent de) {
        jColorActionPerformed(de);
    }

    @Override
    public void changedUpdate(DocumentEvent de) {
        jColorActionPerformed(de);
    }
});

to something like this:

final class UpdateTrigger implements DocumentListener, ActionListener {
  final Timer timer=new Timer(150, this);
  boolean enabled=true;
  UpdateTrigger() {
    timer.setRepeats(false);
  }
  public void insertUpdate(DocumentEvent e) {
    if(enabled) timer.restart();
  }
  public void removeUpdate(DocumentEvent e) {
    if(enabled) timer.restart();
  }
  public void changedUpdate(DocumentEvent e) {
    if(enabled) timer.restart();
  }
  public void actionPerformed(ActionEvent e) {
    enabled=false;
    try { jColorActionPerformed(null); }
    finally { enabled=true; }
  }
}
d.addDocumentListener(new UpdateTrigger());

You might find out then that your code has more issues which are beyond the scope of your question. By the way, just highlighting keywords is not enough for a correct syntax highlighting. You need a parser which understands the syntactical structure, especially comments and String literals, even for the minimum highlighting.

You should study Chapter 3 of the The Java® Language Specification to get the complete picture.

Upvotes: 2

Related Questions