001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.tagging.ac;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import javax.swing.text.AttributeSet;
007import javax.swing.text.BadLocationException;
008import javax.swing.text.DocumentFilter;
009import javax.swing.text.StyleConstants;
010
011/**
012 * A {@link DocumentFilter} to limit the text length in the editor.
013 * @since 18221
014 */
015public class MaxLengthDocumentFilter extends DocumentFilter {
016    /** the document will not accept text longer than this. -1 to disable */
017    private int maxLength = -1;
018    private static final String DIFFERENT = tr("<different>");
019
020    /**
021     * Sets the maximum text length.
022     *
023     * @param length the maximum no. of charactes allowed in this document. -1 to disable
024     */
025    public void setMaxLength(int length) {
026        maxLength = length;
027    }
028
029    @Override
030    public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
031            throws BadLocationException {
032        if (mustInsertOrReplace(fb, 0, string, attr)) {
033            super.insertString(fb, offset, string, attr);
034        }
035    }
036
037    @Override
038    public void replace(FilterBypass fb, int offset, int length, String string, AttributeSet attr)
039            throws BadLocationException {
040        if (mustInsertOrReplace(fb, length, string, attr)) {
041            super.replace(fb, offset, length, string, attr);
042        }
043    }
044
045    private boolean mustInsertOrReplace(FilterBypass fb, int length, String string, AttributeSet attr) {
046        int newLen = fb.getDocument().getLength() - length + ((string == null) ? 0 : string.length());
047        return (maxLength == -1 || newLen <= maxLength || DIFFERENT.equals(string) ||
048                // allow longer text while composing characters or it will be hard to compose
049                // the last characters before the limit
050                ((attr != null) && attr.isDefined(StyleConstants.ComposedTextAttribute)));
051    }
052}