View Javadoc

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 }