001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032: package com.vividsolutions.jump.workbench.ui;
033:
034: import com.vividsolutions.jts.util.Assert;
035:
036: import java.awt.event.FocusAdapter;
037: import java.awt.event.FocusEvent;
038:
039: import javax.swing.JTextField;
040: import javax.swing.text.AttributeSet;
041: import javax.swing.text.BadLocationException;
042: import javax.swing.text.PlainDocument;
043:
044: /**
045: * Prevents the user from entering invalid data.
046: */
047: public class ValidatingTextField extends JTextField {
048: public static final Validator LONG_VALIDATOR = new ValidatingTextField.Validator() {
049: public boolean isValid(String text) {
050: try {
051: Long.parseLong(text.trim() + "0");
052:
053: return true;
054: } catch (NumberFormatException e) {
055: return false;
056: }
057: }
058: };
059:
060: /**
061: * Prevents the user from entering invalid integer.
062: */
063: public static final Validator INTEGER_VALIDATOR = new ValidatingTextField.Validator() {
064: public boolean isValid(String text) {
065: try {
066: //Add "0" so user can type "-" or make it blank [Jon Aquino]
067: Integer.parseInt(text.trim() + "0");
068:
069: return true;
070: } catch (NumberFormatException e) {
071: return false;
072: }
073: }
074: };
075:
076: /**
077: * Prevents the user from entering invalid double.
078: */
079: public static final Validator DOUBLE_VALIDATOR = new ValidatingTextField.Validator() {
080: public boolean isValid(String text) {
081: try {
082: //Add "0" so user can type "-" or make it blank [Jon Aquino]
083: Double.parseDouble(text.trim() + "0");
084:
085: return true;
086: } catch (NumberFormatException e) {
087: return false;
088: }
089: }
090: };
091:
092: /**
093: * Cleaner that does nothing.
094: */
095: public static Cleaner DUMMY_CLEANER = new Cleaner() {
096: public String clean(String text) {
097: return text;
098: }
099: };
100:
101: /**
102: * The validators allow the user to simply enter "+", "-", or ".". If the
103: * user doesn't go any farther, this cleaner will set the text to 0, which
104: * is reasonable.
105: */
106: public static Cleaner NUMBER_CLEANER = new Cleaner() {
107: public String clean(String text) {
108: try {
109: Double.parseDouble(text.trim());
110:
111: return text;
112: } catch (NumberFormatException e) {
113: return "0";
114: }
115: }
116: };
117:
118: /**
119: * Validator that does nothing.
120: */
121: public static Validator DUMMY_VALIDATOR = new Validator() {
122: public boolean isValid(String text) {
123: return true;
124: }
125: };
126:
127: private Cleaner cleaner;
128:
129: /**
130: * Validator that uses dummy cleaner.
131: */
132: public ValidatingTextField(String text, int columns,
133: final Validator validator) {
134: this (text, columns, LEFT, validator, DUMMY_CLEANER);
135: }
136:
137: /**
138: * Validator for text fields.
139: */
140: public ValidatingTextField(String text, int columns,
141: int horizontalAlignment, final Validator validator,
142: final Cleaner cleaner) {
143: super (text, columns);
144: this .cleaner = cleaner;
145: setHorizontalAlignment(horizontalAlignment);
146: installValidationBehavior(this , validator, cleaner);
147:
148: // Clean the text, mainly so that parties wishing to install a
149: // BlankCleaner need only pass "" for the text. [Jon Aquino]
150: setText(cleaner.clean(getText()));
151:
152: //Bonus: workaround for how GridBagLayout shrinks components to
153: //minimum sizes if it can't accomodate their preferred sizes.
154: //[Jon Aquino]
155: setMinimumSize(getPreferredSize());
156: }
157:
158: //Hopefully this will let us add validation behaviour to combo boxes.
159: //[Jon Aquino]
160: public static void installValidationBehavior(
161: final JTextField textField, final Validator validator,
162: final Cleaner cleaner) {
163: final boolean[] validating = new boolean[] { true };
164: textField.setDocument(new PlainDocument() {
165: public void insertString(int offs, String str,
166: AttributeSet a) throws BadLocationException {
167: if (!validating[0]) {
168: super .insertString(offs, str, a);
169: return;
170: }
171: String currentText = this .getText(0, getLength());
172: String beforeOffset = currentText.substring(0, offs);
173: String afterOffset = currentText.substring(offs,
174: currentText.length());
175: String proposedResult = beforeOffset + str
176: + afterOffset;
177: if (validator.isValid(cleaner.clean(proposedResult))) {
178: super .insertString(offs, str, a);
179: }
180: }
181:
182: public void remove(int offs, int len)
183: throws BadLocationException {
184: if (!validating[0]) {
185: super .remove(offs, len);
186: return;
187: }
188: String currentText = this .getText(0, getLength());
189: String beforeOffset = currentText.substring(0, offs);
190: String afterOffset = currentText.substring(len + offs,
191: currentText.length());
192: String proposedResult = beforeOffset + afterOffset;
193: if (validator.isValid(cleaner.clean(proposedResult))) {
194: super .remove(offs, len);
195: }
196: }
197: });
198: textField.addFocusListener(new FocusAdapter() {
199: public void focusLost(FocusEvent e) {
200: // Added validating flag to fix bug: on losing focus, #setText
201: // called #remove, which cleared the text, which then failed
202: // integer validation (being an empty string), which cancelled
203: // the remove, causing duplicate strings to appear.
204: // [Jon Aquino 2005-03-07]
205: validating[0] = false;
206: try {
207: textField.setText(cleaner
208: .clean(textField.getText()));
209: } finally {
210: validating[0] = true;
211: }
212: }
213: });
214: }
215:
216: public String getText() {
217: //Focus may not be lost yet (e.g. when syncing with scrollbar) [Jon
218: // Aquino]
219: return cleaner.clean(super .getText());
220: }
221:
222: public double getDouble() {
223: return Double.parseDouble(getText().trim());
224: }
225:
226: public int getInteger() {
227: return Integer.parseInt(getText().trim());
228: }
229:
230: public static interface Validator {
231: public boolean isValid(String text);
232: }
233:
234: public static interface Cleaner {
235: public String clean(String text);
236: }
237:
238: /**
239: * Implements validator with a greater than threshold.
240: */
241:
242: public static class GreaterThanValidator implements Validator {
243: private double threshold;
244:
245: public GreaterThanValidator(double threshold) {
246: this .threshold = threshold;
247: }
248:
249: public boolean isValid(String text) {
250: try {
251: return Double.parseDouble(text.trim()) > threshold;
252: } catch (NumberFormatException e) {
253: //Handle -, ., E [Jon Aquino 2005-03-17]
254: return true;
255: }
256: }
257: }
258:
259: /**
260: * Implements validator with a less than threshold.
261: */
262:
263: public static class LessThanValidator implements Validator {
264: private double threshold;
265:
266: public LessThanValidator(double threshold) {
267: this .threshold = threshold;
268: }
269:
270: public boolean isValid(String text) {
271: try {
272: return Double.parseDouble(text.trim()) < threshold;
273: } catch (NumberFormatException e) {
274: return true;
275: }
276: }
277: }
278:
279: /**
280: * Implements validator with a greater than or equal to threshold.
281: */
282:
283: public static class GreaterThanOrEqualValidator implements
284: Validator {
285: private double threshold;
286:
287: public GreaterThanOrEqualValidator(double threshold) {
288: this .threshold = threshold;
289: }
290:
291: public boolean isValid(String text) {
292: try {
293: return Double.parseDouble(text.trim()) >= threshold;
294: } catch (NumberFormatException e) {
295: return true;
296: }
297: }
298: }
299:
300: /**
301: * Implements validator with a less than or equal to threshold.
302: */
303:
304: public static class LessThanOrEqualValidator implements Validator {
305: private double threshold;
306:
307: public LessThanOrEqualValidator(double threshold) {
308: this .threshold = threshold;
309: }
310:
311: public boolean isValid(String text) {
312: try {
313: return Double.parseDouble(text.trim()) <= threshold;
314: } catch (NumberFormatException e) {
315: return true;
316: }
317: }
318: }
319:
320: /**
321: * Leave untouched the really good stuff and the really bad stuff, but
322: * replace the sort-of good stuff, as it's probably just transient.
323: */
324: public static class NumberCleaner implements
325: ValidatingTextField.Cleaner {
326: private String replacement;
327:
328: public NumberCleaner(String replacement) {
329: this .replacement = replacement;
330: }
331:
332: public String clean(String text) {
333: try {
334: Double.parseDouble(text.trim());
335: return text.trim();
336: } catch (NumberFormatException e) {
337: try {
338: //Handle -, ., E [Jon Aquino 2004-08-04]
339: Double.parseDouble(text.trim() + "0");
340: return replacement;
341: } catch (NumberFormatException e2) {
342: return text.trim();
343: }
344: }
345: }
346:
347: protected String getReplacement() {
348: return replacement;
349: }
350: }
351:
352: public static class BlankCleaner implements
353: ValidatingTextField.Cleaner {
354: private String replacement;
355:
356: public BlankCleaner(String replacement) {
357: this .replacement = replacement;
358: }
359:
360: public String clean(String text) {
361: return (text.trim().length() == 0) ? getReplacement()
362: : text;
363: }
364:
365: protected String getReplacement() {
366: return replacement;
367: }
368: }
369:
370: public static class MinIntCleaner implements Cleaner {
371: private int minimum;
372:
373: public MinIntCleaner(int minimum) {
374: this .minimum = minimum;
375: }
376:
377: public String clean(String text) {
378: return "" + Math.max(minimum, Integer.parseInt(text));
379: }
380: }
381:
382: /**
383: * Extends CompositeValidator to validat that integers is within a set of
384: * boundary values.
385: */
386: public static class BoundedIntValidator extends CompositeValidator {
387: public BoundedIntValidator(int min, int max) {
388: super (new Validator[] { INTEGER_VALIDATOR,
389: new GreaterThanOrEqualValidator(min),
390: new LessThanOrEqualValidator(max) });
391: Assert.isTrue(min < max);
392: }
393: }
394:
395: public static class BoundedDoubleValidator extends
396: CompositeValidator {
397: public BoundedDoubleValidator(double min, boolean includeMin,
398: double max, boolean includeMax) {
399: super (
400: new Validator[] {
401: DOUBLE_VALIDATOR,
402: includeMin ? (Validator) new GreaterThanOrEqualValidator(
403: min)
404: : new GreaterThanValidator(min),
405: includeMax ? (Validator) new LessThanOrEqualValidator(
406: max)
407: : new LessThanValidator(max) });
408: Assert.isTrue(min < max);
409: }
410: }
411:
412: public static class MaxIntCleaner implements Cleaner {
413: private int maximum;
414:
415: public MaxIntCleaner(int maximum) {
416: this .maximum = maximum;
417: }
418:
419: public String clean(String text) {
420: return "" + Math.min(maximum, Integer.parseInt(text));
421: }
422: }
423:
424: /**
425: * Implements validator to check for more than one condition.
426: */
427:
428: public static class CompositeValidator implements Validator {
429: private Validator[] validators;
430:
431: public CompositeValidator(Validator[] validators) {
432: this .validators = validators;
433: }
434:
435: public boolean isValid(String text) {
436: for (int i = 0; i < validators.length; i++) {
437: if (!validators[i].isValid(text)) {
438: return false;
439: }
440: }
441:
442: return true;
443: }
444: }
445:
446: public static class CompositeCleaner implements Cleaner {
447: private Cleaner[] cleaners;
448:
449: public CompositeCleaner(Cleaner[] cleaners) {
450: this .cleaners = cleaners;
451: }
452:
453: public String clean(String text) {
454: String result = text;
455: for (int i = 0; i < cleaners.length; i++) {
456: result = cleaners[i].clean(result);
457: }
458:
459: return result;
460: }
461: }
462: }
|