Eu não uso DocumentFilter para esse tipo de tarefa, prefiro usar Document. Seguem as classes que uso:
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
// PlainDocument com limitação ao número máximo de caracteres armazenados.
// Valores de limite menores ou iguais a zero indicam que não haverá
// limitações ao número de caracteres.
public class FixedSizePlainDocument extends PlainDocument {
// Atributos exportados
private int maximumSize = 0;
// Construtor
public FixedSizePlainDocument(int maximumSize) {
this.maximumSize = maximumSize;
}
public FixedSizePlainDocument() {
this(0);
}
// getXXXX() / setXXXX()
public int getMaximumSize() {
return maximumSize;
}
public void setMaximumSize(int maximumSize) {
this.maximumSize = maximumSize;
}
// Redefinição do método herdado
@Override
public void insertString(int offset, String str, AttributeSet attr)
throws BadLocationException {
// Evita strings nulas ou vazias
if ((str == null) ||
(str.length() < 1)) {
return;
}
// Só faz checagem de tamanho se o limite for maior do que zero
if (maximumSize > 0) {
// Se o texto atual já chegou ao limite, sai
int curSize = this.getLength();
if (curSize >= maximumSize) {
return;
}
// Se a soma do comprimento atual do texto com o comprimento do
// trecho a ser inserido superar o limite ajustado, trunca o
// trecho a inserir para respeitar o limite
int strSize = str.length();
int sumSize = curSize + strSize;
if (sumSize > maximumSize) {
int lastPosition = Math.min(strSize, strSize - (sumSize - maximumSize));
if (lastPosition >= 0) {
str = str.substring(0, lastPosition);
}
}
}
// Insere a string
super.insertString(offset, str, attr);
}
// Redefinição do método herdado
@Override
public void replace(int offset, int length, String str, AttributeSet attr)
throws BadLocationException {
// Só faz checagem de tamanho se o limite for maior do que zero
if (maximumSize > 0) {
// Se a soma do comprimento atual do texto, menos o comprimento do trecho
// que será removido, mais o comprimento do trecho a ser inserido superar
// o limite ajustado, trunca o trecho inserido para respeitar o limite
int curSize = this.getLength();
int strSize = (str != null) ? str.length() : 0;
int sumSize = curSize - length + strSize;
if (sumSize > maximumSize) {
int lastPosition = Math.min(strSize, strSize - (sumSize - maximumSize));
if ((lastPosition >= 0) &&
(str != null)) {
str = str.substring(0, lastPosition);
}
}
}
// Substitui a string
super.replace(offset, length, str, attr);
}
}
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
// PlainDocument especializado para edição de números inteiros.
public class IntegerNumberPlainDocument extends FixedSizePlainDocument {
// Construtor
public IntegerNumberPlainDocument(int maximumSize) {
super(maximumSize);
}
public IntegerNumberPlainDocument() {
this(0);
}
// Redefinição do método herdado
@Override
public void insertString(int offset, String str, AttributeSet attr)
throws BadLocationException {
// Evita strings nulas ou vazias
if ((str == null) ||
(str.length() < 1)) {
return;
}
// Checa o primeiro caractere para saber se a string digitada
// é numérica ou alfabética. Normalmente isso é suficiente para
// nossas checagens, pois o mais comum é que esta rotina só receba
// um caractere por chamada. Porém, se <str> tiver comprimento
// maior do que 1 (por exemplo, ao colar texto com Ctrl+V), a
// seqüência gerada pela concatenação da string atual do documento
// com a nova string pode gerar um valor inválido. Dessa forma, é
// imprescindível validar posteriormente a string resultante quando
// chegar a hora de convertê-la para número
char c = str.charAt(0);
switch (c) {
// Caracteres numéricos
case '0': case '1': case '2':
case '3': case '4': case '5':
case '6': case '7': case '8':
case '9':
// ... Nada a fazer ...
break;
case '-': { // Sinal de menos (-)
int curSize = this.getLength();
if (curSize > 0) {
// Se a string atual no documento já contiver um sinal de
// menos, sai da rotina
String txt = this.getText(0, curSize);
if (txt.indexOf(String.valueOf(c)) != -1) {
return;
}
}
}
break;
default: // Demais caracteres
// Sai da rotina
return;
}
// Insere a string
super.insertString(offset, str, attr);
}
// Redefinição do método herdado
@Override
public void replace(int offset, int length, String str, AttributeSet attr)
throws BadLocationException {
if ((str != null) &&
(str.length() > 0)) {
// Checa o primeiro caractere para saber se a string digitada
// é numérica ou alfabética. Normalmente isso é suficiente para
// nossas checagens, pois o mais comum é que esta rotina só receba
// um caractere por chamada. Porém, se <str> tiver comprimento
// maior do que 1 (por exemplo, ao colar texto com Ctrl+V), a
// seqüência gerada pela concatenação da string atual do documento
// com a nova string pode gerar um valor inválido. Dessa forma, é
// imprescindível validar posteriormente a string resultante quando
// chegar a hora de convertê-la para número
char c = str.charAt(0);
switch (c) {
// Caracteres numéricos
case '0': case '1': case '2':
case '3': case '4': case '5':
case '6': case '7': case '8':
case '9':
// ... Nada a fazer ...
break;
case '-': { // Sinal de menos (-)
int curSize = this.getLength();
if (curSize > 0) {
// Se a string atual no documento já contiver um sinal de
// menos, sai da rotina
String txt = this.getText(0, curSize);
if (txt.indexOf(String.valueOf(c)) != -1) {
return;
}
}
}
break;
default: // Demais caracteres
// Sai da rotina
return;
}
}
super.replace(offset, length, str, attr);
}
}