1 /*
2 * Copyright (c) 2003-2008 by Cosylab d. d.
3 *
4 * This file is part of CosyBeans-Common.
5 *
6 * CosyBeans-Common is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CosyBeans-Common is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CosyBeans-Common. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package com.cosylab.gui.components.numberfield;
21
22 import javax.swing.text.AttributeSet;
23 import javax.swing.text.BadLocationException;
24 import javax.swing.text.PlainDocument;
25
26 /**
27 * Abstract base for implementation of number parsers, hiding the details of
28 * the <code>javax.swing.text.Document</code> interface. This class handles
29 * parsing of the string and caching last value. It ensures that only
30 * acceptable numbers are entered (within the numeric range of the desired
31 * class).
32 *
33 * @author <a href="mailto:ales.pucelj@cosylab.com">Ales Pucelj</a>
34 * @version $id$
35 */
36 public abstract class AbstractNumberDocument extends PlainDocument {
37
38 private Number value;
39 protected Number min=null;
40 protected Number max=null;
41 private boolean updated = false;
42
43 /**
44 * Constructor for AbstractNumberDocument.
45 */
46 public AbstractNumberDocument() {
47 super();
48 }
49
50 /**
51 * Inserts the string at str parameter into existing string, but only if
52 * the result is valid number.
53 *
54 * @param offs int
55 * @param str String
56 * @param attributes AttributeSet
57 * @throws BadLocationException
58 * @see Document#insertString(int, String, AttributeSet)
59 */
60 public void insertString(int offs, String str, AttributeSet attributes)
61 throws BadLocationException {
62
63 updated = true;
64
65 String currentText = getText(0, getLength());
66 String beforeOffset = currentText.substring(0, offs);
67 String afterOffset = currentText.substring(offs, currentText.length());
68
69 String proposedResult = beforeOffset + str + afterOffset;
70
71 Number newValue = parseNumber(proposedResult);
72
73 if (doChecks(proposedResult,newValue)) {
74 value = newValue;
75 super.insertString(offs, str, attributes);
76 }
77
78 }
79
80 private boolean doChecks(String proposedResult, Number newValue) {
81 return (newValue != null && checkBounds(newValue)) || proposedResult.equals("") || proposedResult.equals("-");
82 }
83
84 /*
85 * Returnd true if OK.
86 */
87 protected boolean checkBounds(Number newVal) {
88 if (newVal==null) return true;
89 if (min==null || min.doubleValue()<=newVal.doubleValue()) {
90 if (max==null || max.doubleValue()>=newVal.doubleValue()) {
91 return true;
92 }
93 }
94 return false;
95 }
96
97 /**
98 * Removes the string specified by offs and len, but only if the resulting
99 * string is a valid number.
100 *
101 * @param offs int
102 * @param len int
103 * @throws BadLocationException
104 * @see Document#remove(int, int)
105 */
106 public void remove(int offs, int len) throws BadLocationException {
107
108 updated = true;
109
110 String currentText = getText(0, getLength());
111 if (currentText == null) currentText = "";
112 String beforeOffset = currentText.substring(0, offs);
113 int s = (offs + len) < 0 ? 0 : offs + len;
114 String afterOffset = "";
115 if (s < currentText.length()) {
116 afterOffset =
117 currentText.substring(s, currentText.length());
118 }
119
120 String proposedResult = beforeOffset + afterOffset;
121
122 Number newValue = parseNumber(proposedResult);
123
124 if (doChecks(proposedResult,newValue)) {
125 value = newValue;
126 super.remove(offs, len);
127 }
128 }
129
130 /**
131 * Parses the given string and returns the number represented. If the string
132 * is not a valid representation of the number, return value will be null.
133 * This method throws no exceptions, since all parse errors result in
134 * invalid number and must be handled internally.
135 *
136 * @param s String
137 * @return Number value of the number represented by string or null if
138 * no or invalid number
139 */
140 protected abstract Number parseNumber(String s);
141
142 /**
143 * Returns last successfully entered number. This value will not change
144 * if the string passed to the document is invalid.
145 *
146 * @return Number number parsed by this Document
147 */
148 public Number getValue() {
149 return value;
150 }
151
152 /**
153 * Returns the maximum value.
154 *
155 * @return maximum value
156 */
157 public Number getMaxValue() {
158 return max;
159 }
160
161 /**
162 * Sets the maximum value.
163 *
164 * @param max new maximum
165 */
166 public void setMaxValue(Number max) {
167 this.max = max;
168 }
169
170 /**
171 * Returns the minimum value.
172 *
173 * @return minimum value
174 */
175 public Number getMinValue() {
176 return min;
177 }
178
179 /**
180 * Sets the minimum value.
181 *
182 * @param min minimum value
183 */
184 public void setMinValue(Number min) {
185 this.min = min;
186 }
187
188 /**
189 * Returns the updated.
190 * @return
191 */
192 public boolean isUpdated() {
193 return updated;
194 }
195
196 /**
197 * Sets the updated property.
198 *
199 * @param updated
200 */
201 public void setUpdated(boolean updated) {
202 this.updated = updated;
203 }
204
205 }