Skip to content

Instantly share code, notes, and snippets.

@RustyKnight
Created January 14, 2022 03:28
Show Gist options
  • Save RustyKnight/6c93b97c49ade0f3e0d146dab0fb9159 to your computer and use it in GitHub Desktop.
Save RustyKnight/6c93b97c49ade0f3e0d146dab0fb9159 to your computer and use it in GitHub Desktop.
A DocumentFilter which allows filters to be chained together
import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.awt.Toolkit;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;
import javax.swing.text.JTextComponent;
/**
* A single DocumentFilter can be set on an AbstractDocument. There may be times
* when you wish to perform multiple filtering of the data as it is added to a
* Document. By extending the ChainedDocumentFilter instead of the
* DocumentFilter you will receive added flexibility in that the filter can be
* used stand alone or with other ChainedDocumentFilters.
*
* Whenever one filter step fails, the chaining of the filters is also
* terminated. In this case is it recommended you invoke the
* provideErrorFeedback() method to provide user feedback.
*/
// Original source https://tips4java.wordpress.com/2009/10/18/chaining-document-filters/
abstract class ChainedDocumentFilter extends DocumentFilter {
private DocumentFilter filter;
/**
* Standard constructor for standalone usage
*/
public ChainedDocumentFilter() {
this(null);
}
/**
* Constructor used when further filtering is required after this filter has
* been applied.
*/
public ChainedDocumentFilter(DocumentFilter filter) {
setFilter(filter);
}
/**
* Get the next filter in the chain.
*
* @return the next filter in the chain
*/
public DocumentFilter getFilter() {
return filter;
}
/**
* Set the next filter in the chain
*
* @param filter
*/
public void setFilter(DocumentFilter filter) {
this.filter = filter;
}
/**
* Install this filter on the AbstractDocument
*
* @param components the text components that will use this filter
*/
public void installFilter(JTextComponent... components) {
for (JTextComponent component : components) {
Document doc = component.getDocument();
if (doc instanceof AbstractDocument) {
((AbstractDocument) doc).setDocumentFilter(this);
}
}
}
/**
* Remove this filter from the AbstractDocument
*
* @param compoents remove the filter from the specified text components
*/
public void uninstallFilter(JTextComponent... components) {
for (JTextComponent component : components) {
Document doc = component.getDocument();
if (doc instanceof AbstractDocument) {
((AbstractDocument) doc).setDocumentFilter(null);
}
}
}
/**
* Provide appropriate LAF feedback when a filter error occurs.
*/
protected void provideErrorFeedback() {
LookAndFeel laf = UIManager.getLookAndFeel();
if (laf == null) {
Toolkit.getDefaultToolkit().beep();
} else {
KeyboardFocusManager fm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
Component component = fm.getFocusOwner();
laf.provideErrorFeedback(component);
}
}
@Override
public void insertString(DocumentFilter.FilterBypass fb, int offs, String str, AttributeSet a)
throws BadLocationException {
if (filter == null) {
super.insertString(fb, offs, str, a);
} else {
filter.insertString(fb, offs, str, a);
}
}
@Override
public void replace(DocumentFilter.FilterBypass fb, int offs, int length, String str, AttributeSet a)
throws BadLocationException {
if (filter == null) {
super.replace(fb, offs, length, str, a);
} else {
filter.replace(fb, offs, length, str, a);
}
}
@Override
public void remove(DocumentFilter.FilterBypass fb, int offset, int length)
throws BadLocationException {
if (filter == null) {
super.remove(fb, offset, length);
} else {
filter.remove(fb, offset, length);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment