### Eclipse Workspace Patch 1.0 #P org.eclipse.core.databinding Index: src/org/eclipse/core/internal/databinding/BindingMessages.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingMessages.java,v retrieving revision 1.6 diff -u -r1.6 BindingMessages.java --- src/org/eclipse/core/internal/databinding/BindingMessages.java 9 May 2008 14:13:00 -0000 1.6 +++ src/org/eclipse/core/internal/databinding/BindingMessages.java 29 Aug 2009 12:45:51 -0000 @@ -18,7 +18,7 @@ /** * @since 1.0 - * + * */ public class BindingMessages { @@ -112,7 +112,7 @@ * Returns the resource object with the given key in the resource bundle for * JFace Data Binding. If there isn't any value under the given key, the key * is returned. - * + * * @param key * the resource name * @return the string @@ -128,16 +128,27 @@ /** * Returns a formatted string with the given key in the resource bundle for * JFace Data Binding. - * + * * @param key * @param arguments * @return formatted string, the key if the key is invalid */ - public static String formatString(String key, Object[] arguments) { + public static String getFormattedString(String key, Object[] arguments) { try { - return MessageFormat.format(bundle.getString(key), arguments); + return formatMessage(getString(key), arguments); } catch (MissingResourceException e) { return key; } } + + /** + * Formats the given message pattern with the provided arguments. + * + * @param pattern + * @param arguments + * @return formatted string + */ + public static String formatMessage(String pattern, Object[] arguments) { + return MessageFormat.format(pattern, arguments); + } } Index: src/org/eclipse/core/internal/databinding/conversion/StringToDateConverter.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToDateConverter.java,v retrieving revision 1.2 diff -u -r1.2 StringToDateConverter.java --- src/org/eclipse/core/internal/databinding/conversion/StringToDateConverter.java 25 May 2009 20:52:19 -0000 1.2 +++ src/org/eclipse/core/internal/databinding/conversion/StringToDateConverter.java 29 Aug 2009 12:45:52 -0000 @@ -15,13 +15,31 @@ import org.eclipse.core.databinding.conversion.IConverter; +import com.ibm.icu.text.DateFormat; /** * Convert a String to a java.util.Date, respecting the current locale * * @since 1.0 */ -public class StringToDateConverter extends DateConversionSupport implements IConverter { +public class StringToDateConverter extends DateConversionSupport implements + IConverter { + + /** + * + */ + public StringToDateConverter() { + super(); + } + + /** + * + * @param formats + */ + public StringToDateConverter(DateFormat[] formats) { + super(formats); + } + public Object convert(Object source) { return parse(source.toString()); } @@ -32,5 +50,5 @@ public Object getToType() { return Date.class; - } + } } Index: src/org/eclipse/core/internal/databinding/conversion/DateConversionSupport.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateConversionSupport.java,v retrieving revision 1.5 diff -u -r1.5 DateConversionSupport.java --- src/org/eclipse/core/internal/databinding/conversion/DateConversionSupport.java 25 May 2009 20:52:20 -0000 1.5 +++ src/org/eclipse/core/internal/databinding/conversion/DateConversionSupport.java 29 Aug 2009 12:45:52 -0000 @@ -31,58 +31,74 @@ *
*/ public abstract class DateConversionSupport { - private final static int DATE_FORMAT=DateFormat.SHORT; - private final static int DEFAULT_FORMATTER_INDEX=0; + private final static int DATE_FORMAT = DateFormat.SHORT; + private final static int DEFAULT_FORMATTER_INDEX = 0; - private final static int NUM_VIRTUAL_FORMATTERS=1; + private final static int NUM_VIRTUAL_FORMATTERS = 1; /** - * Alternative formatters for date, time and date/time. - * Raw milliseconds are covered as a special case. + * Alternative formatters for date, time and date/time. Raw milliseconds are + * covered as a special case. */ - // TODO: These could be shared, but would have to be synchronized. - private DateFormat[] formatters = { - new SimpleDateFormat(BindingMessages.getString(BindingMessages.DATE_FORMAT_DATE_TIME)), - new SimpleDateFormat(BindingMessages.getString(BindingMessages.DATEFORMAT_TIME)), - DateFormat.getDateTimeInstance(DATE_FORMAT, DateFormat.SHORT), - DateFormat.getDateInstance(DATE_FORMAT), - DateFormat.getTimeInstance(DateFormat.SHORT), - DateFormat.getDateTimeInstance(DATE_FORMAT,DateFormat.MEDIUM), - DateFormat.getTimeInstance(DateFormat.MEDIUM) - }; + private final DateFormat[] formatters; + + /** + * + */ + public DateConversionSupport() { + // TODO: These could be shared, but would have to be synchronized. + this(new DateFormat[] { + new SimpleDateFormat(BindingMessages + .getString(BindingMessages.DATE_FORMAT_DATE_TIME)), + new SimpleDateFormat(BindingMessages + .getString(BindingMessages.DATEFORMAT_TIME)), + DateFormat.getDateTimeInstance(DATE_FORMAT, DateFormat.SHORT), + DateFormat.getDateInstance(DATE_FORMAT), + DateFormat.getTimeInstance(DateFormat.SHORT), + DateFormat.getDateTimeInstance(DATE_FORMAT, DateFormat.MEDIUM), + DateFormat.getTimeInstance(DateFormat.MEDIUM) }); + } + + /** + * + * @param formats + */ + public DateConversionSupport(DateFormat[] formats) { + this.formatters = formats; + } /** * Tries all available formatters to parse the given string according to the * default locale or as a raw millisecond value and returns the result of the * first successful run. - * + * * @param str A string specifying a date according to the default locale or in raw milliseconds * @return The parsed date, or null, if no available formatter could interpret the input string */ protected Date parse(String str) { for (int formatterIdx = 0; formatterIdx < formatters.length; formatterIdx++) { - Date parsed=parse(str,formatterIdx); - if(parsed!=null) { + Date parsed = parse(str, formatterIdx); + if (parsed != null) { return parsed; } } return null; } - protected Date parse(String str,int formatterIdx) { - if(formatterIdx>=0) { - ParsePosition pos=new ParsePosition(0); - if (str == null) { - return null; - } - Date date=formatters[formatterIdx].parse(str,pos); - if(pos.getErrorIndex()!=-1||pos.getIndex()!=str.length()) { - return null; - } - return date; + protected Date parse(String str, int formatterIdx) { + if (formatterIdx >= 0) { + ParsePosition pos = new ParsePosition(0); + if (str == null) { + return null; + } + Date date = formatters[formatterIdx].parse(str, pos); + if (pos.getErrorIndex() != -1 || pos.getIndex() != str.length()) { + return null; + } + return date; } try { - long millisecs=Long.parseLong(str); + long millisecs = Long.parseLong(str); return new Date(millisecs); } catch(NumberFormatException exc) { @@ -96,20 +112,20 @@ * @return a string representation of the given date according to the default locale */ protected String format(Date date) { - return format(date,DEFAULT_FORMATTER_INDEX); + return format(date, DEFAULT_FORMATTER_INDEX); } - protected String format(Date date,int formatterIdx) { + protected String format(Date date, int formatterIdx) { if (date == null) return null; - if(formatterIdx>=0) { + if (formatterIdx >= 0) { return formatters[formatterIdx].format(date); } return String.valueOf(date.getTime()); } protected int numFormatters() { - return formatters.length+NUM_VIRTUAL_FORMATTERS; + return formatters.length + NUM_VIRTUAL_FORMATTERS; } /** @@ -118,7 +134,7 @@ * This is for testing purposes only and should not be a part of the API if * this class was to be exposed. * - * + * * @param index * @return date format */ Index: src/org/eclipse/core/internal/databinding/conversion/StringToNumberParser.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToNumberParser.java,v retrieving revision 1.6 diff -u -r1.6 StringToNumberParser.java --- src/org/eclipse/core/internal/databinding/conversion/StringToNumberParser.java 28 Oct 2008 19:54:39 -0000 1.6 +++ src/org/eclipse/core/internal/databinding/conversion/StringToNumberParser.java 29 Aug 2009 12:45:52 -0000 @@ -21,7 +21,7 @@ /** * Utility class for the parsing of strings to numbers. - * + * * @since 1.0 */ public class StringToNumberParser { @@ -73,7 +73,7 @@ /** * The result of a parse operation. - * + * * @since 1.0 */ public static class ParseResult { @@ -84,7 +84,7 @@ * The number as a result of the conversion.null
if the
* value could not be converted or if the type is not a primitive and
* the value was an empty string.
- *
+ *
* @return number
*/
public Number getNumber() {
@@ -94,7 +94,7 @@
/**
* ParsePosition if an error occurred while parsing. null
* if no error occurred.
- *
+ *
* @return parse position
*/
public ParsePosition getPosition() {
@@ -104,7 +104,7 @@
/**
* Formats an appropriate message for a parsing error.
- *
+ *
* @param value
* @param position
* @return message
@@ -115,17 +115,17 @@
.getErrorIndex() : position.getIndex();
if (errorIndex < value.length()) {
- return BindingMessages.formatString(BindingMessages.VALIDATE_NUMBER_PARSE_ERROR,
+ return BindingMessages.getFormattedString(BindingMessages.VALIDATE_NUMBER_PARSE_ERROR,
new Object[] { value, new Integer(errorIndex + 1),
new Character(value.charAt(errorIndex)) });
}
- return BindingMessages.formatString(BindingMessages.VALIDATE_NUMBER_PARSE_ERROR_NO_CHARACTER,
+ return BindingMessages.getFormattedString(BindingMessages.VALIDATE_NUMBER_PARSE_ERROR_NO_CHARACTER,
new Object[] { value, new Integer(errorIndex + 1) });
}
/**
* Formats an appropriate message for an out of range error.
- *
+ *
* @param minValue
* @param maxValue
* @param numberFormat when accessed method synchronizes on instance
@@ -133,6 +133,22 @@
*/
public static String createOutOfRangeMessage(Number minValue,
Number maxValue, NumberFormat numberFormat) {
+ return createOutOfRangeMessage(
+ BindingMessages.getString("Validate_NumberOutOfRangeError"), minValue, maxValue, numberFormat); //$NON-NLS-1$
+ }
+
+ /**
+ * Formats an appropriate message for an out of range error.
+ *
+ * @param message
+ * @param minValue
+ * @param maxValue
+ * @param numberFormat
+ * when accessed method synchronizes on instance
+ * @return message
+ */
+ public static String createOutOfRangeMessage(String message,
+ Number minValue, Number maxValue, NumberFormat numberFormat) {
String min = null;
String max = null;
@@ -141,14 +157,14 @@
max = numberFormat.format(maxValue);
}
- return BindingMessages.formatString(
- "Validate_NumberOutOfRangeError", new Object[] { min, max }); //$NON-NLS-1$
+ return BindingMessages.formatMessage(message, new Object[] { min,
+ max });
}
/**
* Returns true
if the provided number
is in
* the range of a integer.
- *
+ *
* @param number
* @return true
if a valid integer
* @throws IllegalArgumentException
@@ -160,7 +176,7 @@
/**
* Validates the range of the provided number
.
- *
+ *
* @param number
* @param bitLength number of bits allowed to be in range
* @return true
if in range
@@ -204,7 +220,7 @@
/**
* Returns true
if the provided number
is in
* the range of a long.
- *
+ *
* @param number
* @return true
if in range
* @throws IllegalArgumentException
@@ -217,7 +233,7 @@
/**
* Returns true
if the provided number
is in
* the range of a float.
- *
+ *
* @param number
* @return true
if in range
* @throws IllegalArgumentException
@@ -267,7 +283,7 @@
/**
* Returns true
if the provided number
is in
* the range of a double.
- *
+ *
* @param number
* @return true
if in range
* @throws IllegalArgumentException
@@ -280,7 +296,7 @@
/**
* Returns true
if the provided number
is in
* the range of a short.
- *
+ *
* @param number
* @return true
if in range
*/
@@ -291,7 +307,7 @@
/**
* Returns true
if the provided number
is in
* the range of a byte.
- *
+ *
* @param number
* @return true
if in range
*/
Index: src/org/eclipse/core/internal/databinding/conversion/DateToStringConverter.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateToStringConverter.java,v
retrieving revision 1.2
diff -u -r1.2 DateToStringConverter.java
--- src/org/eclipse/core/internal/databinding/conversion/DateToStringConverter.java 25 May 2009 20:52:19 -0000 1.2
+++ src/org/eclipse/core/internal/databinding/conversion/DateToStringConverter.java 29 Aug 2009 12:45:52 -0000
@@ -15,17 +15,35 @@
import org.eclipse.core.databinding.conversion.IConverter;
+import com.ibm.icu.text.DateFormat;
/**
- * Converts a Java.util.Date to a String using the current locale. Null date
+ * Converts a Java.util.Date to a String using the current locale. Null date
* values are converted to an empty string.
*
* @since 1.0
*/
-public class DateToStringConverter extends DateConversionSupport implements IConverter {
+public class DateToStringConverter extends DateConversionSupport implements
+ IConverter {
+
+ /**
+ *
+ */
+ public DateToStringConverter() {
+ super();
+ }
+
+ /**
+ *
+ * @param format
+ */
+ public DateToStringConverter(DateFormat format) {
+ super(new DateFormat[] { format });
+ }
+
public Object convert(Object source) {
if (source != null)
- return format((Date)source);
+ return format((Date) source);
return ""; //$NON-NLS-1$
}
@@ -35,5 +53,5 @@
public Object getToType() {
return String.class;
- }
+ }
}
Index: src/org/eclipse/core/internal/databinding/validation/StringToIntegerValidator.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToIntegerValidator.java,v
retrieving revision 1.2
diff -u -r1.2 StringToIntegerValidator.java
--- src/org/eclipse/core/internal/databinding/validation/StringToIntegerValidator.java 1 Apr 2007 20:58:13 -0000 1.2
+++ src/org/eclipse/core/internal/databinding/validation/StringToIntegerValidator.java 29 Aug 2009 12:45:53 -0000
@@ -13,7 +13,6 @@
import org.eclipse.core.internal.databinding.conversion.StringToNumberParser;
-
/**
* Validates that a string is of the appropriate format and is in the range of
* an integer.
@@ -31,6 +30,16 @@
super(converter, MIN, MAX);
}
+ /**
+ * @param converter
+ * @param parseErrorMessage
+ * @param outOfRangeMessage
+ */
+ public StringToIntegerValidator(NumberFormatConverter converter,
+ String parseErrorMessage, String outOfRangeMessage) {
+ super(converter, MIN, MAX, parseErrorMessage, outOfRangeMessage);
+ }
+
/* (non-Javadoc)
* @see org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator#inRange(java.lang.Number)
*/
Index: src/org/eclipse/core/internal/databinding/validation/StringToDateValidator.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToDateValidator.java,v
retrieving revision 1.5
diff -u -r1.5 StringToDateValidator.java
--- src/org/eclipse/core/internal/databinding/validation/StringToDateValidator.java 9 May 2008 14:13:00 -0000 1.5
+++ src/org/eclipse/core/internal/databinding/validation/StringToDateValidator.java 29 Aug 2009 12:45:53 -0000
@@ -28,24 +28,36 @@
public class StringToDateValidator implements IValidator {
private final StringToDateConverter converter;
+ private final String parseErrorMessage;
+
/**
* @param converter
*/
public StringToDateValidator(StringToDateConverter converter) {
+ this(converter, null);
+ }
+
+ /**
+ * @param converter
+ * @param parseErrorMessage
+ */
+ public StringToDateValidator(StringToDateConverter converter,
+ String parseErrorMessage) {
this.converter = converter;
+ this.parseErrorMessage = parseErrorMessage;
}
/*
* (non-Javadoc)
- *
+ *
* @see org.eclipse.core.databinding.validation.IValidator#validate(java.lang.Object)
*/
public IStatus validate(Object value) {
- if (value instanceof String && ((String)value).trim().length()==0) {
+ if (value instanceof String && ((String) value).trim().length() == 0) {
return Status.OK_STATUS;
}
Object convertedValue = converter.convert(value);
- //The StringToDateConverter returns null if it can't parse the date.
+ // The StringToDateConverter returns null if it can't parse the date.
if (convertedValue == null) {
return ValidationStatus.error(getErrorMessage());
}
@@ -55,10 +67,14 @@
/*
* (non-Javadoc)
- *
+ *
* @see org.eclipse.core.internal.databinding.validation.WrappedConverterValidator#getErrorMessage()
*/
protected String getErrorMessage() {
+ if (parseErrorMessage != null) {
+ return parseErrorMessage;
+ }
+
Date sampleDate = new Date();
// FIXME We need to use the information from the
@@ -79,7 +95,7 @@
private static class FormatUtil extends DateConversionSupport {
/*
* (non-Javadoc)
- *
+ *
* @see org.eclipse.core.internal.databinding.conversion.DateConversionSupport#numFormatters()
*/
protected int numFormatters() {
@@ -88,7 +104,7 @@
/*
* (non-Javadoc)
- *
+ *
* @see org.eclipse.core.internal.databinding.conversion.DateConversionSupport#format(java.util.Date)
*/
protected String format(Date date) {
@@ -97,7 +113,7 @@
/*
* (non-Javadoc)
- *
+ *
* @see org.eclipse.core.internal.databinding.conversion.DateConversionSupport#format(java.util.Date,
* int)
*/
Index: src/org/eclipse/core/internal/databinding/validation/AbstractStringToNumberValidator.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/AbstractStringToNumberValidator.java,v
retrieving revision 1.2
diff -u -r1.2 AbstractStringToNumberValidator.java
--- src/org/eclipse/core/internal/databinding/validation/AbstractStringToNumberValidator.java 4 Jul 2007 20:08:45 -0000 1.2
+++ src/org/eclipse/core/internal/databinding/validation/AbstractStringToNumberValidator.java 29 Aug 2009 12:45:52 -0000
@@ -11,6 +11,8 @@
package org.eclipse.core.internal.databinding.validation;
+import java.text.ParsePosition;
+
import org.eclipse.core.databinding.validation.IValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.internal.databinding.conversion.StringToNumberParser;
@@ -31,7 +33,10 @@
private final Number min;
private final Number max;
- private String outOfRangeMessage;
+ private final String parseErrorMessage;
+ private final String outOfRangeMessage;
+
+ private String formattedOutOfRangeMessage;
/**
* Constructs a new instance.
@@ -42,9 +47,26 @@
*/
protected AbstractStringToNumberValidator(NumberFormatConverter converter,
Number min, Number max) {
+ this(converter, min, max, null, null);
+ }
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param converter converter and thus formatter to be used in validation
+ * @param min minimum value, used for reporting a range error to the user
+ * @param max maximum value, used for reporting a range error to the user
+ * @param parseErrorMessage
+ * @param outOfRangeMessage
+ */
+ protected AbstractStringToNumberValidator(NumberFormatConverter converter,
+ Number min, Number max, String parseErrorMessage,
+ String outOfRangeMessage) {
this.converter = converter;
this.min = min;
this.max = max;
+ this.parseErrorMessage = parseErrorMessage;
+ this.outOfRangeMessage = outOfRangeMessage;
if (converter.getToType() instanceof Class) {
Class clazz = (Class) converter.getToType();
@@ -69,17 +91,15 @@
if (result.getNumber() != null) {
if (!isInRange(result.getNumber())) {
- if (outOfRangeMessage == null) {
- outOfRangeMessage = StringToNumberParser
- .createOutOfRangeMessage(min, max, converter
- .getNumberFormat());
+ if (formattedOutOfRangeMessage == null) {
+ formattedOutOfRangeMessage = createOutOfRangeMessage();
}
- return ValidationStatus.error(outOfRangeMessage);
+ return ValidationStatus.error(formattedOutOfRangeMessage);
}
} else if (result.getPosition() != null) {
- String parseErrorMessage = StringToNumberParser.createParseErrorMessage(
- (String) value, result.getPosition());
+ String parseErrorMessage = createParseErrorMessage((String) value,
+ result.getPosition());
return ValidationStatus.error(parseErrorMessage);
}
@@ -94,4 +114,22 @@
* @return true
if in range
*/
protected abstract boolean isInRange(Number number);
+
+ private String createParseErrorMessage(String input,
+ ParsePosition parsePosition) {
+ if (parseErrorMessage == null) {
+ return StringToNumberParser.createParseErrorMessage(input,
+ parsePosition);
+ }
+ return parseErrorMessage;
+ }
+
+ private String createOutOfRangeMessage() {
+ if (outOfRangeMessage == null) {
+ return StringToNumberParser.createOutOfRangeMessage(min, max,
+ converter.getNumberFormat());
+ }
+ return StringToNumberParser.createOutOfRangeMessage(outOfRangeMessage,
+ min, max, converter.getNumberFormat());
+ }
}
Index: META-INF/MANIFEST.MF
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/META-INF/MANIFEST.MF,v
retrieving revision 1.21
diff -u -r1.21 MANIFEST.MF
--- META-INF/MANIFEST.MF 25 Aug 2009 04:57:27 -0000 1.21
+++ META-INF/MANIFEST.MF 29 Aug 2009 12:45:51 -0000
@@ -8,6 +8,7 @@
Bundle-Localization: plugin
Export-Package: org.eclipse.core.databinding,
org.eclipse.core.databinding.conversion;x-internal:=false,
+ org.eclipse.core.databinding.editing,
org.eclipse.core.databinding.validation;x-internal:=false,
org.eclipse.core.internal.databinding;x-friends:="org.eclipse.core.databinding.beans",
org.eclipse.core.internal.databinding.conversion;x-friends:="org.eclipse.jface.tests.databinding",
Index: src/org/eclipse/core/internal/databinding/validation/DateRangeValidator.java
===================================================================
RCS file: src/org/eclipse/core/internal/databinding/validation/DateRangeValidator.java
diff -N src/org/eclipse/core/internal/databinding/validation/DateRangeValidator.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/internal/databinding/validation/DateRangeValidator.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ovidio Mallo - initial API and implementation (bug 183055)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+import java.text.MessageFormat;
+import java.util.Date;
+
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+
+import com.ibm.icu.text.DateFormat;
+
+/**
+ * @since 1.3
+ */
+public class DateRangeValidator implements IValidator {
+
+ private static final int UNDEFINED = -1;
+ private static final int AFTER = 0;
+ private static final int AFTER_EQUAL = 1;
+ private static final int BEFORE = 2;
+ private static final int BEFORE_EQUAL = 3;
+
+ // TODO: Externalize
+ private static final String AFTER_MESSAGE = "The date must be after {0}."; //$NON-NLS-1$
+ private static final String AFTER_EQUAL_MESSAGE = "The date must be after or on {0}."; //$NON-NLS-1$
+ private static final String BEFORE_MESSAGE = "The date must be before {0}."; //$NON-NLS-1$
+ private static final String BEFORE_EQUAL_MESSAGE = "The date must be before or on {0}."; //$NON-NLS-1$
+ private static final String WITHIN_RANGE_MESSAGE = "The date must lie between {0} and {1}."; //$NON-NLS-1$
+
+ private final Date min;
+ private final Date max;
+ private final int minConstraint;
+ private final int maxConstraint;
+ private final String validationMessage;
+ private final DateFormat format;
+
+ /**
+ *
+ * @param min
+ * @param max
+ * @param minConstraint
+ * @param maxConstraint
+ * @param validationMessage
+ * @param format
+ */
+ private DateRangeValidator(Date min, Date max, int minConstraint,
+ int maxConstraint, String validationMessage, DateFormat format) {
+ this.min = min;
+ this.max = max;
+ this.minConstraint = minConstraint;
+ this.maxConstraint = maxConstraint;
+ this.validationMessage = validationMessage;
+ this.format = format;
+ }
+
+ /**
+ *
+ * @param min
+ * @param validationMessage
+ * @param format
+ * @return .
+ */
+ public static DateRangeValidator after(Date min, String validationMessage,
+ DateFormat format) {
+ return new DateRangeValidator(min, null, AFTER, UNDEFINED,
+ defaultIfNull(validationMessage, AFTER_MESSAGE), format);
+ }
+
+ /**
+ *
+ * @param min
+ * @param validationMessage
+ * @param format
+ * @return .
+ */
+ public static DateRangeValidator afterEqual(Date min,
+ String validationMessage, DateFormat format) {
+ return new DateRangeValidator(min, null, AFTER_EQUAL, UNDEFINED,
+ defaultIfNull(validationMessage, AFTER_EQUAL_MESSAGE), format);
+ }
+
+ /**
+ *
+ * @param max
+ * @param validationMessage
+ * @param format
+ * @return .
+ */
+ public static DateRangeValidator before(Date max, String validationMessage,
+ DateFormat format) {
+ return new DateRangeValidator(null, max, UNDEFINED, BEFORE,
+ defaultIfNull(validationMessage, BEFORE_MESSAGE), format);
+ }
+
+ /**
+ *
+ * @param max
+ * @param validationMessage
+ * @param format
+ * @return .
+ */
+ public static DateRangeValidator beforeEqual(Date max,
+ String validationMessage, DateFormat format) {
+ return new DateRangeValidator(null, max, UNDEFINED, BEFORE_EQUAL,
+ defaultIfNull(validationMessage, BEFORE_EQUAL_MESSAGE), format);
+ }
+
+ /**
+ *
+ * @param min
+ * @param max
+ * @param validationMessage
+ * @param format
+ * @return .
+ */
+ public static DateRangeValidator range(Date min, Date max,
+ String validationMessage, DateFormat format) {
+ return new DateRangeValidator(min, max, AFTER_EQUAL, BEFORE_EQUAL,
+ defaultIfNull(validationMessage, WITHIN_RANGE_MESSAGE), format);
+ }
+
+ public IStatus validate(Object value) {
+ if (value != null) {
+ Date date = (Date) value;
+ if ((min != null && !fulfillsConstraint(date, min, minConstraint))
+ || (max != null && !fulfillsConstraint(date, max,
+ maxConstraint))) {
+ // TODO: Cache message?
+ return ValidationStatus.error(MessageFormat.format(
+ validationMessage, getFormattedRangeExtremas()));
+ }
+ }
+ return ValidationStatus.ok();
+ }
+
+ private boolean fulfillsConstraint(Date date1, Date date2, int constraint) {
+ switch (constraint) {
+ case AFTER:
+ return date1.after(date2);
+ case AFTER_EQUAL:
+ return date1.after(date2) || date1.equals(date2);
+ case BEFORE:
+ return date1.before(date2);
+ case BEFORE_EQUAL:
+ return date1.before(date2) || date1.equals(date2);
+ case UNDEFINED:
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported constraint: " + constraint); //$NON-NLS-1$
+ }
+ }
+
+ private String[] getFormattedRangeExtremas() {
+ // TODO: Synchronize formatting to make it thread safe?
+ if (min == null) {
+ return new String[] { format.format(max) };
+ } else if (max == null) {
+ return new String[] { format.format(min) };
+ }
+ return new String[] { format.format(min), format.format(max) };
+ }
+
+ private static String defaultIfNull(String string, String defaultString) {
+ return (string != null) ? string : defaultString;
+ }
+}
Index: src/org/eclipse/core/databinding/editing/StringEditing.java
===================================================================
RCS file: src/org/eclipse/core/databinding/editing/StringEditing.java
diff -N src/org/eclipse/core/databinding/editing/StringEditing.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/databinding/editing/StringEditing.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ovidio Mallo - initial API and implementation (bug 183055)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.editing;
+
+import java.util.regex.Pattern;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.internal.databinding.conversion.StringStripConverter;
+import org.eclipse.core.internal.databinding.validation.NonNullValidator;
+import org.eclipse.core.internal.databinding.validation.StringRegexValidator;
+
+/**
+ * @since 1.3
+ */
+public final class StringEditing extends Editing {
+
+ private String requiredMessage = null;
+
+ private String matchesMessage = null;
+
+ private StringEditing(IConverter targetConverter) {
+ setTargetConverter(targetConverter);
+ }
+
+ /**
+ * Creates a new editing object for strings which performs no validation or
+ * conversion.
+ *
+ * @return The new editing object which performs no validation or
+ * conversion.
+ */
+ public static StringEditing withDefaults() {
+ return new StringEditing(null);
+ }
+
+ /**
+ * Creates a new editing object which strips whitespace from both ends of
+ * the input string.
+ *
+ * @param stripToNull
+ * Whether to convert the input string to null
in
+ * case stripping the input string results in an empty string.
+ * @return The new editing object which strips whitespace from both ends of
+ * the input string.
+ */
+ public static StringEditing stripped(boolean stripToNull) {
+ return new StringEditing(new StringStripConverter(stripToNull));
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed integer is not
+ * null
. Uses the current {@link #requiredMessage(String)
+ * validation message}.
+ *
+ * @return This editing instance for method chaining.
+ */
+ public StringEditing required() {
+ addModelValidator(new NonNullValidator(requiredMessage));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the input string is
+ * null
.
+ *
+ * @param message
+ * The validation message to be issued in case the input string
+ * is null
.
+ * @return This editing instance for method chaining.
+ *
+ * @see #required()
+ */
+ public StringEditing requiredMessage(String message) {
+ this.requiredMessage = message;
+ return this;
+ }
+
+ /**
+ * Adds a model validator ensuring that the input string
+ * {@link Pattern#matches(String, CharSequence) matches} the given regular
+ * expression . Uses the current {@link #matchesMessage(String) validation
+ * message}.
+ *
+ * @param regex
+ * The regular expression which the input string must match.
+ * @return This editing instance for method chaining.
+ *
+ * @see Pattern#matches(String, CharSequence)
+ */
+ public StringEditing matches(String regex) {
+ addModelValidator(new StringRegexValidator(regex, matchesMessage));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the input string does
+ * not match a given regular expression.
+ *
+ * @param message
+ * The validation message to be issued in case the input string
+ * does not match a given regular expression.
+ * @return This editing instance for method chaining.
+ *
+ * @see #matches(String)
+ */
+ public StringEditing matchesMessage(String message) {
+ this.matchesMessage = message;
+ return this;
+ }
+}
Index: src/org/eclipse/core/internal/databinding/validation/NonNullValidator.java
===================================================================
RCS file: src/org/eclipse/core/internal/databinding/validation/NonNullValidator.java
diff -N src/org/eclipse/core/internal/databinding/validation/NonNullValidator.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/internal/databinding/validation/NonNullValidator.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ovidio Mallo - initial API and implementation (bug 183055)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * @since 1.3
+ */
+public class NonNullValidator implements IValidator {
+
+ private final String validationMessage;
+
+ /**
+ *
+ */
+ public NonNullValidator() {
+ this(null);
+ }
+
+ /**
+ * @param validationMessage
+ */
+ public NonNullValidator(String validationMessage) {
+ // TODO: Externalize
+ this.validationMessage = validationMessage != null ? validationMessage
+ : "The value must not be empty."; //$NON-NLS-1$
+ }
+
+ public IStatus validate(Object value) {
+ if (value == null) {
+ return ValidationStatus.error(validationMessage);
+ }
+ return ValidationStatus.ok();
+ }
+}
Index: src/org/eclipse/core/internal/databinding/validation/StringRegexValidator.java
===================================================================
RCS file: src/org/eclipse/core/internal/databinding/validation/StringRegexValidator.java
diff -N src/org/eclipse/core/internal/databinding/validation/StringRegexValidator.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/internal/databinding/validation/StringRegexValidator.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ovidio Mallo - initial API and implementation (bug 183055)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+import java.text.MessageFormat;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * @since 1.3
+ */
+public class StringRegexValidator implements IValidator {
+
+ // TODO: Externalize
+ private static final String REGEX_VALIDATION_MESSAGE = "The string must match the following regular expression: {0}."; //$NON-NLS-1$
+
+ private final Pattern pattern;
+
+ private final String validationMessage;
+
+ /**
+ *
+ * @param regex
+ */
+ public StringRegexValidator(String regex) {
+ this(regex, null);
+ }
+
+ /**
+ *
+ * @param regex
+ * @param validationMessage
+ */
+ public StringRegexValidator(String regex, String validationMessage) {
+ this.pattern = Pattern.compile(regex);
+ this.validationMessage = validationMessage != null ? validationMessage
+ : REGEX_VALIDATION_MESSAGE;
+ }
+
+ public IStatus validate(Object value) {
+ String input = (String) value;
+ if (input != null) {
+ Matcher matcher = pattern.matcher(input);
+ if (!matcher.matches()) {
+ // TODO: Cache message?
+ return ValidationStatus.error(MessageFormat.format(
+ validationMessage, new String[] { pattern.pattern() }));
+ }
+ }
+ return ValidationStatus.ok();
+ }
+}
Index: src/org/eclipse/core/internal/databinding/validation/IntegerRangeValidator.java
===================================================================
RCS file: src/org/eclipse/core/internal/databinding/validation/IntegerRangeValidator.java
diff -N src/org/eclipse/core/internal/databinding/validation/IntegerRangeValidator.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/internal/databinding/validation/IntegerRangeValidator.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ovidio Mallo - initial API and implementation (bug 183055)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.3
+ */
+public class IntegerRangeValidator extends NumberRangeValidator {
+
+ private static final Integer ZERO = new Integer(0);
+ private static final Integer ONE = new Integer(1);
+
+ /**
+ * @param min
+ * @param max
+ * @param minConstraint
+ * @param maxConstraint
+ * @param validationMessage
+ * @param format
+ */
+ private IntegerRangeValidator(Number min, Number max, int minConstraint,
+ int maxConstraint, String validationMessage, NumberFormat format) {
+ super(min, max, minConstraint, maxConstraint, validationMessage, format);
+ }
+
+ /**
+ *
+ * @param min
+ * @param validationMessage
+ * @param format
+ * @return .
+ */
+ public static IntegerRangeValidator greater(int min,
+ String validationMessage, NumberFormat format) {
+ return new IntegerRangeValidator(new Integer(min), null, GREATER,
+ UNDEFINED, defaultIfNull(validationMessage, GREATER_MESSAGE),
+ format);
+ }
+
+ /**
+ *
+ * @param min
+ * @param validationMessage
+ * @param format
+ * @return .
+ */
+ public static IntegerRangeValidator greaterEqual(int min,
+ String validationMessage, NumberFormat format) {
+ return new IntegerRangeValidator(new Integer(min), null, GREATER_EQUAL,
+ UNDEFINED, defaultIfNull(validationMessage,
+ GREATER_EQUAL_MESSAGE), format);
+ }
+
+ /**
+ *
+ * @param max
+ * @param validationMessage
+ * @param format
+ * @return .
+ */
+ public static IntegerRangeValidator less(int max, String validationMessage,
+ NumberFormat format) {
+ return new IntegerRangeValidator(null, new Integer(max), UNDEFINED,
+ LESS, defaultIfNull(validationMessage, LESS_MESSAGE), format);
+ }
+
+ /**
+ *
+ * @param max
+ * @param validationMessage
+ * @param format
+ * @return .
+ */
+ public static IntegerRangeValidator lessEqual(int max,
+ String validationMessage, NumberFormat format) {
+ return new IntegerRangeValidator(null, new Integer(max), UNDEFINED,
+ LESS_EQUAL,
+ defaultIfNull(validationMessage, LESS_EQUAL_MESSAGE), format);
+ }
+
+ /**
+ *
+ * @param min
+ * @param max
+ * @param validationMessage
+ * @param format
+ * @return .
+ */
+ public static IntegerRangeValidator range(int min, int max,
+ String validationMessage, NumberFormat format) {
+ return new IntegerRangeValidator(new Integer(min), new Integer(max),
+ GREATER_EQUAL, LESS_EQUAL, defaultIfNull(validationMessage,
+ WITHIN_RANGE_MESSAGE), format);
+ }
+
+ /**
+ *
+ * @param validationMessage
+ * @param format
+ * @return .
+ */
+ public static IntegerRangeValidator positive(String validationMessage,
+ NumberFormat format) {
+ return new IntegerRangeValidator(ONE, null, GREATER_EQUAL, UNDEFINED,
+ defaultIfNull(validationMessage, POSITIVE_MESSAGE), format);
+ }
+
+ /**
+ *
+ * @param validationMessage
+ * @param format
+ * @return .
+ */
+ public static IntegerRangeValidator nonNegative(String validationMessage,
+ NumberFormat format) {
+ return new IntegerRangeValidator(ZERO, null, GREATER_EQUAL, UNDEFINED,
+ defaultIfNull(validationMessage, NON_NEGATIVE_MESSAGE), format);
+ }
+
+ protected int compare(Number number1, Number number2) {
+ Integer integer1 = (Integer) number1;
+ Integer integer2 = (Integer) number2;
+ return integer1.compareTo(integer2);
+ }
+
+ private static String defaultIfNull(String string, String defaultString) {
+ return (string != null) ? string : defaultString;
+ }
+}
Index: src/org/eclipse/core/internal/databinding/validation/CompositeValidator.java
===================================================================
RCS file: src/org/eclipse/core/internal/databinding/validation/CompositeValidator.java
diff -N src/org/eclipse/core/internal/databinding/validation/CompositeValidator.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/internal/databinding/validation/CompositeValidator.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ovidio Mallo - initial API and implementation (bug 183055)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * @since 1.3
+ */
+public class CompositeValidator implements IValidator {
+
+ private final IValidator[] validators;
+
+ /**
+ * @param validators
+ */
+ public CompositeValidator(IValidator[] validators) {
+ this.validators = validators;
+ }
+
+ public IStatus validate(Object value) {
+ // TODO: Define an aggregation strategy.
+ int maxSeverity = IStatus.OK;
+ IStatus maxStatus = Status.OK_STATUS;
+ for (int i = 0; i < validators.length; i++) {
+ IValidator validator = validators[i];
+ IStatus status = validator.validate(value);
+ if (status.getSeverity() > maxSeverity) {
+ maxSeverity = status.getSeverity();
+ maxStatus = status;
+ }
+ }
+ return maxStatus;
+ }
+}
Index: src/org/eclipse/core/databinding/editing/IntegerEditing.java
===================================================================
RCS file: src/org/eclipse/core/databinding/editing/IntegerEditing.java
diff -N src/org/eclipse/core/databinding/editing/IntegerEditing.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/databinding/editing/IntegerEditing.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,373 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ovidio Mallo - initial API and implementation (bug 183055)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.editing;
+
+import org.eclipse.core.databinding.conversion.NumberToStringConverter;
+import org.eclipse.core.databinding.conversion.StringToNumberConverter;
+import org.eclipse.core.internal.databinding.validation.IntegerRangeValidator;
+import org.eclipse.core.internal.databinding.validation.NonNullValidator;
+import org.eclipse.core.internal.databinding.validation.StringToIntegerValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.3
+ */
+public final class IntegerEditing extends Editing {
+
+ private final NumberFormat format;
+
+ private String requiredMessage = null;
+
+ private String greaterMessage = null;
+
+ private String greaterEqualMessage = null;
+
+ private String lessMessage = null;
+
+ private String lessEqualMessage = null;
+
+ private String rangeMessage = null;
+
+ private String positiveMessage = null;
+
+ private String nonNegativeMessage = null;
+
+ private IntegerEditing(NumberFormat format, String parseErrorMessage,
+ String outOfRangeMessage) {
+ this.format = format;
+
+ StringToNumberConverter targetConverter = StringToNumberConverter
+ .toInteger(format, false);
+
+ addTargetValidator(new StringToIntegerValidator(targetConverter,
+ parseErrorMessage, outOfRangeMessage));
+ setTargetConverter(targetConverter);
+ setModelConverter(NumberToStringConverter.fromInteger(format, false));
+ }
+
+ /**
+ * Creates a new editing object which defaults the validations and
+ * conversions used for editing based on the platform's locale. Uses the
+ * default validation messages.
+ *
+ * @return The new editing object using the default validations and
+ * conversions for editing.
+ *
+ * @see NumberFormat#getIntegerInstance()
+ */
+ public static IntegerEditing withDefaults() {
+ return withDefaults(null, null);
+ }
+
+ /**
+ * Creates a new editing object which defaults the validations and
+ * conversions used for editing based on the platform's locale. Uses the
+ * specified custom validation messages.
+ *
+ * @param parseErrorMessage
+ * The validation message issued in case the input string cannot
+ * be parsed to an integer.
+ * @param outOfRangeMessage
+ * The validation message issued in case the input string
+ * represents an integer whose value is out of range. Can be
+ * parameterized by the Integer.MIN_VALUE
and
+ * Integer.MAX_VALUE
values.
+ * @return The new editing object using the default validations and
+ * conversions for editing.
+ */
+ public static IntegerEditing withDefaults(String parseErrorMessage,
+ String outOfRangeMessage) {
+ return new IntegerEditing(NumberFormat.getIntegerInstance(),
+ parseErrorMessage, outOfRangeMessage);
+ }
+
+ /**
+ * Creates a new editing object whose validations and conversions used for
+ * editing are based on the given integer format. Uses the default
+ * validation messages.
+ *
+ * @param format
+ * The integer format defining the validations and conversions
+ * used for editing.
+ * @return The new editing object configured by the given integer format.
+ */
+ public static IntegerEditing forFormat(NumberFormat format) {
+ return forFormat(format, null, null);
+ }
+
+ /**
+ * Creates a new editing object whose validations and conversions used for
+ * editing are based on the given integer format. Uses the specified custom
+ * validation messages.
+ *
+ * @param format
+ * The integer format defining the validations and conversions
+ * used for editing.
+ * @param parseErrorMessage
+ * The validation message issued in case the input string cannot
+ * be parsed to an integer.
+ * @param outOfRangeMessage
+ * The validation message issued in case the input string
+ * represents an integer whose value is out of range. Can be
+ * parameterized by the Integer.MIN_VALUE
and
+ * Integer.MAX_VALUE
values.
+ * @return The new editing object configured by the given integer format.
+ */
+ public static IntegerEditing forFormat(NumberFormat format,
+ String parseErrorMessage, String outOfRangeMessage) {
+ return new IntegerEditing(format, parseErrorMessage, outOfRangeMessage);
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed integer is not
+ * null
. Uses the current {@link #requiredMessage(String)
+ * validation message}.
+ *
+ * @return This editing instance for method chaining.
+ */
+ public IntegerEditing required() {
+ addModelValidator(new NonNullValidator(requiredMessage));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the parsed integer is
+ * null
.
+ *
+ * @param message
+ * The validation message to be issued in case the parsed integer
+ * is null
.
+ * @return This editing instance for method chaining.
+ *
+ * @see #required()
+ */
+ public IntegerEditing requiredMessage(String message) {
+ this.requiredMessage = message;
+ return this;
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed integer is greater than
+ * the given number. Uses the current {@link #greaterMessage(String)
+ * validation message}.
+ *
+ * @param number
+ * The number which the parsed integer must be greater than.
+ * @return This editing instance for method chaining.
+ */
+ public IntegerEditing greater(int number) {
+ addModelValidator(IntegerRangeValidator.greater(number, greaterMessage,
+ format));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the parsed integer is
+ * not greater than a given number.
+ *
+ * @param message
+ * The validation message to be issued in case the parsed integer
+ * is not greater than a given number.
+ * @return This editing instance for method chaining.
+ *
+ * @see #greater(int)
+ */
+ public IntegerEditing greaterMessage(String message) {
+ this.greaterMessage = message;
+ return this;
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed integer is greater than
+ * or equal to the given number. Uses the current
+ * {@link #greaterEqualMessage(String) validation message}.
+ *
+ * @param number
+ * The number which the parsed integer must be greater than or
+ * equal to.
+ * @return This editing instance for method chaining.
+ */
+ public IntegerEditing greaterEqual(int number) {
+ addModelValidator(IntegerRangeValidator.greaterEqual(number,
+ greaterEqualMessage, format));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the parsed integer is
+ * not greater than or equal to a given number.
+ *
+ * @param message
+ * The validation message to be issued in case the parsed integer
+ * is not greater than or equal to a given number.
+ * @return This editing instance for method chaining.
+ *
+ * @see #greaterEqual(int)
+ */
+ public IntegerEditing greaterEqualMessage(String message) {
+ this.greaterEqualMessage = message;
+ return this;
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed integer is less than the
+ * given number. Uses the current {@link #lessMessage(String) validation
+ * message}.
+ *
+ * @param number
+ * The number which the parsed integer must be less than.
+ * @return This editing instance for method chaining.
+ */
+ public IntegerEditing less(int number) {
+ addModelValidator(IntegerRangeValidator.less(number, lessMessage,
+ format));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the parsed integer is
+ * not less than a given number.
+ *
+ * @param message
+ * The validation message to be issued in case the parsed integer
+ * is not less than a given number.
+ * @return This editing instance for method chaining.
+ *
+ * @see #less(int)
+ */
+ public IntegerEditing lessMessage(String message) {
+ this.lessMessage = message;
+ return this;
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed integer is less than or
+ * equal to the given number. Uses the current
+ * {@link #lessEqualMessage(String) validation message}.
+ *
+ * @param number
+ * The number which the parsed integer must be less than or equal
+ * to.
+ * @return This editing instance for method chaining.
+ */
+ public IntegerEditing lessEqual(int number) {
+ addModelValidator(IntegerRangeValidator.greaterEqual(number,
+ lessEqualMessage, format));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the parsed integer is
+ * not less than or equal to a given number.
+ *
+ * @param message
+ * The validation message to be issued in case the parsed integer
+ * is not less than or equal to a given number.
+ * @return This editing instance for method chaining.
+ *
+ * @see #lessEqual(int)
+ */
+ public IntegerEditing lessEqualMessage(String message) {
+ this.lessEqualMessage = message;
+ return this;
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed integer is within the
+ * given range. Uses the current {@link #rangeMessage(String) validation
+ * message}.
+ *
+ * @param min
+ * The lower bound of the range (inclusive).
+ * @param max
+ * The upper bound of the range (inclusive).
+ * @return This editing instance for method chaining.
+ */
+ public IntegerEditing range(int min, int max) {
+ addModelValidator(IntegerRangeValidator.range(min, max, rangeMessage,
+ format));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the parsed integer is
+ * not within a given range.
+ *
+ * @param message
+ * The validation message to be issued in case the parsed integer
+ * is not within a given range.
+ * @return This editing instance for method chaining.
+ *
+ * @see #range(int, int)
+ */
+ public IntegerEditing rangeMessage(String message) {
+ this.rangeMessage = message;
+ return this;
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed integer is positive. Uses
+ * the current {@link #positiveMessage(String) validation message}.
+ *
+ * @return This editing instance for method chaining.
+ */
+ public IntegerEditing positive() {
+ addModelValidator(IntegerRangeValidator.positive(positiveMessage,
+ format));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the parsed integer is
+ * not positive.
+ *
+ * @param message
+ * The validation message to be issued in case the parsed integer
+ * is not positive.
+ * @return This editing instance for method chaining.
+ *
+ * @see #positive()
+ */
+ public IntegerEditing positiveMessage(String message) {
+ this.positiveMessage = message;
+ return this;
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed integer is non-negative.
+ * Uses the current {@link #nonNegativeMessage(String) validation message}.
+ *
+ * @return This editing instance for method chaining.
+ */
+ public IntegerEditing nonNegative() {
+ addModelValidator(IntegerRangeValidator.nonNegative(nonNegativeMessage,
+ format));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the parsed integer is
+ * negative.
+ *
+ * @param message
+ * The validation message to be issued in case the parsed integer
+ * is negative.
+ * @return This editing instance for method chaining.
+ *
+ * @see #nonNegative()
+ */
+ public IntegerEditing nonNegativeMessage(String message) {
+ this.nonNegativeMessage = message;
+ return this;
+ }
+}
Index: src/org/eclipse/core/databinding/editing/Editing.java
===================================================================
RCS file: src/org/eclipse/core/databinding/editing/Editing.java
diff -N src/org/eclipse/core/databinding/editing/Editing.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/databinding/editing/Editing.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,466 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ovidio Mallo - initial API and implementation (bug 183055)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.editing;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.databinding.UpdateListStrategy;
+import org.eclipse.core.databinding.UpdateSetStrategy;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.internal.databinding.validation.CompositeValidator;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+
+/**
+ * @since 1.3
+ */
+public class Editing {
+
+ private final List targetValidators = new ArrayList();
+
+ private IConverter targetConverter;
+
+ private final List modelValidators = new ArrayList();
+
+ private final List beforeSetModelValidators = new ArrayList();
+
+ private IConverter modelConverter;
+
+ /**
+ * Adds a custom target validator.
+ *
+ * @param validator
+ * The custom target validator to add.
+ * @return This editing instance for method chaining.
+ */
+ public final Editing addTargetValidator(IValidator validator) {
+ targetValidators.add(validator);
+ return this;
+ }
+
+ /**
+ * Adds a custom model validator.
+ *
+ * @param validator
+ * The custom model validator to add.
+ * @return This editing instance for method chaining.
+ */
+ public final Editing addModelValidator(IValidator validator) {
+ modelValidators.add(validator);
+ return this;
+ }
+
+ /**
+ * Adds a custom before-set model validator.
+ *
+ * @param validator
+ * The custom before-set model validator to add.
+ * @return This editing instance for method chaining.
+ */
+ public final Editing addBeforeSetModelValidator(IValidator validator) {
+ beforeSetModelValidators.add(validator);
+ return this;
+ }
+
+ protected final void setTargetConverter(IConverter converter) {
+ this.targetConverter = converter;
+ }
+
+ protected final void setModelConverter(IConverter converter) {
+ this.modelConverter = converter;
+ }
+
+ /**
+ * Creates a new target-to-model {@link UpdateValueStrategy} with a default
+ * update policy configured by the current state of this editing object.
+ *
+ * @return A new target-to-model {@link UpdateValueStrategy} configured by
+ * the current state of this editing object.
+ *
+ * @see UpdateValueStrategy#UpdateValueStrategy()
+ */
+ public UpdateValueStrategy createTargetValueStrategy() {
+ return applyToTargetValueStrategy(new UpdateValueStrategy());
+ }
+
+ /**
+ * Creates a new target-to-model {@link UpdateValueStrategy} with the given
+ * update policy configured by the current state of this editing object.
+ *
+ * @param updatePolicy
+ * The update policy to use.
+ * @return A new target-to-model {@link UpdateValueStrategy} configured by
+ * the current state of this editing object.
+ *
+ * @see UpdateValueStrategy#UpdateValueStrategy(int)
+ */
+ public UpdateValueStrategy createTargetValueStrategy(int updatePolicy) {
+ return applyToTargetValueStrategy(new UpdateValueStrategy(updatePolicy));
+ }
+
+ /**
+ * Creates a new model-to-target {@link UpdateValueStrategy} with a default
+ * update policy configured by the current state of this editing object.
+ *
+ * @return A new model-to-target {@link UpdateValueStrategy} configured by
+ * the current state of this editing object.
+ *
+ * @see UpdateValueStrategy#UpdateValueStrategy()
+ */
+ public UpdateValueStrategy createModelValueStrategy() {
+ return applyToModelValueStrategy(new UpdateValueStrategy());
+ }
+
+ /**
+ * Creates a new model-to-target {@link UpdateValueStrategy} with the given
+ * update policy configured by the current state of this editing object.
+ *
+ * @param updatePolicy
+ * The update policy to use.
+ * @return A new model-to-target {@link UpdateValueStrategy} configured by
+ * the current state of this editing object.
+ *
+ * @see UpdateValueStrategy#UpdateValueStrategy(int)
+ */
+ public UpdateValueStrategy createModelValueStrategy(int updatePolicy) {
+ return applyToModelValueStrategy(new UpdateValueStrategy(updatePolicy));
+ }
+
+ /**
+ * Creates a new target-to-model {@link UpdateListStrategy} with a default
+ * update policy configured by the current state of this editing object.
+ *
+ * @return A new target-to-model {@link UpdateListStrategy} configured by
+ * the current state of this editing object.
+ *
+ * @see UpdateListStrategy#UpdateListStrategy()
+ */
+ public UpdateListStrategy createTargetListStrategy() {
+ return applyToTargetListStrategy(new UpdateListStrategy());
+ }
+
+ /**
+ * Creates a new target-to-model {@link UpdateListStrategy} with the given
+ * update policy configured by the current state of this editing object.
+ *
+ * @param updatePolicy
+ * The update policy to use.
+ * @return A new target-to-model {@link UpdateListStrategy} configured by
+ * the current state of this editing object.
+ *
+ * @see UpdateListStrategy#UpdateListStrategy(int)
+ */
+ public UpdateListStrategy createTargetListStrategy(int updatePolicy) {
+ return applyToTargetListStrategy(new UpdateListStrategy(updatePolicy));
+ }
+
+ /**
+ * Creates a new model-to-target {@link UpdateListStrategy} with a default
+ * update policy configured by the current state of this editing object.
+ *
+ * @return A new model-to-target {@link UpdateListStrategy} configured by
+ * the current state of this editing object.
+ *
+ * @see UpdateListStrategy#UpdateListStrategy()
+ */
+ public UpdateListStrategy createModelListStrategy() {
+ return applyToModelListStrategy(new UpdateListStrategy());
+ }
+
+ /**
+ * Creates a new model-to-target {@link UpdateListStrategy} with the given
+ * update policy configured by the current state of this editing object.
+ *
+ * @param updatePolicy
+ * The update policy to use.
+ * @return A new model-to-target {@link UpdateListStrategy} configured by
+ * the current state of this editing object.
+ *
+ * @see UpdateListStrategy#UpdateListStrategy(int)
+ */
+ public UpdateListStrategy createModelListStrategy(int updatePolicy) {
+ return applyToModelListStrategy(new UpdateListStrategy(updatePolicy));
+ }
+
+ /**
+ * Creates a new target-to-model {@link UpdateSetStrategy} with a default
+ * update policy configured by the current state of this editing object.
+ *
+ * @return A new target-to-model {@link UpdateSetStrategy} configured by the
+ * current state of this editing object.
+ *
+ * @see UpdateListStrategy#UpdateListStrategy()
+ */
+ public UpdateSetStrategy createTargetSetStrategy() {
+ return applyToTargetSetStrategy(new UpdateSetStrategy());
+ }
+
+ /**
+ * Creates a new target-to-model {@link UpdateListStrategy} with the given
+ * update policy configured by the current state of this editing object.
+ *
+ * @param updatePolicy
+ * The update policy to use.
+ * @return A new target-to-model {@link UpdateListStrategy} configured by
+ * the current state of this editing object.
+ *
+ * @see UpdateSetStrategy#UpdateSetStrategy(int)
+ */
+ public UpdateSetStrategy createTargetSetStrategy(int updatePolicy) {
+ return applyToTargetSetStrategy(new UpdateSetStrategy(updatePolicy));
+ }
+
+ /**
+ * Creates a new model-to-target {@link UpdateSetStrategy} with a default
+ * update policy configured by the current state of this editing object.
+ *
+ * @return A new model-to-target {@link UpdateSetStrategy} configured by the
+ * current state of this editing object.
+ *
+ * @see UpdateSetStrategy#UpdateSetStrategy()
+ */
+ public UpdateSetStrategy createModelSetStrategy() {
+ return applyToModelSetStrategy(new UpdateSetStrategy());
+ }
+
+ /**
+ * Creates a new model-to-target {@link UpdateSetStrategy} with the given
+ * update policy configured by the current state of this editing object.
+ *
+ * @param updatePolicy
+ * The update policy to use.
+ * @return A new model-to-target {@link UpdateSetStrategy} configured by the
+ * current state of this editing object.
+ *
+ * @see UpdateSetStrategy#UpdateSetStrategy(int)
+ */
+ public UpdateSetStrategy createModelSetStrategy(int updatePolicy) {
+ return applyToModelSetStrategy(new UpdateSetStrategy(updatePolicy));
+ }
+
+ /**
+ * Configures an existing target-to-model {@link UpdateValueStrategy} with
+ * the current state of this editing object.
+ *
+ * @param updateStrategy
+ * The {@link UpdateValueStrategy} to configure.
+ * @return The passed-in, configured target-to-model
+ * {@link UpdateValueStrategy}.
+ */
+ public UpdateValueStrategy applyToTargetValueStrategy(
+ UpdateValueStrategy updateStrategy) {
+ updateStrategy.setAfterGetValidator(createTargetValidator());
+ updateStrategy.setConverter(targetConverter);
+ updateStrategy.setAfterConvertValidator(createModelValidator());
+ updateStrategy.setBeforeSetValidator(createBeforeSetModelValidator());
+ return updateStrategy;
+ }
+
+ /**
+ * Configures an existing model-to-target {@link UpdateValueStrategy} with
+ * the current state of this editing object.
+ *
+ * @param updateStrategy
+ * The {@link UpdateValueStrategy} to configure.
+ * @return The passed-in, configured model-to-target
+ * {@link UpdateValueStrategy}.
+ */
+ public UpdateValueStrategy applyToModelValueStrategy(
+ UpdateValueStrategy updateStrategy) {
+ updateStrategy.setConverter(modelConverter);
+ return updateStrategy;
+ }
+
+ /**
+ * Configures an existing target-to-model {@link UpdateListStrategy} with
+ * the current state of this editing object.
+ *
+ * @param updateStrategy
+ * The {@link UpdateListStrategy} to configure.
+ * @return The passed-in, configured target-to-model
+ * {@link UpdateListStrategy}.
+ */
+ public UpdateListStrategy applyToTargetListStrategy(
+ UpdateListStrategy updateStrategy) {
+ updateStrategy.setConverter(targetConverter);
+ return updateStrategy;
+ }
+
+ /**
+ * Configures an existing model-to-target {@link UpdateListStrategy} with
+ * the current state of this editing object.
+ *
+ * @param updateStrategy
+ * The {@link UpdateListStrategy} to configure.
+ * @return The passed-in, configured model-to-target
+ * {@link UpdateListStrategy}.
+ */
+ public UpdateListStrategy applyToModelListStrategy(
+ UpdateListStrategy updateStrategy) {
+ updateStrategy.setConverter(modelConverter);
+ return updateStrategy;
+ }
+
+ /**
+ * Configures an existing target-to-model {@link UpdateListStrategy} with
+ * the current state of this editing object.
+ *
+ * @param updateStrategy
+ * The {@link UpdateSetStrategy} to configure.
+ * @return The passed-in, configured target-to-model
+ * {@link UpdateSetStrategy}.
+ */
+ public UpdateSetStrategy applyToTargetSetStrategy(
+ UpdateSetStrategy updateStrategy) {
+ updateStrategy.setConverter(targetConverter);
+ return updateStrategy;
+ }
+
+ /**
+ * Configures an existing model-to-target {@link UpdateSetStrategy} with the
+ * current state of this editing object.
+ *
+ * @param updateStrategy
+ * The {@link UpdateSetStrategy} to configure.
+ * @return The passed-in, configured model-to-target
+ * {@link UpdateSetStrategy}.
+ */
+ public UpdateSetStrategy applyToModelSetStrategy(
+ UpdateSetStrategy updateStrategy) {
+ updateStrategy.setConverter(modelConverter);
+ return updateStrategy;
+ }
+
+ /**
+ * Converts a target value to a model value by performing the following
+ * steps:
+ *
+ * The conversion process will be aborted by returning null
+ * whenever any of the applied validators produces a {@link IStatus
+ * validation status} having {@link IStatus#getSeverity() severity}
+ * IStatus.ERROR
or IStatus.CANCEL
. During the
+ * conversion process, any validation status whose severity is different
+ * from IStatus.OK
will be {@link MultiStatus#add(IStatus)
+ * aggregated} on the given validationStatus
.
+ *
null
in case the
+ * conversion has been aborted due to a validation error.
+ */
+ public final Object convertToModel(Object targetValue,
+ MultiStatus validationStatus) {
+ // TODO: Cache the target/model validators.
+ IValidator targetValidator = createTargetValidator();
+ if (!applyValidator(targetValidator, targetValue, validationStatus)) {
+ return null;
+ }
+
+ Object modelValue = (targetConverter != null) ? targetConverter
+ .convert(targetValue) : targetValue;
+
+ IValidator modelValidator = createModelValidator();
+ if (!applyValidator(modelValidator, modelValue, validationStatus)) {
+ return null;
+ }
+
+ IValidator beforeSetModelValidator = createBeforeSetModelValidator();
+ if (!applyValidator(beforeSetModelValidator, modelValue,
+ validationStatus)) {
+ return null;
+ }
+
+ return modelValue;
+ }
+
+ /**
+ * {@link #setModelConverter(IConverter) Converts} a model value to a target
+ * value.
+ *
+ * @param modelValue
+ * The target value to be converted to a model value.
+ * @return The converted target value.
+ */
+ public final Object convertToTarget(Object modelValue) {
+ return (modelConverter != null) ? modelConverter.convert(modelValue)
+ : modelValue;
+ }
+
+ private IValidator createTargetValidator() {
+ if (!targetValidators.isEmpty()) {
+ CompositeValidator targetValidator = new CompositeValidator(
+ (IValidator[]) targetValidators
+ .toArray(new IValidator[targetValidators.size()]));
+ return targetValidator;
+ }
+ return null;
+ }
+
+ private IValidator createModelValidator() {
+ if (!modelValidators.isEmpty()) {
+ return new CompositeValidator((IValidator[]) modelValidators
+ .toArray(new IValidator[modelValidators.size()]));
+ }
+ return null;
+ }
+
+ private IValidator createBeforeSetModelValidator() {
+ if (!beforeSetModelValidators.isEmpty()) {
+ return new CompositeValidator(
+ (IValidator[]) beforeSetModelValidators
+ .toArray(new IValidator[beforeSetModelValidators
+ .size()]));
+ }
+ return null;
+ }
+
+ private static boolean applyValidator(IValidator validator, Object value,
+ MultiStatus aggregateStatus) {
+ if (validator != null) {
+ IStatus validationStatus = validator.validate(value);
+ if (!validationStatus.isOK()) {
+ aggregateStatus.add(validationStatus);
+ }
+ return isValid(validationStatus);
+ }
+ return true;
+ }
+
+ private static boolean isValid(IStatus status) {
+ return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING);
+ }
+}
Index: src/org/eclipse/core/databinding/editing/DateEditing.java
===================================================================
RCS file: src/org/eclipse/core/databinding/editing/DateEditing.java
diff -N src/org/eclipse/core/databinding/editing/DateEditing.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/databinding/editing/DateEditing.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,279 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ovidio Mallo - initial API and implementation (bug 183055)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.editing;
+
+import java.util.Date;
+
+import org.eclipse.core.internal.databinding.conversion.DateToStringConverter;
+import org.eclipse.core.internal.databinding.conversion.StringToDateConverter;
+import org.eclipse.core.internal.databinding.validation.DateRangeValidator;
+import org.eclipse.core.internal.databinding.validation.NonNullValidator;
+import org.eclipse.core.internal.databinding.validation.StringToDateValidator;
+
+import com.ibm.icu.text.DateFormat;
+
+/**
+ * @since 1.3
+ */
+public final class DateEditing extends Editing {
+
+ private final DateFormat displayFormat;
+
+ private String requiredMessage = null;
+
+ private String afterMessage = null;
+
+ private String afterEqualMessage = null;
+
+ private String beforeMessage = null;
+
+ private String beforeEqualMessage = null;
+
+ private DateEditing(DateFormat[] inputFormats, String parseErrorMessage,
+ DateFormat displayFormat) {
+ this.displayFormat = displayFormat;
+
+ StringToDateConverter targetConverter;
+ if (inputFormats == null) {
+ targetConverter = new StringToDateConverter();
+ } else {
+ targetConverter = new StringToDateConverter(inputFormats);
+ }
+
+ addTargetValidator(new StringToDateValidator(targetConverter,
+ parseErrorMessage));
+ setTargetConverter(targetConverter);
+ setModelConverter(displayFormat == null ? new DateToStringConverter()
+ : new DateToStringConverter(displayFormat));
+ }
+
+ /**
+ * Creates a new editing object which defaults the validations and
+ * conversions used for editing. Uses the default validation message.
+ *
+ * @return The new editing object using the default validations and
+ * conversions for editing.
+ */
+ public static DateEditing withDefaults() {
+ return withDefaults(null);
+ }
+
+ /**
+ * Creates a new editing object which defaults the validations and
+ * conversions used for editing. Uses the specified custom validation
+ * message.
+ *
+ * @param parseErrorMessage
+ * The validation message issued in case the input string cannot
+ * be parsed to a date.
+ * @return The new editing object using the default validations and
+ * conversions for editing.
+ */
+ public static DateEditing withDefaults(String parseErrorMessage) {
+ return new DateEditing(null, parseErrorMessage, null);
+ }
+
+ /**
+ * Creates a new editing object which supports all the given date input
+ * formats and which uses the specified format for displaying a date. Uses
+ * the default validation message.
+ *
+ * @param inputFormats
+ * The date formats supported for reading in dates.
+ * @param displayFormat
+ * The date format for displaying dates.
+ * @return The new editing object configured by the given date formats.
+ */
+ public static DateEditing forFormats(DateFormat[] inputFormats,
+ DateFormat displayFormat) {
+ return new DateEditing(inputFormats, null, displayFormat);
+ }
+
+ /**
+ * Creates a new editing object which supports all the given date input
+ * formats and which uses the specified format for displaying a date. Uses
+ * the specified custom validation message.
+ *
+ * @param inputFormats
+ * The date formats supported for reading in dates.
+ * @param parseErrorMessage
+ * The validation message issued in case the input string cannot
+ * be parsed to a date.
+ * @param displayFormat
+ * The date format for displaying dates.
+ * @return The new editing object configured by the given date formats.
+ */
+ public static DateEditing forFormats(DateFormat[] inputFormats,
+ String parseErrorMessage, DateFormat displayFormat) {
+ return new DateEditing(inputFormats, parseErrorMessage, displayFormat);
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed date is not
+ * null
. Uses the current {@link #requiredMessage(String)
+ * validation message}.
+ *
+ * @return This editing instance for method chaining.
+ */
+ public DateEditing required() {
+ addModelValidator(new NonNullValidator(requiredMessage));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the parsed date is
+ * null
.
+ *
+ * @param message
+ * The validation message to be issued in case the parsed date is
+ * null
.
+ * @return This editing instance for method chaining.
+ *
+ * @see #required()
+ */
+ public DateEditing requiredMessage(String message) {
+ this.requiredMessage = message;
+ return this;
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed date is after the given
+ * date. Uses the current {@link #afterMessage(String) validation message}.
+ *
+ * @param date
+ * The date which must be before or on the parsed date.
+ * @return This editing instance for method chaining.
+ *
+ * @see Date#after(Date)
+ */
+ public DateEditing after(Date date) {
+ addModelValidator(DateRangeValidator.after(date, afterMessage,
+ displayFormat));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the parsed date is not
+ * after a given date.
+ *
+ * @param message
+ * The validation message to be issued in case the parsed date is
+ * not after a given date.
+ * @return This editing instance for method chaining.
+ *
+ * @see #after(Date)
+ */
+ public DateEditing afterMessage(String message) {
+ this.afterMessage = message;
+ return this;
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed date is after or on the
+ * given date. Uses the current {@link #afterEqualMessage(String) validation
+ * message}.
+ *
+ * @param date
+ * The date which must be before the parsed date.
+ * @return This editing instance for method chaining.
+ *
+ * @see Date#after(Date)
+ * @see Date#equals(Object)
+ */
+ public DateEditing afterEqual(Date date) {
+ addModelValidator(DateRangeValidator.afterEqual(date,
+ afterEqualMessage, displayFormat));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the parsed date is not
+ * after or on a given date.
+ *
+ * @param message
+ * The validation message to be issued in case the parsed date is
+ * not after or on a given date.
+ * @return This editing instance for method chaining.
+ *
+ * @see #afterEqual(Date)
+ */
+ public DateEditing afterEqualMessage(String message) {
+ this.afterEqualMessage = message;
+ return this;
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed date is before the given
+ * date. Uses the current {@link #beforeMessage(String) validation message}.
+ *
+ * @param date
+ * The date which must be after or on the parsed date.
+ * @return This editing instance for method chaining.
+ *
+ * @see Date#before(Date)
+ */
+ public DateEditing before(Date date) {
+ addModelValidator(DateRangeValidator.before(date, beforeMessage,
+ displayFormat));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the parsed date is not
+ * before a given date.
+ *
+ * @param message
+ * The validation message to be issued in case the parsed date is
+ * not before a given date.
+ * @return This editing instance for method chaining.
+ *
+ * @see #before(Date)
+ */
+ public DateEditing beforeMessage(String message) {
+ this.beforeMessage = message;
+ return this;
+ }
+
+ /**
+ * Adds a model validator ensuring that the parsed date is before or on the
+ * given date. Uses the current {@link #beforeEqualMessage(String)
+ * validation message}.
+ *
+ * @param date
+ * The date which must be after the parsed date.
+ * @return This editing instance for method chaining.
+ *
+ * @see Date#before(Date)
+ * @see Date#equals(Object)
+ */
+ public DateEditing beforeEqual(Date date) {
+ addModelValidator(DateRangeValidator.beforeEqual(date,
+ beforeEqualMessage, displayFormat));
+ return this;
+ }
+
+ /**
+ * Sets the validation message to be issued in case the parsed date is not
+ * before or on a given date.
+ *
+ * @param message
+ * The validation message to be issued in case the parsed date is
+ * not before or on a given date.
+ * @return This editing instance for method chaining.
+ *
+ * @see #beforeEqual(Date)
+ */
+ public DateEditing beforeEqualMessage(String message) {
+ this.beforeEqualMessage = message;
+ return this;
+ }
+}
Index: src/org/eclipse/core/internal/databinding/conversion/StringStripConverter.java
===================================================================
RCS file: src/org/eclipse/core/internal/databinding/conversion/StringStripConverter.java
diff -N src/org/eclipse/core/internal/databinding/conversion/StringStripConverter.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/internal/databinding/conversion/StringStripConverter.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ovidio Mallo - initial API and implementation (bug 183055)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+import org.eclipse.core.databinding.conversion.Converter;
+
+/**
+ * @since 1.3
+ */
+public class StringStripConverter extends Converter {
+
+ private final boolean stripToNull;
+
+ /**
+ *
+ * @param stripToNull
+ */
+ public StringStripConverter(boolean stripToNull) {
+ super(String.class, String.class);
+ this.stripToNull = stripToNull;
+ }
+
+ public Object convert(Object fromObject) {
+ String string = (String) fromObject;
+
+ if (string != null && string.length() != 0) {
+ int stripStart = 0;
+ while (stripStart < string.length()
+ && Character.isWhitespace(string.charAt(stripStart))) {
+ stripStart++;
+ }
+
+ int stripEnd = string.length() - 1;
+ while (stripEnd >= 0
+ && Character.isWhitespace(string.charAt(stripEnd))) {
+ stripEnd--;
+ }
+
+ if (stripStart <= stripEnd) {
+ string = string.substring(stripStart, stripEnd + 1);
+ } else {
+ string = null;
+ }
+ }
+
+ if (string == null || string.length() == 0) {
+ string = stripToNull ? null : ""; //$NON-NLS-1$
+ }
+
+ return string;
+ }
+}
Index: src/org/eclipse/core/internal/databinding/validation/NumberRangeValidator.java
===================================================================
RCS file: src/org/eclipse/core/internal/databinding/validation/NumberRangeValidator.java
diff -N src/org/eclipse/core/internal/databinding/validation/NumberRangeValidator.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/internal/databinding/validation/NumberRangeValidator.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ovidio Mallo - initial API and implementation (bug 183055)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+import java.text.MessageFormat;
+
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.3
+ */
+public abstract class NumberRangeValidator implements IValidator {
+
+ protected static final int UNDEFINED = -1;
+ protected static final int GREATER = 0;
+ protected static final int GREATER_EQUAL = 1;
+ protected static final int LESS = 2;
+ protected static final int LESS_EQUAL = 3;
+
+ // TODO: Externalize
+ protected static final String GREATER_MESSAGE = "The value must be greater than {0}."; //$NON-NLS-1$
+ protected static final String GREATER_EQUAL_MESSAGE = "The value must be greater than or equal to {0}."; //$NON-NLS-1$
+ protected static final String LESS_MESSAGE = "The value must be less than {0}."; //$NON-NLS-1$
+ protected static final String LESS_EQUAL_MESSAGE = "The value must be less than or equal to {0}."; //$NON-NLS-1$
+ protected static final String WITHIN_RANGE_MESSAGE = "The value must lie between {0} and {1}."; //$NON-NLS-1$
+ protected static final String POSITIVE_MESSAGE = "The value must be positive."; //$NON-NLS-1$
+ protected static final String NON_NEGATIVE_MESSAGE = "The value must not be negative."; //$NON-NLS-1$
+
+ private final Number min;
+ private final Number max;
+ private final int minConstraint;
+ private final int maxConstraint;
+ private final String validationMessage;
+ private final NumberFormat format;
+
+ /**
+ *
+ * @param min
+ * @param max
+ * @param minConstraint
+ * @param maxConstraint
+ * @param validationMessage
+ * @param format
+ */
+ protected NumberRangeValidator(Number min, Number max, int minConstraint,
+ int maxConstraint, String validationMessage, NumberFormat format) {
+ this.min = min;
+ this.max = max;
+ this.minConstraint = minConstraint;
+ this.maxConstraint = maxConstraint;
+ this.validationMessage = validationMessage;
+ this.format = format;
+ }
+
+ public IStatus validate(Object value) {
+ if (value != null) {
+ Number number = (Number) value;
+ if ((min != null && !fulfillsConstraint(number, min, minConstraint))
+ || (max != null && !fulfillsConstraint(number, max,
+ maxConstraint))) {
+ // TODO: Cache message?
+ return ValidationStatus.error(MessageFormat.format(
+ validationMessage, getFormattedRangeExtremas()));
+ }
+ }
+ return ValidationStatus.ok();
+ }
+
+ private boolean fulfillsConstraint(Number number1, Number number2,
+ int constraint) {
+ int compareResult = compare(number1, number2);
+ switch (constraint) {
+ case GREATER:
+ return compareResult > 0;
+ case GREATER_EQUAL:
+ return compareResult >= 0;
+ case LESS:
+ return compareResult < 0;
+ case LESS_EQUAL:
+ return compareResult <= 0;
+ case UNDEFINED:
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported constraint: " + constraint); //$NON-NLS-1$
+ }
+ }
+
+ protected abstract int compare(Number number1, Number number2);
+
+ private String[] getFormattedRangeExtremas() {
+ // TODO: Synchronize formatting to make it thread safe?
+ if (min == null) {
+ return new String[] { format.format(max) };
+ } else if (max == null) {
+ return new String[] { format.format(min) };
+ }
+ return new String[] { format.format(min), format.format(max) };
+ }
+}
#P org.eclipse.jface.examples.databinding
Index: META-INF/MANIFEST.MF
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jface.examples.databinding/META-INF/MANIFEST.MF,v
retrieving revision 1.21
diff -u -r1.21 MANIFEST.MF
--- META-INF/MANIFEST.MF 24 Aug 2009 21:33:42 -0000 1.21
+++ META-INF/MANIFEST.MF 29 Aug 2009 12:45:57 -0000
@@ -17,5 +17,6 @@
org.eclipse.jface.examples.databinding.mask.internal;x-internal:=true,
org.eclipse.jface.examples.databinding.model;x-internal:=false,
org.eclipse.jface.examples.databinding.radioGroup;x-internal:=false
-Import-Package: com.ibm.icu.text
+Import-Package: com.ibm.icu.math,
+ com.ibm.icu.text
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Index: src/org/eclipse/jface/examples/databinding/snippets/Snippet035Editing.java
===================================================================
RCS file: src/org/eclipse/jface/examples/databinding/snippets/Snippet035Editing.java
diff -N src/org/eclipse/jface/examples/databinding/snippets/Snippet035Editing.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/jface/examples/databinding/snippets/Snippet035Editing.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,218 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.editing.Editing;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
+import org.eclipse.jface.examples.databinding.util.EditingFactory;
+import org.eclipse.jface.internal.databinding.provisional.fieldassist.ControlDecorationSupport;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class Snippet035Editing {
+
+ private static final String EMAIL_REGEX = "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}\\b";
+
+ private DataBindingContext dbc;
+
+ public static void main(String[] args) {
+ Display display = new Display();
+
+ Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+ public void run() {
+ Shell shell = new Snippet035Editing().createShell();
+ Display display = Display.getCurrent();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ }
+ });
+ }
+
+ private Shell createShell() {
+ Display display = Display.getCurrent();
+ Shell shell = new Shell(display);
+ shell.setText("Editing");
+ shell.setLayout(new GridLayout(1, false));
+
+ dbc = new DataBindingContext();
+
+ createValueSection(shell);
+ createListSection(shell);
+
+ shell.pack();
+ shell.open();
+
+ return shell;
+ }
+
+ private void createValueSection(Composite parent) {
+ Group section = createSectionGroup(parent, "Value bindings", false);
+
+ // Edit an e-mail address.
+ Text emailText = createTextField(section, "E-Mail*");
+ Editing emailEditing = EditingFactory.forString().required().matches(EMAIL_REGEX);
+ bindTextField(emailText, new WritableValue(), emailEditing);
+
+ // Edit a required, positive integer and use the default validation message.
+ Text positiveText = createTextField(section, "Positive value*");
+ Editing positiveEditing = EditingFactory.forInteger().required().positive();
+ bindTextField(positiveText, new WritableValue(), positiveEditing);
+
+ // Edit an integer within the range [1, 100] and use the default
+ // validation message.
+ Text rangeText = createTextField(section, "Value in [1, 100]");
+ Editing rangeEditing = EditingFactory.forInteger().range(1, 100);
+ bindTextField(rangeText, new WritableValue(new Integer(0), null), rangeEditing);
+
+ // Edit a percentage value which must lie within [0, 100] and use a custom
+ // validation message which indicates that the value actually represents
+ // a percentage value.
+ Text percentText = createTextField(section, "Value [%]");
+ Editing percentEditing = EditingFactory.forInteger().rangeMessage("Please specify a valid percentage value within [{0}, {1}].").range(0, 100);
+ bindTextField(percentText, new WritableValue(new Integer(-1), null), percentEditing);
+
+ // Edit a hex integer within the range [0x00, 0xff]. The range validation
+ // message will display the range boundaries in hex format as well.
+ Text hexText = createTextField(section, "Hex value in [0x00, 0xff]");
+ Editing hexEditing = EditingFactory.forHexInteger(2).range(0x00, 0xff);
+ bindTextField(hexText, new WritableValue(new Integer(0), null), hexEditing);
+ }
+
+ private void createListSection(Composite parent) {
+ Group section = createSectionGroup(parent, "List bindings", true);
+
+ // Our date should be >= 01.01.1990.
+ Calendar year1990Calendar = Calendar.getInstance();
+ year1990Calendar.clear();
+ year1990Calendar.set(1990, 0, 1);
+ Date year1990 = year1990Calendar.getTime();
+
+ // Edit a date supporting the default input/display patterns.
+ Text dateText = createTextField(section, "Date");
+ Editing dateEditing = EditingFactory.forDate().afterEqual(year1990);
+ final WritableValue dateObservable = new WritableValue();
+ final Binding dateBinding = bindTextField(dateText, dateObservable, dateEditing);
+
+ // Create a list to which the dates are added when the user hits ENTER.
+ new Label(section, SWT.LEFT);
+ ListViewer dateListViewer = new ListViewer(section);
+ GridDataFactory.fillDefaults().grab(true, true).hint(150, 200).applyTo(dateListViewer.getList());
+
+ dateListViewer.setContentProvider(new ObservableListContentProvider());
+ dateListViewer.setLabelProvider(new LabelProvider());
+
+ // We use the same DateEditing object as for the text field above to
+ // create a list binding which maps the entered dates to their string
+ // representation which is then set as input on the ListViewer.
+ final WritableList targetDateList = new WritableList();
+ final WritableList modelDateList = new WritableList();
+ dbc.bindList(
+ targetDateList,
+ modelDateList,
+ dateEditing.createTargetListStrategy(),
+ dateEditing.createModelListStrategy());
+
+ // Set the list containing the string representation of the dates as input.
+ dateListViewer.setInput(targetDateList);
+
+ // Add the current date in the text field when the user hits ENTER.
+ dateText.addSelectionListener(new SelectionAdapter() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ IStatus dateValidationStatus = (IStatus) dateBinding.getValidationStatus().getValue();
+ Date date = (Date) dateObservable.getValue();
+ if (dateValidationStatus.isOK() && date != null) {
+ modelDateList.add(date);
+ }
+ }
+ });
+ }
+
+ private Binding bindTextField(Text text, IObservableValue modelValue, Editing editing) {
+ // Create the binding using the editing object.
+ ISWTObservableValue textObservable = SWTObservables.observeText(text, SWT.Modify);
+ Binding binding = dbc.bindValue(
+ textObservable,
+ modelValue,
+ editing.createTargetValueStrategy(),
+ editing.createModelValueStrategy());
+
+ // Decorate the control with the validation status.
+ ControlDecorationSupport.create(binding, SWT.TOP);
+
+ // Re-format when the text field looses the focus in order to always
+ // display the model in the default format in case multiple input formats
+ // are supported.
+ formatOnFocusOut(text, binding);
+
+ return binding;
+ }
+
+ private static void formatOnFocusOut(final Control control, final Binding binding) {
+ control.addFocusListener(new FocusAdapter() {
+ public void focusLost(FocusEvent e) {
+ IStatus dateValidationStatus = (IStatus) binding.getValidationStatus().getValue();
+ if (dateValidationStatus.isOK()) {
+ binding.updateModelToTarget();
+ }
+ }
+ });
+ }
+
+ private static Group createSectionGroup(Composite parent, String groupText, boolean grabVertical) {
+ Group section = new Group(parent, SWT.SHADOW_ETCHED_IN);
+ section.setText(groupText);
+ GridLayoutFactory.fillDefaults().numColumns(2).equalWidth(false).margins(5, 5).spacing(15, 5).applyTo(section);
+ GridDataFactory.fillDefaults().grab(true, grabVertical).applyTo(section);
+ return section;
+ }
+
+ private static Text createTextField(Composite parent, String labelText) {
+ Label label = new Label(parent, SWT.LEFT);
+ label.setText(labelText);
+ GridDataFactory.fillDefaults().align(SWT.LEFT, SWT.CENTER).applyTo(label);
+
+ Text text = new Text(parent, SWT.BORDER);
+ GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).applyTo(text);
+
+ return text;
+ }
+}
Index: src/org/eclipse/jface/examples/databinding/util/EditingFactory.java
===================================================================
RCS file: src/org/eclipse/jface/examples/databinding/util/EditingFactory.java
diff -N src/org/eclipse/jface/examples/databinding/util/EditingFactory.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/jface/examples/databinding/util/EditingFactory.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.util;
+
+import org.eclipse.core.databinding.editing.DateEditing;
+import org.eclipse.core.databinding.editing.IntegerEditing;
+import org.eclipse.core.databinding.editing.StringEditing;
+
+import com.ibm.icu.text.DateFormat;
+import com.ibm.icu.text.SimpleDateFormat;
+
+/**
+ * @since 3.2
+ */
+public final class EditingFactory {
+
+ private static final String REQUIRED_MESSAGE = "Please specify a value.";
+
+ private static final String INTEGER_PARSE_ERROR_MESSAGE = "The input is invalid.";
+
+ private static final String INTEGER_OUT_OF_RANGE_MESSAGE = "The value lies outside the supported range.";
+
+ private static final String INTEGER_RANGE_MESSAGE = "The value must lie between {0} and {1}.";
+
+ private static final String[] DATE_INPUT_PATTERNS = new String[] {
+ "dd.MM.yy",
+ "dd/MM/yy",
+ "dd.MM.yyyy",
+ "dd/MM/yyyy"
+ };
+
+ private static final String DATE_DISPLAY_PATTERN = "dd.MM.yyyy";
+
+ private static final String DATE_PARSE_ERROR_MESSAGE = createDateParseErrorMessage(DATE_INPUT_PATTERNS);
+
+ private EditingFactory() {
+ // prevent instantiation
+ }
+
+ public static StringEditing forString() {
+ return StringEditing.stripped(true);
+ }
+
+ public static IntegerEditing forInteger() {
+ return IntegerEditing
+ .withDefaults()
+ .requiredMessage(REQUIRED_MESSAGE)
+ .rangeMessage(INTEGER_RANGE_MESSAGE);
+ }
+
+ public static IntegerEditing forHexInteger(int digits) {
+ return IntegerEditing
+ .forFormat(RadixNumberFormat.getHexInstance("0x", digits), INTEGER_PARSE_ERROR_MESSAGE, INTEGER_OUT_OF_RANGE_MESSAGE)
+ .requiredMessage(REQUIRED_MESSAGE)
+ .rangeMessage(INTEGER_RANGE_MESSAGE);
+ }
+
+ public static DateEditing forDate() {
+ return DateEditing
+ .forFormats(
+ createDateFormats(DATE_INPUT_PATTERNS),
+ DATE_PARSE_ERROR_MESSAGE,
+ new SimpleDateFormat(DATE_DISPLAY_PATTERN))
+ .requiredMessage(REQUIRED_MESSAGE);
+ }
+
+ private static DateFormat[] createDateFormats(String[] datePatterns) {
+ DateFormat[] dateFormats = new DateFormat[datePatterns.length];
+ for (int i = 0; i < dateFormats.length; i++) {
+ dateFormats[i] = new SimpleDateFormat(datePatterns[i]);
+ }
+ return dateFormats;
+ }
+
+ private static String createDateParseErrorMessage(String[] datePatterns) {
+ StringBuffer messageSb = new StringBuffer();
+ messageSb.append("Supported formats: ");
+ for (int i = 0; i < datePatterns.length; i++) {
+ if (i > 0) {
+ messageSb.append(", ");
+ }
+ messageSb.append(datePatterns[i]);
+ }
+ return messageSb.toString();
+ }
+}
Index: src/org/eclipse/jface/examples/databinding/util/ObservableMapEditingSupport.java
===================================================================
RCS file: src/org/eclipse/jface/examples/databinding/util/ObservableMapEditingSupport.java
diff -N src/org/eclipse/jface/examples/databinding/util/ObservableMapEditingSupport.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/jface/examples/databinding/util/ObservableMapEditingSupport.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.util;
+
+import org.eclipse.core.databinding.editing.Editing;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.EditingSupport;
+import org.eclipse.jface.viewers.TextCellEditor;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * @since 3.2
+ */
+public class ObservableMapEditingSupport extends EditingSupport {
+
+ private final IObservableMap attributeMap;
+
+ private final Editing editing;
+
+ private final CellEditor cellEditor;
+
+ public ObservableMapEditingSupport(ColumnViewer viewer, IObservableMap attributeMap, Editing editing) {
+ super(viewer);
+ this.attributeMap = attributeMap;
+ this.editing = editing;
+ this.cellEditor = new TextCellEditor((Composite) getViewer().getControl());
+ }
+
+ protected boolean canEdit(Object element) {
+ return true;
+ }
+
+ protected CellEditor getCellEditor(Object element) {
+ return cellEditor;
+ }
+
+ protected Object getValue(Object element) {
+ return editing.convertToTarget(attributeMap.get(element));
+ }
+
+ protected void setValue(Object element, Object value) {
+ MultiStatus validationStatus = new MultiStatus(Policy.JFACE_DATABINDING, 0, null, null);
+ Object modelValue = editing.convertToModel(value, validationStatus);
+ if (handleValidation(validationStatus)) {
+ attributeMap.put(element, modelValue);
+ }
+ }
+
+ private boolean handleValidation(IStatus validationStatus) {
+ if (validationStatus.matches(IStatus.ERROR | IStatus.CANCEL)) {
+ MessageDialog.openError(getViewer().getControl().getShell(), "Validation Error", getValidationMessage(validationStatus));
+ return false;
+ }
+ return true;
+ }
+
+ private static String getValidationMessage(IStatus validationStatus) {
+ if (!validationStatus.isMultiStatus()) {
+ return validationStatus.getMessage();
+ }
+
+ MultiStatus multiStatus = (MultiStatus) validationStatus;
+ StringBuffer sb = new StringBuffer();
+ IStatus[] childStatus = multiStatus.getChildren();
+ for (int i = 0; i < childStatus.length; i++) {
+ if (i > 0) {
+ sb.append('\n');
+ }
+ sb.append(getValidationMessage(childStatus[i]));
+ }
+ return sb.toString();
+ }
+}
Index: src/org/eclipse/jface/examples/databinding/snippets/Snippet036EditingTable.java
===================================================================
RCS file: src/org/eclipse/jface/examples/databinding/snippets/Snippet036EditingTable.java
diff -N src/org/eclipse/jface/examples/databinding/snippets/Snippet036EditingTable.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/jface/examples/databinding/snippets/Snippet036EditingTable.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,246 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.util.Date;
+
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.editing.Editing;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.examples.databinding.ModelObject;
+import org.eclipse.jface.examples.databinding.util.EditingFactory;
+import org.eclipse.jface.examples.databinding.util.ObservableMapEditingCellLabelProvider;
+import org.eclipse.jface.examples.databinding.util.ObservableMapEditingSupport;
+import org.eclipse.jface.internal.databinding.provisional.fieldassist.ControlDecorationSupport;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class Snippet036EditingTable {
+
+ private final Editing nameEditing = EditingFactory.forString().required();
+
+ private final Editing ageEditing = EditingFactory.forInteger().required().nonNegative();
+
+ private final Editing bdayEditing = EditingFactory.forDate().before(new Date());
+
+ private DataBindingContext dbc;
+
+ private TableViewer tableViewer;
+
+ public static void main(String[] args) {
+ Display display = new Display();
+
+ Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+ public void run() {
+ Shell shell = new Snippet036EditingTable().createShell();
+ Display display = Display.getCurrent();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ }
+ });
+ }
+
+ private Shell createShell() {
+ Display display = Display.getCurrent();
+ Shell shell = new Shell(display);
+ shell.setText("Editing");
+ shell.setLayout(new GridLayout(2, false));
+
+ dbc = new DataBindingContext();
+
+ createTableSection(shell);
+ createFieldSection(shell);
+
+ shell.pack();
+ shell.open();
+
+ return shell;
+ }
+
+ private void createTableSection(Composite parent) {
+ Group section = createSectionGroup(parent, 1);
+
+ tableViewer = new TableViewer(section, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION);
+ GridDataFactory.fillDefaults().grab(true, true).hint(350, 250).applyTo(tableViewer.getTable());
+ tableViewer.getTable().setHeaderVisible(true);
+ tableViewer.getTable().setLinesVisible(true);
+
+ ObservableListContentProvider contentProvider = new ObservableListContentProvider();
+ tableViewer.setContentProvider(contentProvider);
+ IObservableSet contentElements = contentProvider.getKnownElements();
+
+ IObservableMap nameMap = BeansObservables.observeMap(contentElements, "name");
+ createColumn("Name*", 150, nameMap, nameEditing);
+
+ IObservableMap ageMap = BeansObservables.observeMap(contentElements, "age");
+ createColumn("Age*", 50, ageMap, ageEditing);
+
+ IObservableMap bdayMap = BeansObservables.observeMap(contentElements, "birthday");
+ createColumn("Birthday", 80, bdayMap, bdayEditing);
+
+ WritableList people = new WritableList();
+ people.add(new Person("John Doe", 27));
+ people.add(new Person("Steve Northover", 33));
+ people.add(new Person("Grant Gayed", 54));
+ people.add(new Person("Veronika Irvine", 25));
+ people.add(new Person("Mike Wilson", 44));
+ people.add(new Person("Christophe Cornu", 37));
+ people.add(new Person("Lynne Kues", 65));
+ people.add(new Person("Silenio Quarti", 15));
+
+ tableViewer.setInput(people);
+
+ tableViewer.setSelection(new StructuredSelection(people.get(0)));
+ }
+
+ private TableViewerColumn createColumn(String text, int width, IObservableMap attributeMap, Editing modelEditing) {
+ TableViewerColumn column = new TableViewerColumn(tableViewer, SWT.NONE);
+ column.getColumn().setText(text);
+ column.getColumn().setWidth(width);
+ column.setLabelProvider(new ObservableMapEditingCellLabelProvider(attributeMap, modelEditing));
+ column.setEditingSupport(new ObservableMapEditingSupport(tableViewer, attributeMap, modelEditing));
+ return column;
+ }
+
+ private void createFieldSection(Composite parent) {
+ final Group section = createSectionGroup(parent, 2);
+
+ final IObservableValue personObservable = ViewersObservables.observeSingleSelection(tableViewer);
+
+ Text nameText = createTextField(section, "Name*");
+ IObservableValue nameObservable = BeansObservables.observeDetailValue(personObservable, "name", null);
+ bindTextField(nameText, nameObservable, nameEditing);
+
+ Text ageText = createTextField(section, "Age*");
+ IObservableValue ageObservable = BeansObservables.observeDetailValue(personObservable, "age", null);
+ bindTextField(ageText, ageObservable, ageEditing);
+
+ Text bdayText = createTextField(section, "Birthday");
+ IObservableValue bdayObservable = BeansObservables.observeDetailValue(personObservable, "birthday", null);
+ bindTextField(bdayText, bdayObservable, bdayEditing);
+ }
+
+ private Binding bindTextField(
+ Text text,
+ IObservableValue modelValue,
+ Editing editing) {
+ // Create the binding using the editing object.
+ ISWTObservableValue textObservable = SWTObservables.observeText(text, SWT.Modify);
+ Binding binding = dbc.bindValue(
+ textObservable,
+ modelValue,
+ editing.createTargetValueStrategy(),
+ editing.createModelValueStrategy());
+
+ // Decorate the control with the validation status.
+ ControlDecorationSupport.create(binding, SWT.TOP);
+
+ formatOnFocusOut(text, binding);
+
+ return binding;
+ }
+
+ private static void formatOnFocusOut(final Control control, final Binding binding) {
+ control.addFocusListener(new FocusAdapter() {
+ public void focusLost(FocusEvent e) {
+ IStatus dateValidationStatus = (IStatus) binding.getValidationStatus().getValue();
+ if (dateValidationStatus.isOK()) {
+ binding.updateModelToTarget();
+ }
+ }
+ });
+ }
+
+ private Group createSectionGroup(Composite parent, int numColumns) {
+ Group section = new Group(parent, SWT.SHADOW_ETCHED_IN);
+ GridLayoutFactory.fillDefaults().numColumns(numColumns).equalWidth(false).margins(5, 5).spacing(15, 5).applyTo(section);
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(section);
+ return section;
+ }
+
+ private static Text createTextField(Composite parent, String labelText) {
+ Label label = new Label(parent, SWT.LEFT);
+ label.setText(labelText);
+ GridDataFactory.fillDefaults().align(SWT.LEFT, SWT.CENTER).applyTo(label);
+
+ Text text = new Text(parent, SWT.BORDER);
+ GridDataFactory.fillDefaults().grab(true, false).hint(200, SWT.DEFAULT).applyTo(text);
+
+ return text;
+ }
+
+ private static final class Person extends ModelObject {
+
+ private String name;
+
+ private int age;
+
+ private Date birthday;
+
+ public Person(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ firePropertyChange("name", this.name, this.name = name);
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ firePropertyChange("age", this.age, this.age = age);
+ }
+
+ public Date getBirthday() {
+ return birthday;
+ }
+
+ public void setBirthday(Date birthday) {
+ firePropertyChange("birthday", this.birthday, this.birthday = birthday);
+ }
+ }
+}
Index: src/org/eclipse/jface/examples/databinding/util/RadixNumberFormat.java
===================================================================
RCS file: src/org/eclipse/jface/examples/databinding/util/RadixNumberFormat.java
diff -N src/org/eclipse/jface/examples/databinding/util/RadixNumberFormat.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/jface/examples/databinding/util/RadixNumberFormat.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.util;
+
+import java.math.BigInteger;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+
+import com.ibm.icu.math.BigDecimal;
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 3.2
+ *
+ */
+public class RadixNumberFormat extends NumberFormat {
+
+ private static final long serialVersionUID = 411884077848863891L;
+
+ private final int radix;
+
+ private final String prefix;
+
+ private final int digits;
+
+ private RadixNumberFormat(int radix, String prefix, int digits) {
+ this.radix = radix;
+ this.prefix = prefix != null ? prefix : "";
+ this.digits = digits;
+ }
+
+ public static RadixNumberFormat getHexInstance() {
+ return getHexInstance(null, 0);
+ }
+
+ public static RadixNumberFormat getHexInstance(String prefix, int digits) {
+ return new RadixNumberFormat(16, prefix, digits);
+ }
+
+ public StringBuffer format(double number, StringBuffer toAppendTo,
+ FieldPosition pos) {
+ // TODO omallo: Is this necessary due to a bug in NumberFormat.format(Object, ...)?
+ return format((long) number, toAppendTo, pos);
+ }
+
+ public StringBuffer format(long number, StringBuffer toAppendTo,
+ FieldPosition pos) {
+ return toAppendTo.append(prefix).append(addPadding(Long.toString(number, radix)));
+ }
+
+ public StringBuffer format(BigInteger number, StringBuffer toAppendTo,
+ FieldPosition pos) {
+ return toAppendTo.append(prefix).append(addPadding(number.toString(radix)));
+ }
+
+ public StringBuffer format(BigDecimal number, StringBuffer toAppendTo,
+ FieldPosition pos) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Number parse(String text, ParsePosition parsePosition) {
+ if (text.length() == 0) {
+ return null;
+ }
+
+ parsePosition.setIndex(parsePosition.getIndex() + text.length());
+
+ try {
+ if (text.startsWith(prefix)) {
+ return Integer.parseInt(text.substring(prefix.length()), radix);
+ }
+ return Integer.parseInt(text, radix);
+ } catch (NumberFormatException e) {
+ parsePosition.setErrorIndex(0);
+ return null;
+ }
+ }
+
+ private String addPadding(String numberText) {
+ if (numberText.length() >= digits) {
+ return numberText;
+ }
+
+ StringBuffer sb = new StringBuffer();
+ for (int i = numberText.length(); i < digits; i++) {
+ sb.append('0');
+ }
+ sb.append(numberText);
+ return sb.toString();
+ }
+}
Index: src/org/eclipse/jface/examples/databinding/util/ObservableMapEditingCellLabelProvider.java
===================================================================
RCS file: src/org/eclipse/jface/examples/databinding/util/ObservableMapEditingCellLabelProvider.java
diff -N src/org/eclipse/jface/examples/databinding/util/ObservableMapEditingCellLabelProvider.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/jface/examples/databinding/util/ObservableMapEditingCellLabelProvider.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.util;
+
+import org.eclipse.core.databinding.editing.Editing;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.jface.databinding.viewers.ObservableMapCellLabelProvider;
+import org.eclipse.jface.viewers.ViewerCell;
+
+/**
+ * @since 3.2
+ */
+public class ObservableMapEditingCellLabelProvider extends
+ ObservableMapCellLabelProvider {
+
+ private final IObservableMap attributeMap;
+
+ private final Editing editing;
+
+ public ObservableMapEditingCellLabelProvider(IObservableMap attributeMap, Editing editing) {
+ super(attributeMap);
+ this.attributeMap = attributeMap;
+ this.editing = editing;
+ }
+
+ public void update(ViewerCell cell) {
+ Object element = cell.getElement();
+ Object attribute = attributeMap.get(element);
+ cell.setText((String) editing.convertToTarget(attribute));
+ }
+}
#P org.eclipse.jface.tests.databinding
Index: src/org/eclipse/core/tests/internal/databinding/BindingMessagesTest.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/BindingMessagesTest.java,v
retrieving revision 1.1
diff -u -r1.1 BindingMessagesTest.java
--- src/org/eclipse/core/tests/internal/databinding/BindingMessagesTest.java 1 Apr 2007 20:58:10 -0000 1.1
+++ src/org/eclipse/core/tests/internal/databinding/BindingMessagesTest.java 29 Aug 2009 12:46:01 -0000
@@ -21,13 +21,13 @@
public class BindingMessagesTest extends TestCase {
public void testFormatString() throws Exception {
String key = "Validate_NumberOutOfRangeError";
- String result = BindingMessages.formatString(key, new Object[] {"1", "2"});
+ String result = BindingMessages.getFormattedString(key, new Object[] {"1", "2"});
assertFalse("key should not be returned", key.equals(result));
}
public void testFormatStringForKeyNotFound() throws Exception {
String key = "key_that_does_not_exist";
- String result = BindingMessages.formatString(key, null);
+ String result = BindingMessages.getFormattedString(key, null);
assertTrue(key.equals(result));
}
}