001/**
002*   Please feel free to use any fragment of the code in this file that you need
003*   in your own work. As far as I am concerned, it's in the public domain. No
004*   permission is necessary or required. Credit is always appreciated if you
005*   use a large chunk or base a significant product on one of my examples,
006*   but that's not required either.
007*
008*   This code is distributed in the hope that it will be useful,
009*   but WITHOUT ANY WARRANTY; without even the implied warranty of
010*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
011*
012*      --- Joseph A. Huwaldt
013**/
014package jahuwaldt.swing;
015
016import javax.swing.text.PlainDocument;
017import javax.swing.text.AttributeSet;
018import javax.swing.text.BadLocationException; 
019
020import java.awt.Toolkit;
021import java.text.Format;
022import java.text.ParsePosition;
023import java.text.DecimalFormat;
024import java.text.DecimalFormatSymbols;
025
026
027/**
028*  A formatted document that uses a user supplied Format object to
029*  control the format of the text in the document.  For example,
030*  this document will ignore anything that isn't a number if a
031*  NumberFormat is supplied.
032*
033*  <p>  Modified by:  Joseph A. Huwaldt    </p>
034*
035*  @author    Joseph A. Huwaldt    Date:  February 24, 2000
036*  @version   September 16, 2012
037**/
038@SuppressWarnings("serial")
039public class FormattedDocument extends PlainDocument {
040
041    private Format format;
042        private ParsePosition pos = new ParsePosition(0);
043        private String decimalSymbol = null;
044        private String minusSignSymbol = null;
045
046        /**
047        *  Construct a formatted document that uses the supplied Format object.
048        **/
049    public FormattedDocument(Format f) {
050        format = f;
051                if (format instanceof DecimalFormat) {
052                        DecimalFormatSymbols symbols = ((DecimalFormat)format).getDecimalFormatSymbols();
053                        decimalSymbol = String.valueOf(symbols.getDecimalSeparator());
054                        minusSignSymbol = String.valueOf(symbols.getMinusSign());
055                }
056    }
057
058        /**
059        *  Method that returns the Format used by this document.
060        **/
061    public Format getFormat() {
062        return format;
063    }
064
065        /**
066        *  Inserts some content into the document using the documents Format to validate
067        *  what has be inserted. Inserting content causes a write lock
068        *  to be held while the actual changes are taking place, followed by notification
069        *  to the observers on the thread that grabbed the write lock.
070        *
071        *  @param  offs  the starting offset >= 0
072        *  @param  str   the string to insert; does nothing with null/empty strings
073        *  @param  a     the attributes for the inserted content
074        **/
075    @Override
076    public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
077
078                if (str == null || str.length() == 0)
079                        return;
080                
081        String currentText = getText(0, getLength());
082        String beforeOffset = currentText.substring(0, offs);
083        String afterOffset = currentText.substring(offs, currentText.length());
084        String proposedResult = beforeOffset + str + afterOffset;
085                
086                //  Work around a couple of "bugs" in DecimalFormat.parseObject().
087                //  Allow leading decimal places when typing in a number.
088                if (format instanceof DecimalFormat &&
089                                (proposedResult.equals(decimalSymbol) || proposedResult.equals(minusSignSymbol + decimalSymbol)))
090                        super.insertString(offs, str, a);
091                
092                
093                else {
094                        pos.setIndex(0);
095                        format.parseObject(proposedResult, pos);
096                        if (pos.getIndex() == proposedResult.length())
097                                super.insertString(offs, str, a);
098
099                        else {
100                                System.err.println("FormattedDocument.insertString() error.");
101                                Toolkit.getDefaultToolkit().beep();
102                        }
103                }
104    }
105
106        /**
107        *  Removes some content from the document. Removing content causes a write lock
108        *  to be held while the actual changes are taking place. Observers are notified
109        *  of the change on the thread that called this method.
110        *
111        *  @param  offs  the starting offset >= 0
112        *  @param  len   the number of characters to remove >= 0
113        **/
114    @Override
115    public void remove(int offs, int len) throws BadLocationException {
116        String currentText = getText(0, getLength());
117        String beforeOffset = currentText.substring(0, offs);
118        String afterOffset = currentText.substring(len + offs, currentText.length());
119        String proposedResult = beforeOffset + afterOffset;
120
121                if (proposedResult.length() != 0) {
122                        pos.setIndex(0);
123                        format.parseObject(proposedResult, pos);
124                        if (pos.getIndex() != proposedResult.length()) {
125                                System.out.println("FormattedDocument.remove() error.");
126                                Toolkit.getDefaultToolkit().beep();
127                                return;
128                        }
129                }
130
131                super.remove(offs, len);
132    }
133}