001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package javax.swing.text;
018:
019: import java.io.Serializable;
020: import java.lang.reflect.Constructor;
021: import java.lang.reflect.InvocationTargetException;
022: import java.security.AccessController;
023: import java.security.PrivilegedAction;
024: import java.text.ParseException;
025: import javax.swing.JFormattedTextField;
026: import javax.swing.SwingUtilities;
027: import javax.swing.text.DocumentFilter.FilterBypass;
028:
029: import org.apache.harmony.x.swing.internal.nls.Messages;
030:
031: /**
032: * <p>
033: * <i>DefaultFormatter</i>
034: * </p>
035: * <h3>Implementation Notes:</h3>
036: * <ul>
037: * <li>The <code>serialVersionUID</code> fields are explicitly declared as a performance
038: * optimization, not as a guarantee of serialization compatibility.</li>
039: * </ul>
040: */
041: public class DefaultFormatter extends
042: JFormattedTextField.AbstractFormatter implements Cloneable,
043: Serializable {
044:
045: private static final long serialVersionUID = 4759164676455607130L;
046:
047: private boolean commitsOnValidEdit;
048: private boolean allowsInvalid = true;
049: private boolean overwriteMode = true;
050: private Class valueClass;
051: private DocumentFilter documentFilter;
052:
053: private class DocumentFilterImpl extends DocumentFilter {
054:
055: @Override
056: public void insertString(final FilterBypass filterBypass,
057: final int offset, final String string,
058: final AttributeSet attrs) throws BadLocationException {
059: if (overwriteMode) {
060: int length = getMaxLengthToRemove(filterBypass, offset,
061: string.length());
062: replaceImpl(filterBypass, offset, length, string, attrs);
063: } else {
064: insertStringImpl(filterBypass, offset, string, attrs);
065: }
066: }
067:
068: @Override
069: public void remove(final FilterBypass filterBypass,
070: final int offset, final int length)
071: throws BadLocationException {
072: removeImpl(filterBypass, offset, length);
073: }
074:
075: @Override
076: public void replace(final FilterBypass filterBypass,
077: final int offset, final int length, final String text,
078: final AttributeSet attrs) throws BadLocationException {
079: if (overwriteMode) {
080: int strLength = getMaxLengthToRemove(filterBypass,
081: offset, Math.max(length, text != null ? text
082: .length() : 0));
083: replaceImpl(filterBypass, offset, strLength, text,
084: attrs);
085: } else {
086: replaceImpl(filterBypass, offset, length, text, attrs);
087: }
088: }
089: }
090:
091: void insertStringImpl(final FilterBypass filterBypass,
092: final int offset, final String string,
093: final AttributeSet attrs) throws BadLocationException {
094: if (needEdit(0, offset, 0, string)) {
095: filterBypass.insertString(offset, string, attrs);
096: }
097: }
098:
099: void removeImpl(final FilterBypass filterBypass, final int offset,
100: final int length) throws BadLocationException {
101: if (needEdit(1, offset, length, null)) {
102: filterBypass.remove(offset, length);
103: }
104: }
105:
106: void replaceImpl(final FilterBypass filterBypass, final int offset,
107: final int length, final String text,
108: final AttributeSet attrs) throws BadLocationException {
109: if (needEdit(2, offset, length, text)) {
110: filterBypass.replace(offset, length, text, attrs);
111: }
112: }
113:
114: final int getMaxLengthToRemove(final FilterBypass filterBypass,
115: final int offset, final int length) {
116: return Math.min(length, filterBypass.getDocument().getLength()
117: - offset);
118:
119: }
120:
121: private String removeString(final String string, final int offset,
122: final int length) {
123: int stringLength = string.length();
124: int start = Math.min(stringLength, offset);
125: int minLength = Math.min(stringLength - start, length);
126: return string.substring(0, start)
127: + string.substring(start + minLength, stringLength);
128: }
129:
130: private String insertString(final String string, final int offset,
131: final String insertedString) {
132: int stringLength = string.length();
133: return offset <= stringLength ? string.substring(0, offset)
134: + insertedString
135: + string.substring(offset, stringLength) : string;
136: }
137:
138: private String replaceString(final String string, final int offset,
139: final int length, final String replacement) {
140: int stringLength = string.length();
141: int start = Math.min(stringLength, offset);
142: int minLength = Math.min(stringLength - start, offset);
143: return string.substring(0, start) + replacement
144: + string.substring(start + minLength, stringLength);
145: }
146:
147: boolean needEdit(final int operationId, final int offset,
148: final int length, final String text) {
149: String supposedText = getFormattedTextField().getText();
150: if (allowsInvalid) {
151: return true;
152: }
153: switch (operationId) {
154: case 0:
155: supposedText = insertString(supposedText, offset, text);
156: break;
157: case 1:
158: supposedText = removeString(supposedText, offset, length);
159: break;
160: case 2:
161: supposedText = replaceString(supposedText, offset, length,
162: text);
163: break;
164: default:
165: break;
166: }
167: Object value = null;
168: boolean result = false;
169: try {
170: value = stringToValue(supposedText);
171: result = value != null;
172: valueToString(value);
173: result = true;
174: } catch (Exception e) {
175: }
176: return allowsInvalid || result;
177: }
178:
179: public DefaultFormatter() {
180:
181: }
182:
183: @Override
184: public Object clone() throws CloneNotSupportedException {
185: return super .clone();
186: }
187:
188: public boolean getAllowsInvalid() {
189: return allowsInvalid;
190: }
191:
192: public boolean getCommitsOnValidEdit() {
193: return commitsOnValidEdit;
194: }
195:
196: @Override
197: protected DocumentFilter getDocumentFilter() {
198: if (documentFilter == null) {
199: documentFilter = new DocumentFilterImpl();
200: }
201: return documentFilter;
202: }
203:
204: public boolean getOverwriteMode() {
205: return overwriteMode;
206: }
207:
208: public Class<?> getValueClass() {
209: return valueClass;
210: }
211:
212: public void setAllowsInvalid(final boolean allowsInvalid) {
213: this .allowsInvalid = allowsInvalid;
214: }
215:
216: public void setCommitsOnValidEdit(final boolean commitsOnValidEdit) {
217: this .commitsOnValidEdit = commitsOnValidEdit;
218: }
219:
220: @Override
221: protected void setEditValid(final boolean isEditValid) {
222: super .setEditValid(isEditValid);
223: SwingUtilities.invokeLater(new Runnable() {
224: public void run() {
225: checkCommitCondition();
226: formatValue();
227: }
228: });
229: }
230:
231: public void setOverwriteMode(final boolean overwriteMode) {
232: this .overwriteMode = overwriteMode;
233: }
234:
235: public void setValueClass(final Class<?> valueClass) {
236: this .valueClass = valueClass;
237: }
238:
239: private Object stringToValue(final String string,
240: final Class valueClass) {
241: return AccessController
242: .doPrivileged(new PrivilegedAction<Object>() {
243: public Object run() {
244: Constructor constructor = null;
245: try {
246: constructor = valueClass
247: .getConstructor(new Class[] { String.class });
248: } catch (NoSuchMethodException e) {
249: return string;
250: }
251: Object result = null;
252: try {
253: constructor.setAccessible(true);
254: result = constructor
255: .newInstance(new Object[] { string });
256: } catch (IllegalAccessException e) {
257: } catch (InvocationTargetException e) {
258: } catch (InstantiationException e) {
259: }
260: return result;
261: }
262: });
263: }
264:
265: @Override
266: public Object stringToValue(final String string)
267: throws ParseException {
268: final Class valueClass = (this .valueClass != null) ? this .valueClass
269: : getTextFieldValueClass();
270: if (valueClass == null || string == null) {
271: return string;
272: }
273: Object result = stringToValue(string, valueClass);
274: if (result == null) {
275: throw new ParseException(Messages.getString("swing.86"), 0); //$NON-NLS-1$
276: }
277: return result;
278: }
279:
280: @Override
281: public String valueToString(final Object value)
282: throws ParseException {
283: return value != null ? value.toString() : "";
284: }
285:
286: private Class getTextFieldValueClass() {
287: JFormattedTextField textField = getFormattedTextField();
288: if (textField == null) {
289: return null;
290: }
291: Object value = getFormattedTextField().getValue();
292: return value != null ? value.getClass() : null;
293: }
294:
295: private void checkCommitCondition() {
296: JFormattedTextField textField = getFormattedTextField();
297: if (textField.isEditValid() && commitsOnValidEdit) {
298: try {
299: textField.setValue(stringToValue(textField.getText()));
300: } catch (ParseException e) {
301: }
302: }
303: }
304:
305: private void formatValue() {
306: if (allowsInvalid) {
307: return;
308: }
309: JFormattedTextField textField = getFormattedTextField();
310: String text = textField.getText();
311: String formattedText = getFormattedText(text);
312: if (!formattedText.equals(text)) {
313: int caret = textField.getCaretPosition();
314: textField.setText(formattedText);
315: textField.setCaretPosition(Math.min(formattedText.length(),
316: caret));
317: }
318: }
319:
320: String getFormattedText(final String text) {
321: Object value = null;
322: try {
323: value = stringToValue(text);
324: } catch (ParseException e) {
325: }
326: return (value != null) ? text : "";
327: }
328: }
|