001: /* InputElement.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Tue Jul 5 08:49:30 2005, Created by tomyeh
010: }}IS_NOTE
011:
012: Copyright (C) 2005 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: This program is distributed under GPL Version 2.0 in the hope that
016: it will be useful, but WITHOUT ANY WARRANTY.
017: }}IS_RIGHT
018: */
019: package org.zkoss.zul.impl;
020:
021: import java.util.HashMap;
022:
023: import org.zkoss.lang.Objects;
024: import org.zkoss.xml.HTMLs;
025: import org.zkoss.xml.XMLs;
026:
027: import org.zkoss.lang.Exceptions;
028: import org.zkoss.util.logging.Log;
029:
030: import org.zkoss.zk.ui.UiException;
031: import org.zkoss.zk.ui.WrongValueException;
032: import org.zkoss.zk.ui.util.Clients;
033: import org.zkoss.zk.ui.ext.client.InputableX;
034: import org.zkoss.zk.ui.ext.client.Errorable;
035: import org.zkoss.zk.ui.event.Events;
036: import org.zkoss.zk.ui.sys.ComponentsCtrl;
037: import org.zkoss.zk.au.out.AuSetAttribute;
038: import org.zkoss.zk.scripting.Namespace;
039: import org.zkoss.zk.scripting.Namespaces;
040:
041: import org.zkoss.zul.mesg.MZul;
042: import org.zkoss.zul.Constraint;
043: import org.zkoss.zul.ClientConstraint;
044: import org.zkoss.zul.CustomConstraint;
045: import org.zkoss.zul.SimpleConstraint;
046: import org.zkoss.zul.ext.Constrainted;
047:
048: /**
049: * A skeletal implementation of an input box.
050: * <p>Events: onChange, onChanging, onFocus, onBlur, onSelection, and onOK<br/>
051: *
052: * <p>Default {@link #getSclass}: text.
053: * @author tomyeh
054: * @since 3.0.1 supports onOK event.
055: */
056: abstract public class InputElement extends XulElement implements
057: Constrainted {
058: private static final Log log = Log.lookup(InputElement.class);
059:
060: /** The value. */
061: private Object _value;
062: /** Used by setTextByClient() to disable sending back the value */
063: private transient String _txtByClient;
064: /** The error message. Not null if users entered a wrong data (and
065: * not correct it yet).
066: */
067: private String _errmsg;
068: /** The name. */
069: private String _name;
070: private int _maxlength, _cols;
071: private int _tabindex = -1;
072: private Constraint _constr;
073: private boolean _disabled, _readonly;
074: /** Whether this input is validated (Feature 1461209). */
075: private boolean _valided;
076: /** Whether the validation is calused by {@link #isValid}. */
077: private transient boolean _checkOnly;
078:
079: public InputElement() {
080: setSclass("text");
081: }
082:
083: /** Returns whether it is disabled.
084: * <p>Default: false.
085: */
086: public boolean isDisabled() {
087: return _disabled;
088: }
089:
090: /** Sets whether it is disabled.
091: */
092: public void setDisabled(boolean disabled) {
093: if (_disabled != disabled) {
094: _disabled = disabled;
095: smartUpdate("disabled", _disabled);
096: }
097: }
098:
099: /** Returns whether it is readonly.
100: * <p>Default: false.
101: */
102: public boolean isReadonly() {
103: return _readonly;
104: }
105:
106: /** Sets whether it is readonly.
107: */
108: public void setReadonly(boolean readonly) {
109: if (_readonly != readonly) {
110: _readonly = readonly;
111: smartUpdate("readOnly", _readonly);
112: }
113: }
114:
115: /** Returns the name of this component.
116: * <p>Default: null.
117: * <p>Don't use this method if your application is purely based
118: * on ZK's event-driven model.
119: * <p>The name is used only to work with "legacy" Web application that
120: * handles user's request by servlets.
121: * It works only with HTTP/HTML-based browsers. It doesn't work
122: * with other kind of clients.
123: */
124: public String getName() {
125: return _name;
126: }
127:
128: /** Sets the name of this component.
129: * <p>Don't use this method if your application is purely based
130: * on ZK's event-driven model.
131: * <p>The name is used only to work with "legacy" Web application that
132: * handles user's request by servlets.
133: * It works only with HTTP/HTML-based browsers. It doesn't work
134: * with other kind of clients.
135: *
136: * @param name the name of this component.
137: */
138: public void setName(String name) {
139: if (name != null && name.length() == 0)
140: name = null;
141: if (!Objects.equals(_name, name)) {
142: _name = name;
143: smartUpdate("name", _name);
144: }
145: }
146:
147: /** Returns the error message that is caused when user entered
148: * invalid value, or null if no error at all.
149: *
150: * <p>The error message is set when user has entered a wrong value,
151: * or setValue is called with a wrong value.
152: * It is cleared once a correct value is assigned.
153: *
154: * <p>If the error message is set, we say this input is in the error mode.
155: * Any following invocation to {@link #getText} or getValue will throw
156: * any exception.
157: * Example, {@link org.zkoss.zul.Textbox#getValue} and
158: * {@link org.zkoss.zul.Intbox#getValue}.
159: */
160: public String getErrorMessage() {
161: return _errmsg;
162: }
163:
164: /** Clears the error message.
165: *
166: * <p>The error message is cleared automatically, so you rarely need
167: * to call this method.
168: * However, if a constraint depends on multiple input fields and
169: * the error can be corrected by changing one of these fields,
170: * then you may have to clear the error message manullay by invoking
171: * this method.
172: *
173: * <p>For example, assume you have two {@link org.zkoss.zul.Intbox}
174: * and want the value of the first one to be smaller than that of the
175: * second one. Then, you have to call this method for the second intbox
176: * once the validation of the first intbox succeeds, and vice versa.
177: * Otherwise, the error message for the seoncd intbox remains if
178: * the user fixed the error by lowering down the value of the first one
179: * Why? The second intbox got no idea to clear the error message
180: * (since its content doesn't change).
181: *
182: * @param revalidateRequired whether to re-validate the current value
183: * when {@link #getText} or others (such as {@link org.zkoss.zul.Intbox#getValue})
184: * is called.
185: * If false, the current value is assumed to be correct and
186: * the following invocation to {@link #getText} or others (such as {@link org.zkoss.zul.Intbox#getValue})
187: * won't check the value again.
188: * Note: when an input element is constrcuted, the initial value
189: * is assumed to be "not-validated-yet".
190: * @since 3.0.1
191: */
192: public void clearErrorMessage(boolean revalidateRequired) {
193: if (_errmsg != null) {
194: _errmsg = null;
195: Clients.closeErrorBox(this );
196: }
197: _valided = !revalidateRequired;
198: }
199:
200: /** Clears the error message.
201: * It is the same as clearErrorMessage(false). That is, the current
202: * value is assumed to be correct. {@link #getText} or others (such as {@link org.zkoss.zul.Intbox#getValue})
203: * won't re-validate it again.
204: *
205: * <p>The error message is cleared automatically, so you rarely need
206: * to call this method.
207: *
208: * @see #clearErrorMessage(boolean)
209: */
210: public void clearErrorMessage() {
211: clearErrorMessage(false);
212: }
213:
214: /** Returns the value in the String format.
215: * In most case, you shall use the setValue method instead, e.g.,
216: * {@link org.zkoss.zul.Textbox#getValue} and
217: * {@link org.zkoss.zul.Intbox#getValue}.
218: *
219: * <p>It invokes {@link #checkUserError} to ensure no user error.
220: *
221: * <p>It invokes {@link #coerceToString} to convert the stored value
222: * into a string.
223: *
224: * @exception WrongValueException if user entered a wrong value
225: */
226: public String getText() throws WrongValueException {
227: checkUserError();
228: return coerceToString(_value);
229: }
230:
231: /** Sets the value in the String format.
232: * In most case, you shall use the setValue method instead, e.g.,
233: * {@link org.zkoss.zul.Textbox#setValue} and
234: * {@link org.zkoss.zul.Intbox#setValue}.
235: *
236: * <p>It invokes {@link #coerceFromString} fisrt and then {@link #validate}.
237: * Derives might override them for type conversion and special
238: * validation.
239: *
240: * @param value the value; If null, it is considered as empty.
241: */
242: public void setText(String value) throws WrongValueException {
243: if (_maxlength > 0 && value != null
244: && value.length() > _maxlength)
245: throw showCustomError(new WrongValueException(this ,
246: MZul.STRING_TOO_LONG, new Integer(_maxlength)));
247:
248: final Object val = coerceFromString(value);
249: validate(val);
250:
251: final boolean errFound = _errmsg != null;
252: clearErrorMessage(); //no error at all
253:
254: if (!Objects.equals(_value, val)) {
255: _value = val;
256:
257: final String fmtval = coerceToString(_value);
258: if (_txtByClient == null
259: || !Objects.equals(_txtByClient, fmtval)) {
260: _txtByClient = null; //only once
261: smartUpdate("value", fmtval);
262: //Note: we have to disable the sending back of the value
263: //Otherwise, it cause Bug 1488579's problem 3.
264: //Reason: when user set a value to correct one and set
265: //to an illegal one, then click the button cause both events
266: }
267: } else if (_txtByClient != null) {
268: //value equals but formatted result might differ because
269: //our parse is more fault tolerant
270: final String fmtval = coerceToString(_value);
271: if (!Objects.equals(_txtByClient, fmtval)) {
272: _txtByClient = null; //only once
273: smartUpdate("value", fmtval);
274: //being sent back to the server.
275: }
276: } else if (errFound) {
277: smartUpdate("value", coerceToString(_value));
278: //Bug 1876292: make sure client see the updated value
279: }
280: }
281:
282: /** Coerces the value passed to {@link #setText}.
283: *
284: * <p>Deriving note:<br>
285: * If you want to store the value in other type, say BigDecimal,
286: * you have to override {@link #coerceToString} and {@link #coerceFromString}
287: * to convert between a string and your targeting type.
288: *
289: * <p>Moreover, when {@link org.zkoss.zul.Textbox} is called, it calls this method
290: * with value = null. Derives shall handle this case properly.
291: */
292: abstract protected Object coerceFromString(String value)
293: throws WrongValueException;
294:
295: /** Coerces the value passed to {@link #setText}.
296: *
297: * <p>Default: convert null to an empty string.
298: *
299: * <p>Deriving note:<br>
300: * If you want to store the value in other type, say BigDecimal,
301: * you have to override {@link #coerceToString} and {@link #coerceFromString}
302: * to convert between a string and your targeting type.
303: */
304: abstract protected String coerceToString(Object value);
305:
306: /** Validates the value returned by {@link #coerceFromString}.
307: * <p>Default: use {@link #getConstraint}'s {@link Constraint#validate},
308: * if not null.
309: * <p>You rarely need to override this method.
310: */
311: protected void validate(Object value) throws WrongValueException {
312: final Constraint constr = getConstraint();
313: if (constr != null) {
314: //Bug 1698190: contructor might be zscript
315: final HashMap backup = new HashMap();
316: final Namespace ns = Namespaces.beforeInterpret(backup,
317: this , true);
318: try {
319: constr.validate(this , value);
320: if (!_checkOnly && (constr instanceof CustomConstraint)) {
321: try {
322: ((CustomConstraint) constr).showCustomError(
323: this , null);
324: //not call thru showCustomError(Wrong...) for better performance
325: } catch (Throwable ex) {
326: log.realCauseBriefly(ex);
327: }
328: }
329: } catch (WrongValueException ex) {
330: if (!_checkOnly && (constr instanceof CustomConstraint))
331: ((CustomConstraint) constr).showCustomError(this ,
332: ex);
333: throw ex;
334: } finally {
335: Namespaces.afterInterpret(backup, ns, true);
336: }
337: }
338: }
339:
340: /** Shows the error message in the custom way by calling
341: * ({@link CustomConstraint#showCustomError}, if the contraint
342: * implements {@link CustomConstraint}.
343: *
344: * <p>Derived class shall call this method before throwing
345: * {@link WrongValueException}, such that the constraint,
346: * if any, has a chance to show the error message in a custom way.
347: *
348: * @param ex the exception, or null to clean up the error.
349: * @return the exception (ex)
350: */
351: protected WrongValueException showCustomError(WrongValueException ex) {
352: if (_constr instanceof CustomConstraint) {
353: final HashMap backup = new HashMap();
354: final Namespace ns = Namespaces.beforeInterpret(backup,
355: this , true);
356: try {
357: ((CustomConstraint) _constr).showCustomError(this , ex);
358: } catch (Throwable t) {
359: log.realCause(t); //and ignore it
360: } finally {
361: Namespaces.afterInterpret(backup, ns, true);
362: }
363: }
364: return ex;
365: }
366:
367: /** Returns the maxlength.
368: * <p>Default: 0 (non-postive means unlimited).
369: */
370: public int getMaxlength() {
371: return _maxlength;
372: }
373:
374: /** Sets the maxlength.
375: */
376: public void setMaxlength(int maxlength) {
377: if (_maxlength != maxlength) {
378: _maxlength = maxlength;
379: invalidate();
380: }
381: }
382:
383: /** Returns the cols.
384: * <p>Default: 0 (non-positive means the same as browser's default).
385: */
386: public int getCols() {
387: return _cols;
388: }
389:
390: /** Sets the cols.
391: */
392: public void setCols(int cols) throws WrongValueException {
393: if (cols <= 0)
394: throw new WrongValueException("Illegal cols: " + cols);
395:
396: if (_cols != cols) {
397: _cols = cols;
398: smartUpdate("cols", Integer.toString(_cols));
399: }
400: }
401:
402: /** Returns the tab order of this component.
403: * <p>Default: -1 (means the same as browser's default).
404: */
405: public int getTabindex() {
406: return _tabindex;
407: }
408:
409: /** Sets the tab order of this component.
410: */
411: public void setTabindex(int tabindex) throws WrongValueException {
412: if (_tabindex != tabindex) {
413: _tabindex = tabindex;
414: if (tabindex < 0)
415: smartUpdate("tabindex", null);
416: else
417: smartUpdate("tabindex", Integer.toString(_tabindex));
418: }
419: }
420:
421: /** Returns whether it is multiline.
422: * <p>Default: false.
423: */
424: public boolean isMultiline() {
425: return false;
426: }
427:
428: /** Returns the type.
429: * <p>Default: text.
430: */
431: public String getType() {
432: return "text";
433: }
434:
435: /** Selects the whole text in this input.
436: */
437: public void select() {
438: smartUpdate("z.sel", "all");
439: }
440:
441: //-- Constrainted --//
442: public void setConstraint(String constr) {
443: setConstraint(SimpleConstraint.getInstance(constr));
444: }
445:
446: public void setConstraint(Constraint constr) {
447: if (!Objects.equals(_constr, constr)) {
448: _constr = constr;
449: _valided = false;
450: invalidate();
451: }
452: }
453:
454: public final Constraint getConstraint() {
455: return _constr;
456: }
457:
458: //-- super --//
459: /** Returns whether to send back the request of the specified event
460: * immediately -- non-deferable.
461: * Returns true if you want the component (on the server)
462: * to process the event immediately.
463: *
464: * <p>Default: Besides super.isAsapRequired(evtnm), it also returns true
465: * if evtnm is Events.ON_CHANGE, {@link #getConstraint} is not null,
466: * and {@link ClientConstraint#getClientValidation} is null.
467: */
468: protected boolean isAsapRequired(String evtnm) {
469: return (Events.ON_CHANGE.equals(evtnm) && _constr != null && ((_constr instanceof CustomConstraint)
470: || !(_constr instanceof ClientConstraint) || !((ClientConstraint) _constr)
471: .isClientComplete()))
472: || super .isAsapRequired(evtnm);
473: }
474:
475: public String getInnerAttrs() {
476: final StringBuffer sb = new StringBuffer(64).append(super
477: .getInnerAttrs());
478:
479: if (isMultiline()) {
480: if (_cols > 0)
481: HTMLs.appendAttribute(sb, "cols", _cols);
482: if (_maxlength > 0)
483: HTMLs.appendAttribute(sb, "z.maxlen", _maxlength);
484: } else {
485: HTMLs.appendAttribute(sb, "value", coerceToString(_value));
486: if (_cols > 0)
487: HTMLs.appendAttribute(sb, "size", _cols);
488: if (_maxlength > 0)
489: HTMLs.appendAttribute(sb, "maxlength", _maxlength);
490: HTMLs.appendAttribute(sb, "type", "password"
491: .equals(getType()) ? "password" : "text");
492: }
493:
494: if (_tabindex >= 0)
495: HTMLs.appendAttribute(sb, "tabindex", _tabindex);
496:
497: HTMLs.appendAttribute(sb, "name", _name);
498: if (isDisabled())
499: HTMLs.appendAttribute(sb, "disabled", "disabled");
500: if (isReadonly())
501: HTMLs.appendAttribute(sb, "readonly", "readonly");
502: return sb.toString();
503: }
504:
505: public String getOuterAttrs() {
506: final StringBuffer sb = new StringBuffer(64).append(super
507: .getOuterAttrs());
508:
509: appendAsapAttr(sb, Events.ON_CHANGE);
510: appendAsapAttr(sb, Events.ON_CHANGING);
511: appendAsapAttr(sb, Events.ON_FOCUS);
512: appendAsapAttr(sb, Events.ON_BLUR);
513: appendAsapAttr(sb, Events.ON_SELECTION);
514: appendAsapAttr(sb, Events.ON_OK);
515:
516: if (_constr != null) {
517: String serverValid = null;
518: if (_constr instanceof CustomConstraint) {
519: serverValid = "custom";
520: //validate-at-server is required and no client validation
521: } else if (_constr instanceof ClientConstraint) {
522: final ClientConstraint cc = (ClientConstraint) _constr;
523: HTMLs.appendAttribute(sb, "z.valid", toJavaScript(cc
524: .getClientValidation()));
525: HTMLs.appendAttribute(sb, "z.ermg", cc
526: .getErrorMessage(this ));
527: if (!cc.isClientComplete())
528: serverValid = "both";
529: //validate-at-server is required after the client validation
530: } else {
531: serverValid = "both";
532: }
533: HTMLs.appendAttribute(sb, "z.srvald", serverValid);
534: }
535: return sb.toString();
536: }
537:
538: /** Converts the client validation to JavaScript.
539: */
540: private final String toJavaScript(String script) {
541: return script != null ? script.indexOf('(') >= 0 ? ComponentsCtrl
542: .parseClientScript(this , script)
543: : script
544: : null;
545: }
546:
547: /** Returns the value in the targeting type.
548: * It is used by the deriving class to implement the getValue method.
549: * For example, {@link org.zkoss.zul.Intbox#getValue} is the same
550: * as this method except with a different signature.
551: *
552: * <p>It invokes {@link #checkUserError} to ensure no user error.
553: * @exception WrongValueException if the user entered a wrong value
554: * @see #getText
555: */
556: protected Object getTargetValue() throws WrongValueException {
557: checkUserError();
558: return _value;
559: }
560:
561: /** Returns the raw value directly with checking whether any
562: * error message not yet fixed. In other words, it does NOT invoke
563: * {@link #checkUserError}.
564: *
565: * <p>Note: if the user entered an incorrect value (i.e., caused
566: * {@link WrongValueException}), the incorrect value doesn't
567: * be stored so this method returned the last correct value.
568: *
569: * @see #getRawText
570: * @see #getText
571: * @see #setRawValue
572: */
573: public Object getRawValue() {
574: return _value;
575: }
576:
577: /** Returns the text directly without checking whether any error
578: * message not yet fixed. In other words, it does NOT invoke
579: * {@link #checkUserError}.
580: *
581: * <p>Note: if the user entered an incorrect value (i.e., caused
582: * {@link WrongValueException}), the incorrect value doesn't
583: * be stored so this method returned the last correct value.
584: *
585: * @see #getRawValue
586: * @see #getText
587: */
588: public String getRawText() {
589: return coerceToString(_value);
590: }
591:
592: /** Sets the raw value directly. The caller must make sure the value
593: * is correct (or intend to be incorrect), because this method
594: * doesn't do any validation.
595: *
596: * <p>If you feel confusing with setValue, such as {@link org.zkoss.zul.Textbox#setValue},
597: * it is usually better to use setValue instead. This method
598: * is reserved for developer that really want to set an 'illegal'
599: * value (such as an empty string to a textbox with no-empty contraint).
600: *
601: * <p>Note: since 3.0.1, the value will be re-validate again if
602: * {@link #getText} or others (such as {@link org.zkoss.zul.Intbox#getValue})
603: * is called. In other words, it is assumed that the specified value
604: * is not validated yet -- the same state when this component is
605: * created. If you want to avoid the re-valiation, you have to invoke
606: * {@link #clearErrorMessage()}.
607: *
608: * <p>Like setValue, the result is returned back to the server
609: * by calling {@link #getText}.
610: *
611: * @see #getRawValue
612: */
613: public void setRawValue(Object value) {
614: if (_errmsg != null || !Objects.equals(_value, value)) {
615: clearErrorMessage(true);
616: _value = value;
617: smartUpdate("value", coerceToString(_value));
618: }
619: }
620:
621: /** Sets the value directly.
622: * Note: Unlike {@link #setRawValue} (nor setValue), this method
623: * assigns the value directly without clearing error message or
624: * synchronizing with the client.
625: *
626: * <p>It is usually used only the constructor.
627: * Though it is also OK to use {@link #setRawValue} in the constructor,
628: * this method has better performance.
629: * @since 3.0.3
630: */
631: protected void setValueDirectly(Object value) {
632: _value = value;
633: }
634:
635: /** Returns the current content of this input is correct.
636: * If the content is not correct, next call to the getvalue method will
637: * throws WrongValueException.
638: */
639: public boolean isValid() {
640: if (_errmsg != null)
641: return false;
642:
643: if (!_valided && _constr != null) {
644: _checkOnly = true;
645: try {
646: validate(_value);
647: } catch (Throwable ex) {
648: return false;
649: } finally {
650: _checkOnly = false;
651: }
652: }
653: return true;
654: }
655:
656: /**
657: * Sets the text of this InputElement to the specified text which is
658: * begining with the new start point and ending with the new end point.
659: *
660: * @param start the start position of the text (included)
661: * @param end the end position of the text (excluded)
662: * @param newtxt the new text to be set.
663: * @param isHighLight
664: * Sets whether it will represent highlihgt style or cursor
665: * style.If the start point same with the end point always
666: * represent cursor style.
667: */
668: public void setSelectedText(int start, int end, String newtxt,
669: boolean isHighLight) {
670: if (start <= end) {
671: final String txt = getText();
672: final int len = txt.length();
673: if (start < 0)
674: start = 0;
675: if (start > len)
676: start = len;
677: if (end < 0)
678: end = 0;
679: if (end > len)
680: end = len;
681:
682: if (newtxt == null)
683: newtxt = "";
684:
685: setText(txt.substring(0, start) + newtxt
686: + txt.substring(end));
687: setSelectionRange(start, isHighLight ? start
688: + newtxt.length() : start);
689: }
690: }
691:
692: /**
693: * Sets the selection end to the specified position and the selection start
694: * to the specified position. The new end point is constrained to be at or
695: * after the current selection start. If the new start point is different
696: * with the new end point, then will represent the result of highlight in
697: * this text.
698: *
699: * <p>Set both arguments to the same value to move the cursor to
700: * the corresponding position without selecting text.
701: *
702: * @param start the start position of the text (included)
703: * @param end the end position of the text (excluded)
704: */
705: public void setSelectionRange(int start, int end) {
706: if (start <= end)
707: response("setAttr", new AuSetAttribute(this , "z.sel", start
708: + "," + end));
709: }
710:
711: /** Checks whether user entered a wrong value (and not correct it yet).
712: * Since user might enter a wrong value and moves on to other components,
713: * this methid is called when {@link #getText} or {@link #getTargetValue} is
714: * called.
715: *
716: * <p>Derives rarely need to access this method if they use only
717: * {@link #getText} and {@link #getTargetValue}.
718: */
719: protected void checkUserError() throws WrongValueException {
720: if (_errmsg != null)
721: throw showCustomError(new WrongValueException(this , _errmsg));
722: //Note: we still throw exception to abort the exec flow
723: //It's client's job NOT to show the error box!
724: //(client checks z.srvald to decide whether to open error box)
725:
726: if (!_valided && _constr != null)
727: setText(coerceToString(_value));
728: }
729:
730: /** Returns the text for HTML AREA (Internal Use Only).
731: *
732: * <p>Used only for component generation. Not for applications.
733: */
734: public String getAreaText() {
735: return XMLs.encodeText(coerceToString(_value));
736: }
737:
738: //-- Component --//
739: /** Not childable. */
740: public boolean isChildable() {
741: return false;
742: }
743:
744: //-- ComponentCtrl --//
745: protected Object newExtraCtrl() {
746: return new ExtraCtrl();
747: }
748:
749: public WrongValueException onWrongValue(WrongValueException ex) {
750: _errmsg = Exceptions.getMessage(ex);
751: return showCustomError(ex);
752: }
753:
754: /** A utility class to implement {@link #getExtraCtrl}.
755: * It is used only by component developers.
756: */
757: protected class ExtraCtrl extends XulElement.ExtraCtrl implements
758: InputableX, Errorable {
759: //-- InputableX --//
760: public boolean setTextByClient(String value)
761: throws WrongValueException {
762: _txtByClient = value;
763: try {
764: final Object oldval = _value;
765: setText(value); //always since it might have func even not change
766: return oldval != _value; //test if modifed
767: } catch (WrongValueException ex) {
768: _errmsg = ex.getMessage();
769: //we have to 'remember' the error, so next call to getValue
770: //will throw an exception with proper value.
771: throw ex;
772: } finally {
773: _txtByClient = null;
774: }
775: }
776:
777: //-- Errorable --//
778: public void setErrorByClient(String value, String msg) {
779: _errmsg = msg != null && msg.length() > 0 ? msg : null;
780: }
781: }
782: }
|