4

In a JTextPane the performance of highlighting a text with ~50000 lines is very slow. Do I have any chance to increase the preformance?

Here is a SSCE

import java.awt.*; 
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
 
public class TextFieldUnicode {
    private JTextPane textPane;
    private JTextField textField;
    private Document doc;
    private Highlighter hilit = new DefaultHighlighter();
    private Highlighter.HighlightPainter painter =  new DefaultHighlighter.DefaultHighlightPainter(Color.GREEN);

    private TextFieldUnicode() throws BadLocationException {
     this.textPane = new JTextPane();
     this.textField = new JTextField();
     this.doc = new DefaultStyledDocument();     
    
     generateSomeText();     
     addTextFieldListener();  
     createAndShowGUI(textField);
    } //end of constructor
     
    private void generateSomeText() throws BadLocationException {
     for(int i=0;i<=50000;i++) {
      doc.insertString(doc.getLength(), "hello world, hello stackoverflow, here is some generated text "+i+"\n", null);
     }
     textPane.setDocument(doc);
    }
    
    //here, the text will be highlighter after a search query is inserted
    private void addTextFieldListener() {
     
     textField.addActionListener( new ActionListener() {   
   @Override
   public void actionPerformed(ActionEvent arg0) {
    float start = System.nanoTime(); //start meausuring time
    
       textPane.setHighlighter(hilit);     

    String query = textField.getText();
    String text = textPane.getText();
       text = text.replaceAll("[\n]+", "");
    
    if(text != null) {     
     int index = text.indexOf(query); //get index of word      
     int len = query.length();    //get length of word

     while ( index >= 0 ) {
      try {
       textPane.getHighlighter().addHighlight(index, index+len, painter);
      } catch (BadLocationException e) {
       e.printStackTrace();
      }   
       index = text.indexOf(query, (index+len));
     }        
    }
    float stop = System.nanoTime(); //stop meausuring time
    System.out.println("time="+(stop-start)/1000000000+"s");
   }
  });
    }
 
   private void createAndShowGUI(JTextField textField) {
        //Create and set up the window.
        JFrame frame = new JFrame("TextFieldDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(textField, BorderLayout.NORTH);
        frame.add(new JScrollPane(textPane), BorderLayout.CENTER);    
        frame.setSize(new Dimension(600, 600));
        frame.setVisible(true);
    }
 
    public static void main(String[] args) throws BadLocationException {
     new TextFieldUnicode();
    }
}

And here is a screenshot of my example: it takes approx. 2 seconds on an i5@1,9GHz/8GByte Ram when I highlight the e letters. Any suggestions of how to do it in a better way?enter image description here

Ramses
  • 652
  • 2
  • 8
  • 30
  • Maybe the algorithm you're using is not efficient ? – Sharcoux Feb 22 '15 at 21:33
  • 3
    Maybe the idea of a `JTextPane` with 50,000 lines is already a bad idea? – user207421 Feb 22 '15 at 23:11
  • 1
    Check this to get some ideas http://java-sl.com/JEditorPanePerformance.html and please post your code to be analysed – StanislavL Feb 23 '15 at 05:59
  • 1
    Didn't take 2 seconds on my machine. time=0.26843545s, time=0.13421772s, time=0.13421772s. And +1 for the SSCCE – Can't Tell Feb 23 '15 at 12:28
  • @Can't Tell: thanks. it took **time=1.178s, time=0.373, time=0.337s** on my machine. Nonetheless, do you see any optimization potential in my code? – Ramses Feb 23 '15 at 12:53
  • @user3300710: why do you run a `replaceAll` first? – Willem Van Onsem Feb 23 '15 at 13:50
  • 1
    @user3300710: perhaps you can improve the algorithm by only highlighting the lines that are [actually visible](http://stackoverflow.com/questions/13165617/how-to-determine-which-lines-are-visible-in-scrollable-jtextarea)? – Willem Van Onsem Feb 23 '15 at 13:52
  • @CommuSoft: a JtextPane has a different implementation than a JTextComponent, see [here](http://stackoverflow.com/questions/19765489/jtextpane-highlighting-issue) . This is why I use replaceAll before highlighting the text. – Ramses Feb 23 '15 at 14:07
  • @CommuSoft: ok, I'll study it.. – Ramses Feb 23 '15 at 14:08

1 Answers1

2

As someone in the comments pointed out you can try only highlighting the visible text. This end up a lot quicker. Here is an example using your code (it can probably be improved):

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.JViewport;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Document;
import javax.swing.text.Highlighter;

public class TextFieldUnicode {

  private JScrollPane scrollPane;

  private JTextPane textPane;

  private JTextField textField;

  private Document doc;

  private Highlighter hilit = new DefaultHighlighter();

  private Highlighter.HighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(
      Color.GREEN);

  private TextFieldUnicode() throws BadLocationException {
    this.textPane = new JTextPane();
    this.textField = new JTextField();
    this.doc = new DefaultStyledDocument();
    this.scrollPane = new JScrollPane(this.textPane);
    this.textPane.setHighlighter(this.hilit);

    generateSomeText();
    addTextFieldListener();
    createAndShowGUI(this.textField);
  } // end of constructor

  private void generateSomeText() throws BadLocationException {
    for (int i = 0; i <= 50000; i++) {
      doc.insertString(doc.getLength(),
          "hello world, hello stackoverflow, here is some generated text " + i
              + "\n", null);
    }
    textPane.setDocument(doc);
  }

  // here, the text will be highlighter after a search query is inserted
  private void addTextFieldListener() {
    textField.addActionListener(new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent arg0) {
        paintHighlights();
      }
    });
    textPane.addComponentListener(new ComponentListener() {

      @Override
      public void componentShown(ComponentEvent e) {
      }

      @Override
      public void componentResized(ComponentEvent e) {
      }

      @Override
      public void componentMoved(ComponentEvent e) {
        paintHighlights();
      }

      @Override
      public void componentHidden(ComponentEvent e) {
      }
    });
  }

  private void paintHighlights() {
    float start = System.nanoTime(); // start measuring time

    textPane.getHighlighter().removeAllHighlights();

    String query = textField.getText();
    String text = null;

    JViewport viewport = scrollPane.getViewport();
    Rectangle viewRect = viewport.getViewRect();

    Point p = viewRect.getLocation();
    int startIndex = textPane.viewToModel(p);

    p.x += viewRect.width;
    p.y += viewRect.height;
    int endIndex = textPane.viewToModel(p);

    DefaultStyledDocument document = (DefaultStyledDocument) textPane
        .getDocument();
    try {
      text = document.getText(startIndex, endIndex - startIndex);
    } catch (BadLocationException ex) {
      ex.printStackTrace();
    }

    if (text != null) {
      int index = text.indexOf(query); // get index of word
      int len = query.length(); // get length of word

      while (index >= 0) {
        try {
          textPane.getHighlighter().addHighlight(startIndex + index,
              startIndex + index + len, painter);
        } catch (BadLocationException e) {
          e.printStackTrace();
        }

        index = text.indexOf(query, (index + len));
      }
    }
    float stop = System.nanoTime(); // stop meausuring time
    System.out.println("time=" + (stop - start) / 1000000000 + "s");
  }

  private void createAndShowGUI(JTextField textField) {
    // Create and set up the window.
    JFrame frame = new JFrame("TextFieldDemo");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(textField, BorderLayout.NORTH);
    frame.add(this.scrollPane, BorderLayout.CENTER);
    frame.setSize(new Dimension(600, 600));
    frame.setVisible(true);
  }

  public static void main(String[] args) throws BadLocationException {
    new TextFieldUnicode();
  }
}
SamYonnou
  • 2,068
  • 1
  • 19
  • 23