001: /*
002: ItsNat Java Web Application Framework
003: Copyright (C) 2007 Innowhere Software Services S.L., Spanish Company
004: Author: Jose Maria Arranz Santamaria
005:
006: This program is free software: you can redistribute it and/or modify
007: it under the terms of the GNU Affero General Public License as published by
008: the Free Software Foundation, either version 3 of the License, or
009: (at your option) any later version. See the GNU Affero General Public
010: License for more details. See the copy of the GNU Affero General Public License
011: included in this program. If not, see <http://www.gnu.org/licenses/>.
012: */
013:
014: package org.itsnat.impl.comp.html;
015:
016: import java.beans.PropertyVetoException;
017: import org.itsnat.comp.ItsNatFormattedTextField.ItsNatFormatter;
018: import org.itsnat.comp.html.ItsNatHTMLInputTextFormatted;
019: import org.itsnat.core.ItsNatException;
020: import org.itsnat.impl.comp.ItsNatFormatterFactoryDefaultImpl;
021: import org.itsnat.impl.comp.ItsNatFormatterFormatBasedImpl;
022: import java.text.Format;
023: import java.text.ParseException;
024: import java.util.Date;
025: import javax.swing.event.DocumentEvent;
026: import org.itsnat.comp.ItsNatFormattedTextField.ItsNatFormatterFactory;
027: import org.itsnat.core.NameValue;
028: import org.itsnat.impl.comp.ItsNatFormatterDefaultImpl;
029: import org.w3c.dom.events.Event;
030: import org.w3c.dom.html.HTMLInputElement;
031:
032: /**
033: *
034: * @author jmarranz
035: */
036: public class ItsNatHTMLInputTextFormattedImpl extends
037: ItsNatHTMLInputTextImpl implements ItsNatHTMLInputTextFormatted {
038: protected Object value;
039: protected boolean edited;
040: protected boolean editValid;
041: protected int focusLostBehavior = COMMIT_OR_REVERT;
042: protected ItsNatFormatterFactory factory;
043: protected ItsNatFormatter formatter;
044:
045: /**
046: * Creates a new instance of ItsNatHTMLInputTextFormattedImpl
047: */
048: public ItsNatHTMLInputTextFormattedImpl(HTMLInputElement element,
049: NameValue[] artifacts,
050: ItsNatHTMLComponentManagerImpl componentMgr) {
051: super (element, artifacts, componentMgr);
052:
053: setItsNatFormatterFactory(createDefaultItsNatFormatterFactory());
054:
055: init();
056: }
057:
058: public void enableEventListeners() {
059: super .enableEventListeners();
060:
061: enableEventListener("focus");
062: enableEventListener("blur");
063: }
064:
065: public int getFocusLostBehavior() {
066: return focusLostBehavior;
067: }
068:
069: public void setFocusLostBehavior(int behavior) {
070: if ((behavior != COMMIT) && (behavior != COMMIT_OR_REVERT)
071: && (behavior != PERSIST) && (behavior != REVERT))
072: throw new ItsNatException(
073: "Value is not valid must be one of: ItsNatFormattedTextField.COMMIT, ItsNatFormattedTextField.COMMIT_OR_REVERT, ItsNatFormattedTextField.PERSIST or ItsNatFormattedTextField.REVERT");
074:
075: this .focusLostBehavior = behavior;
076: }
077:
078: public Object getValue() {
079: return value;
080: }
081:
082: public void setValue(Object value) throws PropertyVetoException {
083: setValue(value, true, true);
084: }
085:
086: public void updateDisplay() {
087: Object value = getValue();
088: try {
089: setValue(value, true, false);
090: } catch (PropertyVetoException ex) {
091: // Es imposible que ocurra porque se le dice fire=false
092: throw new ItsNatException("INTERNAL ERROR");
093: }
094: }
095:
096: public void setValue(Object value, boolean toDisplay, boolean fire)
097: throws PropertyVetoException {
098: Object oldValue = this .value;
099:
100: if (fire)
101: fireVetoableChange("value", oldValue, value);
102:
103: this .value = value;
104:
105: if (toDisplay) {
106: String str;
107: try {
108: str = valueToString(value);
109:
110: setEditValid(true);
111: } catch (ParseException ex) {
112: // El valor pasado no tiene el formato esperado por el formatter
113: this .value = oldValue;
114:
115: setEditValid(false);
116:
117: throw new ItsNatException(ex);
118: }
119: setText(str);
120: }
121:
122: if (fire)
123: firePropertyChange("value", oldValue, value); // Si son iguales no se lanza
124:
125: setEdited(false);
126: }
127:
128: public void commitEdit() throws ParseException,
129: PropertyVetoException {
130: String str = getText();
131: Object newValue = stringToValue(str);
132: setValue(newValue, false, true);
133: }
134:
135: public boolean isEdited() {
136: return edited;
137: }
138:
139: private void setEdited(boolean edited) {
140: this .edited = edited;
141: }
142:
143: public void setEditValid(boolean isValid) {
144: // No hacer público
145: this .editValid = isValid;
146: }
147:
148: public boolean isEditValid() {
149: return editValid;
150: }
151:
152: public String valueToString(Object value) throws ParseException {
153: ItsNatFormatter formatter = getItsNatFormatter();
154: return formatter.valueToString(value, this );
155: }
156:
157: public Object stringToValue(String str) throws ParseException {
158: ItsNatFormatter formatter = getItsNatFormatter();
159: return formatter.stringToValue(str, this );
160: }
161:
162: public void setHasFocus(boolean hasFocus) {
163: super .setHasFocus(hasFocus);
164:
165: updateDisplay(); // Es útil si el formatter formatea de forma diferente si está siendo editado a cuando está sin el foco, es el caso de usar el formatter factory default con varios formatters
166: }
167:
168: public void postHandleEventOnChange(Event evt) {
169: super .postHandleEventOnChange(evt);
170:
171: int fb = getFocusLostBehavior();
172: if ((fb == COMMIT) || (fb == COMMIT_OR_REVERT)) {
173: try {
174: commitEdit();
175:
176: // Si se llega aquí es que fue válido el valor del control,
177: // es posible de todas formas que su presentación visual no
178: // esté normalizada de acuerdo al formatter, por tanto reformateamos el valor y lo enviamos
179: // al control, no enviamos evento porque no hay cambio en value (y aunque pusiéramos true no se envía si no hay cambio de valor real)
180: // Un ejemplo de esto es en el caso de un Date, el formatter
181: // admite el 32 de enero pero realmente almacena el 1 de febrero
182: // así al actualizar el display lo veremos normalizado como 1 de febrero
183: updateDisplay(); // Ya se encarga de hacer también setEditValid(true);
184: } catch (ParseException ex) {
185: // El valor editado no tiene el formato esperado por el formatter
186: // restauramos al valor antiguo el control en el caso COMMIT_OR_REVERT
187: // No lanzamos el evento propiedad "value" porque no ha cambiado (y aunque pusiéramos true no se envía si no hay cambio de valor real)
188: setEditValid(false);
189: if (fb == COMMIT_OR_REVERT)
190: updateDisplay(); // Restauramos el valor bueno del componente, pone de nuevo setEditValid(true);
191: // El el caso de COMMIT el valor erróneo se queda en el control
192: } catch (PropertyVetoException ex) {
193: // Idem caso anterior, aunque en este caso ha sido el veto la causa del rechazo, en definitiva el valor no es válido
194: setEditValid(false);
195: if (fb == COMMIT_OR_REVERT)
196: updateDisplay();
197: }
198: } else if (fb == REVERT) {
199: // Restauramos el valor bueno del componente sea cual sea el del control
200: updateDisplay(); // Ya se encarga de hacer también setEditValid(true);
201: } else {
202: // El caso PERSIST es no hacer nada, sea válido o no el valor en el control
203: // En ese caso isEditValid() no tiene mucho sentido llamarse pues no sabemos que devolver
204: }
205: }
206:
207: public void postHandleEventOnKeyUp(Event evt) {
208: super .postHandleEventOnKeyUp(evt);
209:
210: // Estudiar si enviar el valor al formatter específico para
211: // cada tecla pulsada, con el fin de corregir la edición si es posible
212: // y ayudar al usuario. Una idea: si el usuario ha introducido
213: // una letra no válida detectarla y quitarla
214: // Otra forma diferente sería usar un DocumentFilter
215: // Por ahora no hacemos nada
216: }
217:
218: public void insertUpdate(DocumentEvent e) {
219: super .insertUpdate(e);
220:
221: setEdited(true);
222: }
223:
224: public void removeUpdate(DocumentEvent e) {
225: super .removeUpdate(e);
226:
227: setEdited(true);
228: }
229:
230: public void changedUpdate(DocumentEvent e) {
231: super .changedUpdate(e);
232:
233: // No hay atributos
234: }
235:
236: public ItsNatFormatterFactory createDefaultItsNatFormatterFactory() {
237: return new ItsNatFormatterFactoryDefaultImpl();
238: }
239:
240: public ItsNatFormatterFactory getItsNatFormatterFactory() {
241: return factory; // puede ser null
242: }
243:
244: public void setItsNatFormatterFactory(ItsNatFormatterFactory factory) {
245: this .factory = factory;
246: }
247:
248: public ItsNatFormatter getItsNatFormatter() {
249: ItsNatFormatter formatter = this .formatter;
250: if (formatter == null) {
251: ItsNatFormatterFactory factory = getItsNatFormatterFactory();
252: if (factory != null)
253: formatter = factory.getItsNatFormatter(this );
254: if (formatter == null)
255: formatter = getDefaultFormatterOfValue(getValue());
256: }
257:
258: if (formatter == null)
259: throw new ItsNatException("No formatter is available");
260: return formatter;
261: }
262:
263: public void setItsNatFormatter(ItsNatFormatter formatter) {
264: this .formatter = formatter;
265: }
266:
267: public void setFormat(Format format) {
268: // No hacemos un getFormat() porque el verdadero formateador es ItsNatFormatter
269: // el cual en teoría puede no estar basado en java.text.Format
270: setItsNatFormatter(createItsNatFormatter(format));
271: }
272:
273: public ItsNatFormatter createItsNatFormatter(Format format) {
274: return new ItsNatFormatterFormatBasedImpl(format);
275: }
276:
277: public ItsNatFormatter getDefaultFormatterOfValue(Object value) {
278: Format format = null;
279: if (value != null) {
280: if (value instanceof Date) {
281: format = getItsNatComponentManager()
282: .getItsNatDocument().getDefaultDateFormat();
283: } else if (value instanceof Number) {
284: format = getItsNatComponentManager()
285: .getItsNatDocument().getDefaultNumberFormat();
286: }
287: }
288:
289: if (format != null)
290: return new ItsNatFormatterFormatBasedImpl(format);
291: else
292: return new ItsNatFormatterDefaultImpl();
293: }
294: }
|