com.fathzer.soft.ajlib.swing.TextSearcher Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ajlib Show documentation
Show all versions of ajlib Show documentation
A-Jlib is a simple java library with Swing widgets, utilities
and other stuff
package com.fathzer.soft.ajlib.swing;
import java.text.Normalizer;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Document;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import com.fathzer.soft.ajlib.swing.widget.HTMLPane;
/**
* A class that searches for a text in a JTextComponent and highlights all
* occurrences of that text.
* It could be used, for instance, with an HTMLPane.
* It supports case and diacritical sensitive/insensitive search.
*
* @author Jean-Marc Astesana
* License: LGPL v3
* This code was inspired by an example from Kim Topley (ISBN: 0 13
* 083292 8 - Publisher: Prentice Hall)
* @see HTMLPane
*/
public class TextSearcher {
protected JTextComponent comp;
private boolean caseSensitive;
private boolean diacriticalSensitive;
private String text;
private int[] offsets;
private int searchedNormalizedLength;
/**
* Constructor.
*
The searched text is set to null.
* @see #TextSearcher(JTextComponent, String)
*/
public TextSearcher(JTextComponent comp) {
this(comp, null);
}
/**
* Constructor.
*
By default, the searcher is not case sensitive nor diacritical sensitive.
* @param comp The text component in which to search
*/
public TextSearcher(JTextComponent comp, String searchedText) {
this.comp = comp;
this.caseSensitive = false;
this.diacriticalSensitive = false;
this.text = searchedText;
search();
}
/**
* Searches for this.text and updates the offset of all the occurrences.
*/
private void search() {
if (text == null || text.isEmpty()) {
this.offsets = new int[0];
return;
}
// Look for the text we are given
// 1?) Extract a normalized form of the text in which to search
String content = null;
try {
Document d = comp.getDocument();
content = normalize(d.getText(0, d.getLength()));
} catch (BadLocationException e) {
// Cannot happen
throw new RuntimeException(e);
}
// 2?) Get the normalized form of the searched text
text = normalize(text);
// 3?) Search for the searched text
List indexes = new LinkedList();
int lastIndex = 0;
int size = 0;
searchedNormalizedLength = text.length();
while ((lastIndex = content.indexOf(text, lastIndex)) != -1) {
indexes.add(lastIndex);
size++;
lastIndex = lastIndex + searchedNormalizedLength;
}
offsets = new int[size];
Iterator iter = indexes.iterator();
for (int i = 0; i < offsets.length; i++) {
offsets[i] = iter.next();
}
}
/**
* Gets the normalized version of a text.
* The normalized version is lowercased if the TextSearcher is not case
* sensitive and has no diacritical marks if it is diacritical insensitive.
* @param content
* @return a String
*/
private String normalize(String content) {
if (!isCaseSensitive()) {
content = content.toLowerCase();
}
if (!isDiacriticalSensitive()) {
content = Normalizer.normalize(content, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
}
return content;
}
/**
* Tests whether the search is case sensitive or not.
*
* @return true if the search is case sensitive.
*/
public boolean isCaseSensitive() {
return this.caseSensitive;
}
/**
* Sets if the search is case sensitive or not.
* By default, the search is not case sensitive.
* This method updates the offsets attribute.
* @param caseSensitive true to have a case sensitive search
* @see #getOffsets()
*/
public void setCaseSentitive(boolean caseSensitive) {
if (caseSensitive != this.caseSensitive) {
this.caseSensitive = caseSensitive;
search();
}
}
/**
* Tests whether the search is diacritical sensitive or not.
* @return true if the search is diacritical sensitive.
*/
public boolean isDiacriticalSensitive() {
return diacriticalSensitive;
}
/**
* Sets if the search is diacritical sensitive or not.
* By default, the search is not diacritical sensitive.
* This method updates the offsets attribute.
* @param diacriticalSensitive true to have a diacritical sensitive search
* @see #getOffsets()
*/
public void setDiacriticalSensitive(boolean diacriticalSensitive) {
if (diacriticalSensitive != this.diacriticalSensitive) {
this.diacriticalSensitive = diacriticalSensitive;
search();
}
}
/** Gets the offsets of the searched text.
*
These offsets can be passed to the highlight methods
* @return a int array (its dimension is 0 if the text was not found)
* @see #highlight(int, javax.swing.text.Highlighter.HighlightPainter)
* @see #highlight(int[], javax.swing.text.Highlighter.HighlightPainter)
*/
public int[] getOffsets() {
return offsets;
}
/** Highlights some portions of the text.
* @param offsets the start of the portions to highlight (these offsets are returned by getOffsets method).
* null to remove all highlights.
* @param painter The painter to be used (or null to use the default one).
* @throws BadLocationException if offsets are out of text bounds
* @see #getOffsets()
*/
public void highlight(int[] offsets, Highlighter.HighlightPainter painter) throws BadLocationException {
if (painter==null) {
painter = DefaultHighlighter.DefaultPainter;
}
// Remove any existing highlights for last word
Highlighter highlighter = comp.getHighlighter();
highlighter.removeAllHighlights();
if (offsets!=null) {
for (int i = 0; i < offsets.length; i++) {
highlighter.addHighlight(offsets[i], offsets[i] + this.searchedNormalizedLength, painter);
}
// Scroll the text pane in order to view the first occurrence.
if (offsets.length>0) {
comp.scrollRectToVisible(comp.modelToView(offsets[0]));
}
}
}
/** Highlights a portion of the text.
* @param offset the start of the portion to highlight (one of the offsets returned by getOffsets method).
* @param painter The painter to be used (or null to use the default one).
* @throws BadLocationException if offset is out of text bounds
* @see #getOffsets()
*/
public void highlight(int offset, Highlighter.HighlightPainter painter) throws BadLocationException {
if (painter==null) {
painter = DefaultHighlighter.DefaultPainter;
}
// Remove any existing highlights for last word
Highlighter highlighter = comp.getHighlighter();
highlighter.removeAllHighlights();
highlighter.addHighlight(offset, offset + this.searchedNormalizedLength, painter);
// Scroll the text pane in order to view the first occurrence.
comp.scrollRectToVisible(comp.modelToView(offset));
}
public void setSearchedText(String text) {
this.text = text;
search();
}
}