Bug 169965 - [DataBinding] Provide examples of masking for Text
Summary: [DataBinding] Provide examples of masking for Text
Status: RESOLVED WORKSFORME
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.3   Edit
Hardware: PC Windows XP
: P3 enhancement (vote)
Target Milestone: ---   Edit
Assignee: Boris Bokowski CLA
QA Contact:
URL:
Whiteboard:
Keywords: api, helpwanted
Depends on:
Blocks:
 
Reported: 2007-01-09 08:30 EST by Orestis Markou CLA
Modified: 2011-09-14 12:25 EDT (History)
9 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Orestis Markou CLA 2007-01-09 08:30:26 EST
Build ID: I20070102

Steps To Reproduce:
When passing null as the BindSpec before the API change and renames, you used to get a PartialValidator for simple types (Double, Integer etc) for free. 

Now you don't. Instead you get a validator that returns STATUS.OK no matter what. You can see this in DefaultBindSpec, line 382.

As this is (IMO) by far the commonest case when using forms to edit objects (ie. don't allow user to enter invalid value), I would like that back, please :)

It makes no sense to allow text in a text box that binds to a number, and the user won't find out until they press OK. Since the plumbing for assigning validators already exists, and works, I wonder why this functionality was removed.

More information:
Comment 1 Orestis Markou CLA 2007-01-09 08:31:04 EST
This is how I bind my stuff:

dbc.bindValue(SWTObservables.observeText(bodyWeight, SWT.Modify),BeansObservables.observeValue(measurement, "bodyWeight"), null);

Comment 2 Orestis Markou CLA 2007-01-09 08:36:21 EST
I would also like to propose, if this is going to be fixed, to take into account the issues about i18n and l10n. In the previous implementation, regexps where used, as far as I remember. This provided a basic functionality, but it was too simple: my users are Greek (so am I) and we use different delimiters for the thousands and decimals:

10.000.00,32 instead of
10,000,00.32

I just told them to use the English way, and they didn't mind (much). But I always felt that this should be fixed.
Comment 3 Boris Bokowski CLA 2007-01-09 11:18:27 EST
I removed the partial validator implementations exactly for that reason (l10n).  I'm from Germany where numbers are written the Greek (or Arab?) way too.

Brad - the Eclipse way would be to use ICU4J's DecimalFormat, are you OK with adding a dependency on ICU4J for this?  See http://wiki.eclipse.org/index.php/ICU4J.
Comment 4 Dave Orme CLA 2007-01-09 15:07:48 EST
I'm not Brad, but I'm a committer so I'll +1 on this. :)
Comment 5 Brad Reynolds CLA 2007-01-09 20:40:21 EST
Since DecimalFormat is a part of the replacement plug-in I'm in.

+ 1
Comment 6 Boris Bokowski CLA 2007-01-16 11:55:42 EST
Sorry I probably wasn't fully awake when I looked at this.  The problem is how do we get something like a regexp, or even a list of valid characters for a data type like float in a way that works with i10n?  Does anybody know more about DecimalFormat.toLocalizedPattern() and if it could be used for this purpose?
Comment 7 Orestis Markou CLA 2007-01-16 14:18:26 EST
I don't know about DecimalFormat, but the naive way for doing this would be trying to use Float.parseFloat(string) that AFAIK uses the current Locale to do it's thing, and catch any NumberFormatExceptions that arise...
Comment 8 Boris Bokowski CLA 2007-01-16 15:20:48 EST
(In reply to comment #7)
> I don't know about DecimalFormat, but the naive way for doing this would be
> trying to use Float.parseFloat(string) that AFAIK uses the current Locale to do
> it's thing, and catch any NumberFormatExceptions that arise...

This does not work because there are substrings of valid floats that don't successfully parse, e.g. "-", or ".".
Comment 9 Orestis Markou CLA 2007-01-16 15:26:29 EST
I said  it was naive, didn't I ? :)

I'll try to come up with something better, as this is a major no go for me...
Comment 10 Brad Reynolds CLA 2007-01-16 19:39:50 EST
(In reply to comment #6)
> Sorry I probably wasn't fully awake when I looked at this.  The problem is how
> do we get something like a regexp, or even a list of valid characters for a
> data type like float in a way that works with i10n?  Does anybody know more
> about DecimalFormat.toLocalizedPattern() and if it could be used for this
> purpose?
> 

Are you saying that you don't think the format symbols defined for DecimalFormat will support our needs?
Comment 11 Brad Reynolds CLA 2007-02-25 00:44:36 EST
I'll take a look into this.  I really want to see this done for 1.0.
Comment 12 Brad Reynolds CLA 2007-02-25 18:53:41 EST
We have a few issues here...
1. We're not localizing the display of integers, floats, or doubles by default.
2. We're not partiallly validating any of the above types by default.

#1 should be able to use the com.ibm.icu.text.DecimalFormat for formatting and parsing of the value in the default converter.  We would assume the current locale by default and if any customization is necessary the consumer would have to write their own converter.  Also since DecimalFormat is a part of the replacement plugin this wouldn't have much of an impact on consumers.

#2 is trickier.  We can retrieve the format symbols from DecimalFormatSymbols.  It provides API to retrieve the grouping separator, decimal separator, etc.  and would allow us to cover the case of "-", ".", etc.  But the current design of partial validation is going to make this difficult and costly.  The value received by the partial validator is what the full value will be rather than what was just entered.  We'd have to parse the value and if that fails we'd have to check for combinations of any of the format symbols on every key stroke.  We can't rely upon java.util.regex for this as it is not part of the CDC 1.0 foundation.  We'd either have to roll our own or include a third party library.  If the partial validator was passed only what changed we'd be able to partially validate this but this is not the case today.  Dave or Boris if you have any input on why this was written this way I'd appreciate knowing.  My guess is that this was meant to be an abstraction for masking.  My feelings are that since we're losing the context of the change this could become costly.  If we were to validate each character of the value in order to do this properly we'd have to add a dependency on the full version of com.ibm.icu.text and disallow the replacement plugin option.  The reason being that in JSE 1.4.x Character.isDigit(char) doesn't take into account supplementary characters[1].  This is a part of ICU4J and also Java 5.  There might be a way to not require this if building against a 1.5 JDK but I'd have to do some digging.

I'm going to look into this more this week but needed to get my thoughts out.

[1] http://java.sun.com/developer/technicalArticles/Intl/Supplementary/
Comment 13 Brad Reynolds CLA 2007-03-01 23:03:43 EST
I'm working on converting the converters to use ICU4J and that's going fine.  But the "partial validation" is making me pretty uneasy.  As a result I'm thinking about removing it... or I guess not adding it back.  Bear with me...

Partial validation was meant as an abstraction for masking Text widgets.  I don't know of a use case other than Text and if someone has one I'd like to hear it.  But partial validation abstracts so much that it's going to be costly in regards to performance.  Most masking implementations that I've written or seen convey the value being added and it's position.  This allows for fine grained processing which intelligent masking requires.  By providing an abstraction we're forcing the validator to unravel the diff to determine what was entered and where in was entered.

So my opinion at this point in time is that we should remove partial validation but provide API to expose what is necessary for a consumer to implement intelligent masking.  We would need to provide examples of this as it will be a common request so that with minimal effort the consumer could get up and running with it.

Comments?  This might be a good topic to discuss at EclipseCon in the presence of beer. ;)
Comment 14 Bill Blalock CLA 2007-03-06 09:39:17 EST
>..but provide API to expose what is necessary for a consumer to implement
>intelligent masking.  We would need to provide examples of this as it will be a
>common request so that with minimal effort the consumer could get up and
>running with it.

As a consumer I would like to see data binding simplify the chore of validating what goes into the widget from SWT.  Intelligent masking would be very nice.

At Eclipsecon 2007 BOF we saw a great presentation.  I asked about using databinding to filter the allowed keys.  My example was a US Zip code.  It should be 5 numeric characters.  My question was " could non numeric characters be ignored.

An example program was modified to uppercase and then only vowels for the name.  This was cool and impressive but there were two problems.  One was identified immediately by the developers.  The "aeiou" was translated to "AEIOU" in the field in the table but not in the widget in which it was being typed.

The second problem, to my mind, was that when the first non-vowel was entered the field was invalid.  How does the user simply fix it?  As the programmer I would like to simple ignore characters which don't fit the rules. If this were typed

 aeIoYU

I want to see in the widget to which I am entering the data.  

 AEIOU

The lower case keys uppercased the the "Y" simply dropped.

I like the idea of using regular expressions and encourage the team to put them back in.

This history shows how regular expressions don't work well for numbers.  I suggest that something else supplement, or be used in place of, regular expressions.  It seems that text representing numbers is not well suited to be evaluated as regular expressions but they do have some fairly simple rules.

digits only except
- none or one decimal delimiter dependent on the nl
- none, one or many thousands delimited depending on the nl
- none or one negative sign at the beginning or end of the string (nl dependent)

It seems that this would work for simple data types (int, float, etc) and BigDecimal.  Please don't forget BigDecimal (and I suppose the other Bigs).

I think that there is a difference between a number and text which contains numbers.  I may be wrong but I don't see that there is really much intellegent masking, except for range perhaps, for true numbers.

For example, a "telephone number" is not a numeric value.  You dont add it or manipulat it as a number.  It is simply text which is formatted by rules.

Things that are true numbers don't have much formatting and simple validation.  Counts, amounts, measurements, etc.  The validation that they do have isn't done well by regex.  Why not treat them differently?
  
In summary I want to see from databinding (or maybe parly from SWT)
- intellegent masking
- basic keystroke validation
- option to use regular expression for keystroke validation
- choice to simply ignore keystrokes which are valid as user enters them
- option to use regular expressions to validate field
- testing / editing text with is going into a primitive or BigXXXXX with rules appropriate to the numeric data in place of regular expressions.

If databinding can't provide all of this, then I would like to see hooks to do it myself and plenty of examples how.

Thanks you all very much.  Great work.

Comment 15 Brad Reynolds CLA 2007-03-29 10:13:14 EDT
I have some code locally but it's not worth publishing at this point.  I'm adding the "helpwanted" keyword as I won't be getting back to this soon.
Comment 16 Brad Reynolds CLA 2007-04-11 23:34:19 EDT
Removing blocking of bug 154132 (plan item).

Also I'm rewording the summary as partial validation has been removed from the binding pipleine and this request is now for examples of how to perform partial validation.  The i18n issues mentioned in comment 2 are being handled by our default converters and validators.  If not please open a new bug with details of the issue.
Comment 17 Matt Carter CLA 2007-07-30 13:39:39 EDT
In case this is of use to anyone, I'm currently doing partial validation using the inline code below (requires Data Binding patch comment #7 on Bug 180392 too):

	/**
	 * Apply partial validation behaviour (disallow invalid characters from being entered)
	 * to a Text control for numeric fields.
	 * @param text Text control
	 * @param type Type being bound (e.g. int.class)
	 */
	private static void applyPartialValidation(final Text text, final Class type) {
		if(type == int.class || type == Integer.class)
			new PartialValidator(text, intConverter != null ? 
					intConverter : (intConverter = StringToNumberConverter.toInteger(false)));
		else if(type == long.class || type == Long.class)
			new PartialValidator(text, longConverter != null ? 
					longConverter : (longConverter = StringToNumberConverter.toLong(false)));
		else if(type == double.class || type == Double.class)
			new PartialValidator(text, doubleConverter != null ? 
					doubleConverter : (doubleConverter = StringToNumberConverter.toDouble(false)));
		else if(type == byte.class || type == Byte.class)
			new PartialValidator(text, byteConverter != null ? 
					byteConverter : (byteConverter = StringToNumberConverter.toByte(false)));
		else if(type == short.class || type == Short.class)
			new PartialValidator(text, shortConverter != null ? 
					shortConverter : (shortConverter = StringToNumberConverter.toShort(false)));
		else if(type == char.class || type == Character.class)
			text.setTextLimit(1);
	}
	

	/* Re-used partial validation converters */
	private static StringToNumberConverter intConverter = null;
	private static StringToNumberConverter longConverter = null;
	private static StringToNumberConverter doubleConverter = null;
	private static StringToNumberConverter byteConverter = null;
	private static StringToNumberConverter shortConverter = null;
	
	/**
	 * Apply partial validation (disallow invalid characters from being entered)
	 * to a Text control for numeric fields of the given type.
	 * @param text Text or Combo control
	 * @param type Type being bound (e.g. int.class)
	 */
	private static class PartialValidator implements VerifyListener {
		final Text text;
		final StringToNumberConverter converter;
		public PartialValidator(final Text text, StringToNumberConverter converter) {
			this.text = text;
			this.converter = converter;
			text.addVerifyListener(this);
		}
		public void verifyText(VerifyEvent e) {
			// Ensure valid number
			String currentText = text.getText();
			String newText = currentText.substring(0, e.start) + e.text
			+ currentText.substring(e.end);
			if(newText.equals("")) return;
			// Try parsing
			try {
				Object out = converter.convert(newText);
				if(out == null) {
					// Disallow bad number
					e.doit = false;
				}
			} catch(Exception ex) {
				e.doit = false;
			}
		}
	}
Comment 18 Matthew Hall CLA 2011-09-14 00:39:45 EDT
I think most of this bug is satisfied by the Nebula FormattedText. Ok to resolve worksforme?
Comment 19 Dave Orme CLA 2011-09-14 12:02:24 EDT
+1
Comment 20 Matthew Hall CLA 2011-09-14 12:25:25 EDT
Ok, closing