### Eclipse Workspace Patch 1.0 #P org.eclipse.jface.examples.databinding Index: .settings/org.eclipse.jdt.ui.prefs =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface.examples.databinding/.settings/org.eclipse.jdt.ui.prefs,v retrieving revision 1.6 diff -u -r1.6 org.eclipse.jdt.ui.prefs --- .settings/org.eclipse.jdt.ui.prefs 16 Feb 2009 23:03:20 -0000 1.6 +++ .settings/org.eclipse.jdt.ui.prefs 1 Mar 2010 21:29:23 -0000 @@ -1,4 +1,4 @@ -#Tue Feb 10 16:06:02 MST 2009 +#Thu Jan 07 19:11:04 CET 2010 cleanup.add_default_serial_version_id=true cleanup.add_generated_serial_version_id=false cleanup.add_missing_annotations=true @@ -51,7 +51,7 @@ cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile cleanup_settings_version=2 eclipse.preferences.version=1 -editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=false formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile formatter_settings_version=11 org.eclipse.jdt.ui.exception.name=e @@ -78,7 +78,7 @@ sp_cleanup.always_use_this_for_non_static_method_access=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false -sp_cleanup.format_source_code=true +sp_cleanup.format_source_code=false sp_cleanup.format_source_code_changes_only=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false 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 1 Mar 2010 21:29:23 -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/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 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.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/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,283 @@ +/******************************************************************************* + * 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.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.BooleanEditing; +import org.eclipse.core.databinding.editing.DateEditing; +import org.eclipse.core.databinding.editing.Editing; +import org.eclipse.core.databinding.editing.NumberEditing; +import org.eclipse.core.databinding.editing.StringEditing; +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.databinding.validation.IValidator; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.databinding.validation.constraint.Constraints; +import org.eclipse.core.databinding.validation.constraint.NumberConstraints; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.databinding.fieldassist.ControlDecorationSupport; +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.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; + +import com.ibm.icu.text.MessageFormat; + +public class Snippet035Editing { + + 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*"); + StringEditing emailEditing = EditingFactory.forEmailString(); + emailEditing.modelConstraints().required(); + bindTextField(emailText, new WritableValue(), emailEditing); + + // Edit a required, positive short using the default validation message. + Text positiveText = createTextField(section, "Positive short*"); + NumberEditing positiveEditing = EditingFactory.forShort(); + positiveEditing.modelConstraints().required().positive(); + bindTextField(positiveText, new WritableValue(), positiveEditing); + + // Edit a required double with a maximum of five significant and two + // fractional digits. + Text priceText = createTextField(section, "Price [$]*"); + NumberEditing priceEditing = EditingFactory.forDouble(); + priceEditing.modelConstraints().required().maxPrecision(5).maxScale(2); + bindTextField(priceText, new WritableValue(), priceEditing); + + // Edit an integer within the range [1, 100] using the default + // validation message. + Text rangeText = createTextField(section, "Value in [1, 100]"); + NumberEditing rangeEditing = EditingFactory.forInteger(); + rangeEditing.modelConstraints().range(1, 100); + bindTextField(rangeText, new WritableValue(0, null), rangeEditing); + + // Edit a percentage value which must lie within [0, 100] using a custom + // validation message which indicates that the value actually represents + // a percentage value. Note that the constraint message must be specified + // before the actual constraint. + Text percentText = createTextField(section, "Percentage [%]"); + NumberEditing percentEditing = EditingFactory.forInteger(); + percentEditing.modelConstraints() + .rangeMessage("Please specify a percentage value within [{0}, {1}].") + .range(0, 100); + bindTextField(percentText, new WritableValue(-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]"); + NumberEditing hexEditing = EditingFactory.forHexInteger(2); + hexEditing.modelConstraints().range(0x00, 0xff); + bindTextField(hexText, new WritableValue(0, null), hexEditing); + + // Edit a boolean value. + Text boolText = createTextField(section, "Boolean value"); + BooleanEditing boolEditing = EditingFactory.forBoolean(); + bindTextField(boolText, new WritableValue(true, null), boolEditing); + + // Edit a value within [1, 10] or [20, 30]. + Text rangesText = createTextField(section, "Value in [1, 10] or [20, 30]"); + NumberEditing rangesEditing = EditingFactory.forInteger(); + rangesEditing.modelConstraints().addValidator(new RangesValidator(1, 10, 20, 30)); + bindTextField(rangesText, new WritableValue(), rangesEditing); + } + + 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"); + DateEditing dateEditing = EditingFactory.forDate(); + dateEditing.modelConstraints().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(); + dateEditing.bindList(dbc, targetDateList, modelDateList); + + // 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 = editing.bindValue(dbc, textObservable, modelValue); + + // 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); + + final Text text = new Text(parent, SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).applyTo(text); + + // Select the text when gaining focus. + text.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + text.selectAll(); + } + }); + + return text; + } + + private static final class RangesValidator implements IValidator { + + private final String validationMessage; + + private final IValidator validator; + + public RangesValidator(int min1, int max1, int min2, int max2) { + this.validationMessage = MessageFormat.format( + "The value must lie within [{0}, {1}] or [{2}, {3}].", + new Object[] { min1, max1, min2, max2 }); + this.validator = + NumberConstraints.forInteger() + .range(min1, max1) + .range(min2, max2) + .aggregationPolicy(Constraints.Aggregation.MIN_SEVERITY) + .createValidator(); + } + + public IStatus validate(Object value) { + if (!validator.validate(value).isOK()) { + return ValidationStatus.error(validationMessage); + } + return ValidationStatus.ok(); + } + } +} 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,205 @@ +/******************************************************************************* + * 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.jface.examples.databinding.util; + +import java.util.Locale; + +import org.eclipse.core.databinding.editing.BooleanEditing; +import org.eclipse.core.databinding.editing.DateEditing; +import org.eclipse.core.databinding.editing.NumberEditing; +import org.eclipse.core.databinding.editing.StringEditing; + +import com.ibm.icu.text.DateFormat; +import com.ibm.icu.text.NumberFormat; + +/** + * @since 3.2 + */ +public final class EditingFactory { + + private static final String REQUIRED_MESSAGE = "Please specify a value."; + + private static final String NUMBER_PARSE_ERROR_MESSAGE = "The input is invalid."; + + private static final String NUMBER_OUT_OF_RANGE_MESSAGE = "The value lies outside the supported range."; + + private static final String NUMBER_RANGE_MESSAGE = "The value must lie between {0} and {1}."; + + private static final String[] BOOLEAN_TRUE_VALUES = new String[] { + "yes", "y", "true", "1" + }; + + private static final String[] BOOLEAN_FALSE_VALUES = new String[] { + "no", "n", "false", "0" + }; + + private static final String[] DATE_INPUT_PATTERNS = new String[] { + "yyMMdd", + "yyyyMMdd" + }; + + private static final String DATE_DISPLAY_PATTERN = "yyyyMMdd"; + + private static final String DATE_PARSE_ERROR_MESSAGE = createDateParseErrorMessage(DATE_INPUT_PATTERNS); + + private static final String EMAIL_REGEX = "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}\\b"; + + private static final String EMAIL_ERROR_MESSAGE = "Please specify a valid e-mail address."; + + private EditingFactory() { + // prevent instantiation + } + + public static StringEditing forString() { + return StringEditing.strippedToNull(); + } + + public static StringEditing forEmailString() { + StringEditing editing = StringEditing.strippedToNull(); + editing.modelConstraints() + .matchesMessage(EMAIL_ERROR_MESSAGE) + .matches(EMAIL_REGEX); + return editing; + } + + public static NumberEditing forLong() { + return forLong(Locale.getDefault()); + } + + public static NumberEditing forLong(Locale locale) { + NumberEditing editing = NumberEditing.forLongFormat( + NumberFormat.getIntegerInstance(locale), + NUMBER_PARSE_ERROR_MESSAGE, + NUMBER_OUT_OF_RANGE_MESSAGE); + configure(editing, locale); + return editing; + } + + public static NumberEditing forInteger() { + return forInteger(Locale.getDefault()); + } + + public static NumberEditing forInteger(Locale locale) { + NumberEditing editing = NumberEditing.forIntegerFormat( + NumberFormat.getIntegerInstance(locale), + NUMBER_PARSE_ERROR_MESSAGE, + NUMBER_OUT_OF_RANGE_MESSAGE); + configure(editing, locale); + return editing; + } + + public static NumberEditing forShort() { + return forShort(Locale.getDefault()); + } + + public static NumberEditing forShort(Locale locale) { + NumberEditing editing = NumberEditing.forShortFormat( + NumberFormat.getIntegerInstance(locale), + NUMBER_PARSE_ERROR_MESSAGE, + NUMBER_OUT_OF_RANGE_MESSAGE); + configure(editing, locale); + return editing; + } + + public static NumberEditing forByte() { + return forByte(Locale.getDefault()); + } + + public static NumberEditing forByte(Locale locale) { + NumberEditing editing = NumberEditing.forByteFormat( + NumberFormat.getIntegerInstance(locale), + NUMBER_PARSE_ERROR_MESSAGE, + NUMBER_OUT_OF_RANGE_MESSAGE); + configure(editing, locale); + return editing; + } + + public static NumberEditing forHexInteger(int digits) { + RadixNumberFormat hexFormat = RadixNumberFormat.getHexInstance("0x", digits); + NumberEditing editing = NumberEditing.forIntegerFormat( + hexFormat, + NUMBER_PARSE_ERROR_MESSAGE, + NUMBER_OUT_OF_RANGE_MESSAGE); + configure(editing, Locale.getDefault()); + editing.modelConstraints().numberFormat(hexFormat); + return editing; + } + + public static NumberEditing forDouble() { + return forDouble(Locale.getDefault()); + } + + public static NumberEditing forDouble(Locale locale) { + NumberEditing editing = NumberEditing.forDoubleFormat( + NumberFormat.getNumberInstance(locale), + NUMBER_PARSE_ERROR_MESSAGE, + NUMBER_OUT_OF_RANGE_MESSAGE); + configure(editing, locale); + return editing; + } + + private static void configure(NumberEditing editing, Locale locale) { + editing.modelConstraints() + .numberFormat(NumberFormat.getNumberInstance(locale)) + .scaleFormat(NumberFormat.getIntegerInstance(locale)) + .precisionFormat(NumberFormat.getIntegerInstance(locale)) + .requiredMessage(REQUIRED_MESSAGE) + .rangeMessage(NUMBER_RANGE_MESSAGE); + } + + public static BooleanEditing forBoolean() { + BooleanEditing editing = + BooleanEditing.forStringValues( + BOOLEAN_TRUE_VALUES, + BOOLEAN_FALSE_VALUES); + configure(editing); + return editing; + } + + private static void configure(BooleanEditing editing) { + editing.modelConstraints().requiredMessage(REQUIRED_MESSAGE); + } + + public static DateEditing forDate() { + return forDate(Locale.getDefault()); + } + + public static DateEditing forDate(Locale locale) { + DateEditing editing = DateEditing + .forFormats( + createDateFormats(DATE_INPUT_PATTERNS, locale), + DATE_PARSE_ERROR_MESSAGE, + DateFormat.getPatternInstance(DATE_DISPLAY_PATTERN, locale)); + editing.modelConstraints().requiredMessage(REQUIRED_MESSAGE); + return editing; + } + + private static DateFormat[] createDateFormats(String[] datePatterns, Locale locale) { + DateFormat[] dateFormats = new DateFormat[datePatterns.length]; + for (int i = 0; i < dateFormats.length; i++) { + dateFormats[i] = DateFormat.getPatternInstance(datePatterns[i], locale); + } + 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/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,99 @@ +/******************************************************************************* + * 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.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) { + 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 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.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)); + } +} 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,261 @@ +/******************************************************************************* + * 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.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.DateEditing; +import org.eclipse.core.databinding.editing.Editing; +import org.eclipse.core.databinding.editing.NumberEditing; +import org.eclipse.core.databinding.editing.StringEditing; +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.fieldassist.ControlDecorationSupport; +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.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 StringEditing nameEditing; + + private NumberEditing ageEditing; + + private DateEditing bdayEditing; + + 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)); + + nameEditing = EditingFactory.forString(); + nameEditing.modelConstraints().required(); + + ageEditing = EditingFactory.forInteger(); + ageEditing.modelConstraints().required().nonNegative(); + + bdayEditing = EditingFactory.forDate(); + bdayEditing.modelConstraints().before(new Date()); + + 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 = editing.bindValue(dbc, textObservable, modelValue); + + // 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); + + final Text text = new Text(parent, SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).hint(200, SWT.DEFAULT).applyTo(text); + + // Select the text when gaining focus. + text.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + text.selectAll(); + } + }); + + return text; + } + + public 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); + } + } +} #P org.eclipse.core.databinding Index: .classpath =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/.classpath,v retrieving revision 1.3 diff -u -r1.3 .classpath --- .classpath 3 Apr 2008 17:15:42 -0000 1.3 +++ .classpath 1 Mar 2010 21:29:24 -0000 @@ -1,6 +1,6 @@ - + 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.7 diff -u -r1.7 StringToNumberParser.java --- src/org/eclipse/core/internal/databinding/conversion/StringToNumberParser.java 22 Jan 2010 21:54:48 -0000 1.7 +++ src/org/eclipse/core/internal/databinding/conversion/StringToNumberParser.java 1 Mar 2010 21:29:24 -0000 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Ovidio Mallo - bug 183055 *******************************************************************************/ package org.eclipse.core.internal.databinding.conversion; @@ -115,12 +116,12 @@ .getErrorIndex() : position.getIndex(); if (errorIndex < value.length()) { - return BindingMessages.formatString( + return BindingMessages.getFormattedString( BindingMessages.VALIDATE_NUMBER_PARSE_ERROR, new Object[] { value, new Integer(errorIndex + 1), new Character(value.charAt(errorIndex)) }); } - return BindingMessages.formatString( + return BindingMessages.getFormattedString( BindingMessages.VALIDATE_NUMBER_PARSE_ERROR_NO_CHARACTER, new Object[] { value, new Integer(errorIndex + 1) }); } @@ -136,6 +137,23 @@ */ public static String createOutOfRangeMessage(Number minValue, Number maxValue, NumberFormat numberFormat) { + return createOutOfRangeMessage(BindingMessages + .getString(BindingMessages.VALIDATE_NUMBER_OUT_OF_RANGE_ERROR), + minValue, maxValue, numberFormat); + } + + /** + * 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; @@ -144,8 +162,8 @@ max = numberFormat.format(maxValue); } - return BindingMessages.formatString( - "Validate_NumberOutOfRangeError", new Object[] { min, max }); //$NON-NLS-1$ + return BindingMessages + .formatMessage(message, new Object[] { min, max }); } /** Index: src/org/eclipse/core/internal/databinding/conversion/StringToBooleanPrimitiveConverter.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToBooleanPrimitiveConverter.java,v retrieving revision 1.4 diff -u -r1.4 StringToBooleanPrimitiveConverter.java --- src/org/eclipse/core/internal/databinding/conversion/StringToBooleanPrimitiveConverter.java 25 May 2009 20:52:20 -0000 1.4 +++ src/org/eclipse/core/internal/databinding/conversion/StringToBooleanPrimitiveConverter.java 1 Mar 2010 21:29:24 -0000 @@ -9,6 +9,7 @@ * Contributors: * db4objects - Initial API and implementation * Tom Schindl - bugfix for 217940 + * Ovidio Mallo - bug 183055 */ package org.eclipse.core.internal.databinding.conversion; @@ -24,19 +25,22 @@ * StringToBooleanPrimitiveConverter. */ public class StringToBooleanPrimitiveConverter implements IConverter { - private static final String[] trueValues; - private static final String[] falseValues; + private static final String[] DEFAULT_TRUE_VALUES; + private static final String[] DEFAULT_FALSE_VALUES; static { String delimiter = BindingMessages.getString(BindingMessages.VALUE_DELIMITER); String values = BindingMessages.getString(BindingMessages.TRUE_STRING_VALUES); - trueValues = valuesToSortedArray(delimiter, values); + DEFAULT_TRUE_VALUES = valuesToSortedArray(delimiter, values); values = BindingMessages.getString(BindingMessages.FALSE_STRING_VALUES); - falseValues = valuesToSortedArray(delimiter, values); + DEFAULT_FALSE_VALUES = valuesToSortedArray(delimiter, values); } + private String[] trueValues = DEFAULT_TRUE_VALUES; + private String[] falseValues = DEFAULT_FALSE_VALUES; + /** * Returns a sorted array with all values converted to upper case. * @@ -57,6 +61,33 @@ return array; } + /** + * Sets the string values which are considered to represent a + * true and false value, respectively. + * + *

+ * Note that the capitalization of the provided strings is ignored. + *

+ * + * @param trueValues + * The set of strings representing a true value. + * @param falseValues + * The set of strings representing a false value. + */ + public final void setSourceStrings(String[] trueValues, String[] falseValues) { + this.trueValues = new String[trueValues.length]; + for (int i = 0; i < trueValues.length; i++) { + this.trueValues[i] = trueValues[i].toUpperCase(); + } + Arrays.sort(this.trueValues); // for binary search + + this.falseValues = new String[falseValues.length]; + for (int i = 0; i < falseValues.length; i++) { + this.falseValues[i] = falseValues[i].toUpperCase(); + } + Arrays.sort(this.falseValues); // for binary search + } + /* * (non-Javadoc) * 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 1 Mar 2010 21:29:24 -0000 @@ -10,6 +10,7 @@ * db4objects - Initial API and implementation * Tom Schindl - bugfix for 217940 * Matthew Hall - bug 121110 + * Ovidio Mallo - bug 183055 ******************************************************************************/ package org.eclipse.core.internal.databinding.conversion; @@ -52,6 +53,20 @@ }; /** + * Sets the {@link DateFormat}s to be used for parsing/formatting dates. Any + * of the supplied formats will be used for {@link #parse(String) parsing} a + * date from a string while the first format in the array will be used for + * {@link #format(Date) formatting} a date as a string. + * + * @param formatters + * The {@link DateFormat}s to be used for parsing/formatting + * dates. + */ + public final void setFormatters(DateFormat[] formatters) { + this.formatters = formatters; + } + + /** * 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. Index: src/org/eclipse/core/internal/databinding/validation/StringToCharacterValidator.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToCharacterValidator.java,v retrieving revision 1.3 diff -u -r1.3 StringToCharacterValidator.java --- src/org/eclipse/core/internal/databinding/validation/StringToCharacterValidator.java 25 May 2009 20:52:19 -0000 1.3 +++ src/org/eclipse/core/internal/databinding/validation/StringToCharacterValidator.java 1 Mar 2010 21:29:24 -0000 @@ -8,6 +8,7 @@ * Contributors: * Matt Carter - initial API and implementation * Tom Schindl - bugfix for 217940 + * Ovidio Mallo - bug 183055 ******************************************************************************/ package org.eclipse.core.internal.databinding.validation; @@ -26,6 +27,8 @@ private final StringToCharacterConverter converter; + private String parseErrorMessage = BindingMessages.getString(BindingMessages.VALIDATE_CHARACTER_HELP); + /** * @param converter */ @@ -33,6 +36,18 @@ this.converter = converter; } + /** + * Sets the validation message to be used in case the source string does not + * represent a valid character. + * + * @param message + * The validation message to be used in case the source string + * does not represent a valid character. + */ + public final void setParseErrorMessage(String message) { + this.parseErrorMessage = message; + } + /* * (non-Javadoc) * @@ -44,10 +59,8 @@ } catch (IllegalArgumentException e) { // The StringToCharacterConverter throws an IllegalArgumentException // if it cannot convert. - return ValidationStatus.error(BindingMessages - .getString(BindingMessages.VALIDATE_CHARACTER_HELP)); + return ValidationStatus.error(parseErrorMessage); } return Status.OK_STATUS; } - } 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 1 Mar 2010 21:29:24 -0000 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Tom Schindl - bugfix for 217940 + * Ovidio Mallo - bug 183055 *******************************************************************************/ package org.eclipse.core.internal.databinding.validation; @@ -28,6 +29,8 @@ public class StringToDateValidator implements IValidator { private final StringToDateConverter converter; + private String parseErrorMessage = null; + /** * @param converter */ @@ -35,6 +38,18 @@ this.converter = converter; } + /** + * Sets the validation message to be used in case the source string does not + * represent a valid date. + * + * @param message + * The validation message to be used in case the source string + * does not represent a valid date. + */ + public final void setParseErrorMessage(String message) { + this.parseErrorMessage = message; + } + /* * (non-Javadoc) * @@ -47,18 +62,17 @@ Object convertedValue = converter.convert(value); //The StringToDateConverter returns null if it can't parse the date. if (convertedValue == null) { - return ValidationStatus.error(getErrorMessage()); + return ValidationStatus.error(getParseErrorMessage()); } return Status.OK_STATUS; } - /* - * (non-Javadoc) - * - * @see org.eclipse.core.internal.databinding.validation.WrappedConverterValidator#getErrorMessage() - */ - protected String getErrorMessage() { + private String getParseErrorMessage() { + if (parseErrorMessage != null) { + return parseErrorMessage; + } + Date sampleDate = new Date(); // FIXME We need to use the information from the @@ -73,7 +87,9 @@ samples.append('\''); samples.append(util.format(sampleDate, 0)); samples.append('\''); - return BindingMessages.getString(BindingMessages.EXAMPLES) + ": " + samples + ",..."; //$NON-NLS-1$//$NON-NLS-2$ + parseErrorMessage = BindingMessages.getString(BindingMessages.EXAMPLES) + ": " + samples + ",..."; //$NON-NLS-1$//$NON-NLS-2$ + + return parseErrorMessage; } private static class FormatUtil extends DateConversionSupport { 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 1 Mar 2010 21:29:24 -0000 @@ -7,12 +7,16 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Ovidio Mallo - bug 183055 ******************************************************************************/ 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.BindingMessages; import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; import org.eclipse.core.internal.databinding.conversion.StringToNumberParser.ParseResult; import org.eclipse.core.runtime.IStatus; @@ -31,7 +35,10 @@ private final Number min; private final Number max; - private String outOfRangeMessage; + private String parseErrorMessage; + + private String outOfRangeMessage = BindingMessages.getString(BindingMessages.VALIDATE_NUMBER_OUT_OF_RANGE_ERROR); + private String formattedOutOfRangeMessage; /** * Constructs a new instance. @@ -55,6 +62,34 @@ } /** + * Sets the validation message pattern to be used in case the source string + * does not represent a valid number. + * + * @param message + * The validation message pattern to be used in case the source + * string does not represent a valid number. + */ + public final void setParseErrorMessage(String message) { + this.parseErrorMessage = message; + } + + /** + * Sets the validation message pattern to be used in case the parsed number + * lies outside the value range supported by the number type associated to + * this validator. + * + * @param message + * The validation message pattern to be used in case the parsed + * number lies outside the supported value range. Can be + * parameterized by the Integer.MIN_VALUE and + * Integer.MAX_VALUE values. + */ + public final void setOutOfRangeMessage(String message) { + this.outOfRangeMessage = message; + this.formattedOutOfRangeMessage = null; + } + + /** * Validates the provided value. An error status is returned if: *
    *
  • The value cannot be parsed.
  • @@ -69,16 +104,10 @@ if (result.getNumber() != null) { if (!isInRange(result.getNumber())) { - if (outOfRangeMessage == null) { - outOfRangeMessage = StringToNumberParser - .createOutOfRangeMessage(min, max, converter - .getNumberFormat()); - } - - return ValidationStatus.error(outOfRangeMessage); + return ValidationStatus.error(getFormattedOutOfRangeMessage()); } } else if (result.getPosition() != null) { - String parseErrorMessage = StringToNumberParser.createParseErrorMessage( + String parseErrorMessage = createParseErrorMessage( (String) value, result.getPosition()); return ValidationStatus.error(parseErrorMessage); @@ -94,4 +123,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 getFormattedOutOfRangeMessage() { + if (formattedOutOfRangeMessage == null) { + formattedOutOfRangeMessage = StringToNumberParser + .createOutOfRangeMessage(outOfRangeMessage, min, max, + converter.getNumberFormat()); + } + return formattedOutOfRangeMessage; + } } 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 1 Mar 2010 21:29:24 -0000 @@ -8,7 +8,9 @@ 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.databinding.validation.constraint, 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", org.eclipse.core.internal.databinding.validation;x-friends:="org.eclipse.jface.tests.databinding" @@ -22,6 +24,6 @@ org.osgi.util.tracker;version="[1.3.3,2.0.0)";resolution:=optional, org.eclipse.osgi.framework.log;version="[1.0.0,2.0.0)";resolution:=optional Bundle-RequiredExecutionEnvironment: CDC-1.1/Foundation-1.1, - J2SE-1.4 + J2SE-1.5 Bundle-Activator: org.eclipse.core.internal.databinding.Activator Bundle-ActivationPolicy: lazy Index: .settings/org.eclipse.jdt.core.prefs =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/.settings/org.eclipse.jdt.core.prefs,v retrieving revision 1.6 diff -u -r1.6 org.eclipse.jdt.core.prefs --- .settings/org.eclipse.jdt.core.prefs 5 Feb 2009 18:51:11 -0000 1.6 +++ .settings/org.eclipse.jdt.core.prefs 1 Mar 2010 21:29:24 -0000 @@ -1,4 +1,4 @@ -#Thu Feb 05 11:35:38 MST 2009 +#Sun Feb 28 19:30:40 CET 2010 eclipse.preferences.version=1 org.eclipse.jdt.core.builder.cleanOutputFolder=clean org.eclipse.jdt.core.builder.duplicateResourceTask=warning @@ -17,9 +17,9 @@ org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.4 +org.eclipse.jdt.core.compiler.compliance=1.5 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -96,7 +96,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.3 +org.eclipse.jdt.core.compiler.source=1.5 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 Index: .settings/org.eclipse.jdt.ui.prefs =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/.settings/org.eclipse.jdt.ui.prefs,v retrieving revision 1.3 diff -u -r1.3 org.eclipse.jdt.ui.prefs --- .settings/org.eclipse.jdt.ui.prefs 16 Feb 2009 23:03:21 -0000 1.3 +++ .settings/org.eclipse.jdt.ui.prefs 1 Mar 2010 21:29:24 -0000 @@ -1,4 +1,4 @@ -#Tue Feb 10 16:05:48 MST 2009 +#Thu Jan 07 19:10:54 CET 2010 cleanup.add_default_serial_version_id=true cleanup.add_generated_serial_version_id=false cleanup.add_missing_annotations=true @@ -51,7 +51,7 @@ cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile cleanup_settings_version=2 eclipse.preferences.version=1 -editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=false formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile formatter_settings_version=11 org.eclipse.jdt.ui.exception.name=e @@ -78,7 +78,7 @@ sp_cleanup.always_use_this_for_non_static_method_access=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false -sp_cleanup.format_source_code=true +sp_cleanup.format_source_code=false sp_cleanup.format_source_code_changes_only=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false Index: src/org/eclipse/core/databinding/UpdateStrategy.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateStrategy.java,v retrieving revision 1.17 diff -u -r1.17 UpdateStrategy.java --- src/org/eclipse/core/databinding/UpdateStrategy.java 25 May 2009 20:52:19 -0000 1.17 +++ src/org/eclipse/core/databinding/UpdateStrategy.java 1 Mar 2010 21:29:24 -0000 @@ -9,6 +9,7 @@ * IBM Corporation - initial API and implementation * Matt Carter - bug 180392 * - bug 197679 (Character support completed) + * Ovidio Mallo - bug 183055 *******************************************************************************/ package org.eclipse.core.databinding; @@ -24,6 +25,7 @@ import org.eclipse.core.databinding.util.Policy; import org.eclipse.core.internal.databinding.ClassLookupSupport; import org.eclipse.core.internal.databinding.Pair; +import org.eclipse.core.internal.databinding.conversion.BooleanToStringConverter; import org.eclipse.core.internal.databinding.conversion.CharacterToStringConverter; import org.eclipse.core.internal.databinding.conversion.IdentityConverter; import org.eclipse.core.internal.databinding.conversion.IntegerToStringConverter; @@ -35,7 +37,6 @@ import org.eclipse.core.internal.databinding.conversion.NumberToIntegerConverter; import org.eclipse.core.internal.databinding.conversion.NumberToLongConverter; import org.eclipse.core.internal.databinding.conversion.NumberToShortConverter; -import org.eclipse.core.internal.databinding.conversion.ObjectToStringConverter; import org.eclipse.core.internal.databinding.conversion.StringToByteConverter; import org.eclipse.core.internal.databinding.conversion.StringToCharacterConverter; import org.eclipse.core.internal.databinding.conversion.StringToShortConverter; @@ -307,7 +308,7 @@ new Pair(BOOLEAN_CLASS, "java.lang.Boolean"), new IdentityConverter(Boolean.class, Boolean.class)); //$NON-NLS-1$ converterMap .put( - new Pair(BOOLEAN_CLASS, "java.lang.String"), new ObjectToStringConverter(Boolean.class)); //$NON-NLS-1$ + new Pair(BOOLEAN_CLASS, "java.lang.String"), new BooleanToStringConverter(Boolean.class)); //$NON-NLS-1$ converterMap .put( new Pair(BOOLEAN_CLASS, "java.lang.Object"), new IdentityConverter(Boolean.class, Object.class)); //$NON-NLS-1$ 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 1 Mar 2010 21:29:24 -0000 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Tom Schindl - bugfix for 217940 + * Ovidio Mallo - bug 183055 *******************************************************************************/ package org.eclipse.core.internal.databinding; @@ -69,6 +70,11 @@ public static final String FALSE_STRING_VALUES = "FalseStringValues"; //$NON-NLS-1$ /** + * Key to be used for a "Validate_InvalidBooleanString" message + */ + public static final String VALIDATE_INVALID_BOOLEAN_STRING = "Validate_InvalidBooleanString"; //$NON-NLS-1$ + + /** * Key to be used for a "Validate_NumberOutOfRangeError" message */ public static final String VALIDATE_NUMBER_OUT_OF_RANGE_ERROR = "Validate_NumberOutOfRangeError"; //$NON-NLS-1$ @@ -99,6 +105,31 @@ public static final String VALIDATE_CHARACTER_HELP = "Validate_CharacterHelp"; //$NON-NLS-1$ /** + * Key to be used for a "Validate_CharacterNoWhitespace" message + */ + public static final String VALIDATE_CHARACTER_NO_WHITESPACE = "Validate_CharacterNoWhitespace"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_CharacterNoSpace" message + */ + public static final String VALIDATE_CHARACTER_NO_SPACE = "Validate_CharacterNoSpace"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_CharacterLetter" message + */ + public static final String VALIDATE_CHARACTER_LETTER = "Validate_CharacterLetter"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_CharacterDigit" message + */ + public static final String VALIDATE_CHARACTER_DIGIT = "Validate_CharacterDigit"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_CharacterLetterOrDigit" message + */ + public static final String VALIDATE_CHARACTER_LETTER_OR_DIGIT = "Validate_CharacterLetterOrDigit"; //$NON-NLS-1$ + + /** * Key to be used for a "Examples" message */ public static final String EXAMPLES = "Examples"; //$NON-NLS-1$ @@ -109,10 +140,110 @@ public static final String VALIDATE_NUMBER_PARSE_ERROR_NO_CHARACTER = "Validate_NumberParseErrorNoCharacter"; //$NON-NLS-1$ /** + * Key to be used for a "Validate_NonNull" message + */ + public static final String VALIDATE_NON_NULL = "Validate_NonNull"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_NonEmptyString" message + */ + public static final String VALIDATE_NON_EMPTY_STRING = "Validate_NonEmptyString"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_NonStringRegex" message + */ + public static final String VALIDATE_STRING_REGEX = "Validate_NonStringRegex"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_NumberRangeGreater" message + */ + public static final String VALIDATE_NUMBER_RANGE_GREATER = "Validate_NumberRangeGreater"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_NumberRangeGreaterEqual" message + */ + public static final String VALIDATE_NUMBER_RANGE_GREATER_EQUAL = "Validate_NumberRangeGreaterEqual"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_NumberRangeLess" message + */ + public static final String VALIDATE_NUMBER_RANGE_LESS = "Validate_NumberRangeLess"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_NumberRangeLessEqual" message + */ + public static final String VALIDATE_NUMBER_RANGE_LESS_EQUAL = "Validate_NumberRangeLessEqual"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_NumberRangeWithinRange" message + */ + public static final String VALIDATE_NUMBER_RANGE_WITHIN_RANGE = "Validate_NumberRangeWithinRange"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_NumberRangePositive" message + */ + public static final String VALIDATE_NUMBER_RANGE_POSITIVE = "Validate_NumberRangePositive"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_NumberRangeNonNegative" message + */ + public static final String VALIDATE_NUMBER_RANGE_NON_NEGATIVE = "Validate_NumberRangeNonNegative"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_DateRangeAfter" message + */ + public static final String VALIDATE_DATE_RANGE_AFTER = "Validate_DateRangeAfter"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_DateRangeAfterEqual" message + */ + public static final String VALIDATE_DATE_RANGE_AFTER_EQUAL = "Validate_DateRangeAfterEqual"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_DateRangeBefore" message + */ + public static final String VALIDATE_DATE_RANGE_BEFORE = "Validate_DateRangeBefore"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_DateRangeBeforeEqual" message + */ + public static final String VALIDATE_DATE_RANGE_BEFORE_EQUAL = "Validate_DateRangeBeforeEqual"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_DateRangeWithinRange" message + */ + public static final String VALIDATE_DATE_RANGE_WITHIN_RANGE = "Validate_DateRangeWithinRange"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_StringLengthMin" message + */ + public static final String VALIDATE_STRING_LENGTH_MIN = "Validate_StringLengthMin"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_StringLengthMax" message + */ + public static final String VALIDATE_STRING_LENGTH_MAX = "Validate_StringLengthMax"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_StringLengthRange" message + */ + public static final String VALIDATE_STRING_LENGTH_RANGE = "Validate_StringLengthRange"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_DecimalMaxScale" message + */ + public static final String VALIDATE_DECIMAL_MAX_SCALE = "Validate_DecimalMaxScale"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_DecimalMaxPrecision" message + */ + public static final String VALIDATE_DECIMAL_MAX_PRECISION = "Validate_DecimalMaxPrecision"; //$NON-NLS-1$ + + /** * 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 +259,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/messages.properties =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/messages.properties,v retrieving revision 1.9 diff -u -r1.9 messages.properties --- src/org/eclipse/core/internal/databinding/messages.properties 16 Apr 2008 02:51:53 -0000 1.9 +++ src/org/eclipse/core/internal/databinding/messages.properties 1 Mar 2010 21:29:24 -0000 @@ -43,6 +43,8 @@ TrueStringValues=yes,true FalseStringValues=no,false +Validate_InvalidBooleanString=The boolean string is invalid. + Validate_NumberOutOfRangeError=Please enter a value between [{0}] and [{1}] and with a similar format. Validate_NumberParseError=Invalid character for value [{0}] at position [{1}] character [{2}]. Validate_NumberParseErrorNoCharacter=Missing character for value [{0}] at position [{1}]. @@ -53,4 +55,47 @@ Validate_NoChangeAllowedHelp=Changes are not allowed in this field Validate_CharacterHelp=Please type a character +#CharacterValidator +Validate_CharacterNoWhitespace=The character must be no whitespace. +Validate_CharacterNoSpace=The character must be no space. +Validate_CharacterLetter=The character must be a letter. +Validate_CharacterDigit=The character must be a digit. +Validate_CharacterLetterOrDigit=The character must be a letter or digit. + +#NonNullValidator +Validate_NonNull=The value must not be empty. + +#NonEmptyStringValidator +Validate_NonEmptyString=The string must not be empty. + +#StringRegexValidator +Validate_NonStringRegex=The string must match the following pattern: {0}. + +#NumberRangeValidator +Validate_NumberRangeGreater=The value must be greater than {0}. +Validate_NumberRangeGreaterEqual=The value must be greater than or equal to {0}. +Validate_NumberRangeLess=The value must be less than {0}. +Validate_NumberRangeLessEqual=The value must be less than or equal to {0}. +Validate_NumberRangeWithinRange=The value must lie between {0} and {1}. +Validate_NumberRangePositive=The value must be positive. +Validate_NumberRangeNonNegative=The value must not be negative. + +#DateRangeValidator +Validate_DateRangeAfter=The date must be after {0}. +Validate_DateRangeAfterEqual=The date must be after or on {0}. +Validate_DateRangeBefore=The date must be before {0}. +Validate_DateRangeBeforeEqual=The date must be before or on {0}. +Validate_DateRangeWithinRange=The date must lie between {0} and {1}. + +#StringLengthValidator +Validate_StringLengthMin=The string must have at least {0} characters. +Validate_StringLengthMax=The string must not have more than {0} characters. +Validate_StringLengthRange=The string must have between {0} and {1} characters. + +#DecimalScaleValidator +Validate_DecimalMaxScale=The decimal must not have more than {0} fractional digits. + +#DecimalPrecisionValidator +Validate_DecimalMaxPrecision=The decimal must not have more than {0} significant digits. + Examples=Examples \ No newline at end of file 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,129 @@ +/******************************************************************************* + * 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.IConverter; +import org.eclipse.core.databinding.validation.constraint.StringConstraints; +import org.eclipse.core.internal.databinding.conversion.StringStripConverter; +import org.eclipse.core.internal.databinding.conversion.StringTrimConverter; + +/** + * @noextend This class is not intended to be subclassed by clients. + * @since 1.3 + */ +public class StringEditing extends Editing { + + /** + * Creates a new editing object for booleans. + * + * @param targetConverter + * The {@link #setTargetConverter(IConverter) target converter} + * to use for editing. + * + * @noreference This constructor is not intended to be referenced by + * clients. + */ + protected 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. + * + * @return The new editing object which strips whitespace from both ends of + * the input string. + * + * @see Character#isWhitespace(char) + */ + public static StringEditing stripped() { + return new StringEditing(new StringStripConverter(false)); + } + + /** + * Creates a new editing object which strips whitespace from both ends of + * the input string. In case stripping the input string results in an empty + * string, the input string will be converted to null. + * + * @return The new editing object which strips whitespace from both ends of + * the input string. Resulting empty strings are converted to + * null. + * + * @see Character#isWhitespace(char) + */ + public static StringEditing strippedToNull() { + return new StringEditing(new StringStripConverter(true)); + } + + /** + * Creates a new editing object which {@link String#trim()}s the input + * string. + * + * @return The new editing object which trims whitespace from both ends of + * the input string. + * + * @see String#trim() + */ + public static StringEditing trimmed() { + return new StringEditing(new StringTrimConverter(false)); + } + + /** + * Creates a new editing object which {@link String#trim()}s the input + * string. In case trimming the input string results in an empty string, the + * input string will be converted to null. + * + * @return The new editing object which trims whitespace from both ends of + * the input string. Resulting empty strings are converted to + * null. + * + * @see String#trim() + */ + public static StringEditing trimmedToNull() { + return new StringEditing(new StringTrimConverter(true)); + } + + public StringConstraints targetConstraints() { + return (StringConstraints) super.targetConstraints(); + } + + public StringConstraints modelConstraints() { + return (StringConstraints) super.modelConstraints(); + } + + public StringConstraints beforeSetModelConstraints() { + return (StringConstraints) super.beforeSetModelConstraints(); + } + + protected StringConstraints createTargetConstraints() { + return new StringConstraints(); + } + + protected StringConstraints createModelConstraints() { + return new StringConstraints(); + } + + protected StringConstraints createBeforeSetModelConstraints() { + return new StringConstraints(); + } +} 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,285 @@ +/******************************************************************************* + * 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.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; + +import com.ibm.icu.text.NumberFormat; + +/** + * Provides validations for numbers which must lie within an open/closed range. + * + * @param + * + * @since 1.3 + */ +public class NumberRangeValidator> implements + IValidator { + + // The set of constraint types for the lower/upper bound of the date range. + 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; + + // The default validation messages. + protected static final String GREATER_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_NUMBER_RANGE_GREATER); + protected static final String GREATER_EQUAL_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_NUMBER_RANGE_GREATER_EQUAL); + protected static final String LESS_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_NUMBER_RANGE_LESS); + protected static final String LESS_EQUAL_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_NUMBER_RANGE_LESS_EQUAL); + protected static final String WITHIN_RANGE_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_NUMBER_RANGE_WITHIN_RANGE); + protected static final String POSITIVE_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_NUMBER_RANGE_POSITIVE); + protected static final String NON_NEGATIVE_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_NUMBER_RANGE_NON_NEGATIVE); + + private final N min; + private final N max; + private final int minConstraint; + private final int maxConstraint; + private final String validationMessage; + private String formattedValidationMessage; + private final NumberFormat format; + + /** + * Single constructor which supports the individual number range types. + * + * @param min + * The min number of the range or null in case no + * lower bound is defined. + * @param max + * The max number of the range or null in case no + * upper bound is defined. + * @param minConstraint + * The type of constraint imposed by the lower bound of the + * range. + * @param maxConstraint + * The type of constraint imposed by the upper bound of the + * range. + * @param validationMessage + * The validation message pattern to use. Can be parameterized by + * the lower and upper bound of the range, if defined. + * @param format + * The integer format to use for formatting numbers in the + * validation message. + */ + protected NumberRangeValidator(N min, N 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; + } + + /** + * Creates a validator which checks that an input integer is greater than + * the given number. + * + * @param + * @param number + * The reference number of the greater constraint. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the given reference number. + * @param format + * The display format to use for formatting integers in the + * validation message. + * @return The validator instance. + */ + public static > NumberRangeValidator greater( + M number, String validationMessage, NumberFormat format) { + return new NumberRangeValidator(number, null, GREATER, UNDEFINED, + defaultIfNull(validationMessage, GREATER_MESSAGE), format); + } + + /** + * Creates a validator which checks that an input integer is greater than or + * equal to the given number. + * + * @param + * @param number + * The reference number of the greater-equal constraint. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the given reference number. + * @param format + * The display format to use for formatting integers in the + * validation message. + * @return The validator instance. + */ + public static > NumberRangeValidator greaterEqual( + M number, String validationMessage, NumberFormat format) { + return new NumberRangeValidator(number, null, GREATER_EQUAL, + UNDEFINED, defaultIfNull(validationMessage, + GREATER_EQUAL_MESSAGE), format); + } + + /** + * Creates a validator which checks that an input integer is less than the + * given number. + * + * @param + * @param number + * The reference number of the less constraint. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the given reference number. + * @param format + * The display format to use for formatting integers in the + * validation message. + * @return The validator instance. + */ + public static > NumberRangeValidator less( + M number, String validationMessage, NumberFormat format) { + return new NumberRangeValidator(null, number, UNDEFINED, LESS, + defaultIfNull(validationMessage, LESS_MESSAGE), format); + } + + /** + * Creates a validator which checks that an input integer is less than or + * equal to the given number. + * + * @param + * @param number + * The reference number of the less-equal constraint. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the given reference number. + * @param format + * The display format to use for formatting integers in the + * validation message. + * @return The validator instance. + */ + public static > NumberRangeValidator lessEqual( + M number, String validationMessage, NumberFormat format) { + return new NumberRangeValidator(null, number, UNDEFINED, LESS_EQUAL, + defaultIfNull(validationMessage, LESS_EQUAL_MESSAGE), format); + } + + /** + * Creates a validator which checks that an input integer is within the + * given range. + * + * @param + * @param min + * The lower bound of the range (inclusive). + * @param max + * The upper bound of the range (inclusive). + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the range's lower and upper bound. + * @param format + * The display format to use for formatting integers in the + * validation message. + * @return The validator instance. + */ + public static > NumberRangeValidator range( + M min, M max, String validationMessage, NumberFormat format) { + return new NumberRangeValidator(min, max, GREATER_EQUAL, LESS_EQUAL, + defaultIfNull(validationMessage, WITHIN_RANGE_MESSAGE), format); + } + + /** + * + * @param + * @param one + * @param validationMessage + * @param format + * @return . + */ + public static > NumberRangeValidator positive( + M one, String validationMessage, NumberFormat format) { + return new NumberRangeValidator(one, null, GREATER_EQUAL, UNDEFINED, + defaultIfNull(validationMessage, POSITIVE_MESSAGE), format); + } + + /** + * + * @param + * @param zero + * @param validationMessage + * @param format + * @return . + */ + public static > NumberRangeValidator nonNegative( + M zero, String validationMessage, NumberFormat format) { + return new NumberRangeValidator(zero, null, GREATER_EQUAL, + UNDEFINED, defaultIfNull(validationMessage, + NON_NEGATIVE_MESSAGE), format); + } + + public IStatus validate(Object value) { + if (value != null) { + N number = (N) value; + if ((min != null && !fulfillsConstraint(number, min, minConstraint)) + || (max != null && !fulfillsConstraint(number, max, + maxConstraint))) { + return ValidationStatus.error(getFormattedValidationMessage()); + } + } + return ValidationStatus.ok(); + } + + private boolean fulfillsConstraint(N number1, N number2, int constraint) { + int compareResult = number1.compareTo(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$ + } + } + + private synchronized String getFormattedValidationMessage() { + if (formattedValidationMessage == null) { + formattedValidationMessage = MessageFormat.format( + validationMessage, getValidationMessageArguments()); + } + return formattedValidationMessage; + } + + private String[] getValidationMessageArguments() { + synchronized (format) { + 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/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,243 @@ +/******************************************************************************* + * 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.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; + +import com.ibm.icu.text.DateFormat; + +/** + * Provides validations for date which must lie within an open/closed range. + * + * @since 1.3 + */ +public class DateRangeValidator implements IValidator { + + // The set of constraint types for the lower/upper bound of the date range. + 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; + + // The default validation messages. + private static final String AFTER_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_DATE_RANGE_AFTER); + private static final String AFTER_EQUAL_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_DATE_RANGE_AFTER_EQUAL); + private static final String BEFORE_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_DATE_RANGE_BEFORE); + private static final String BEFORE_EQUAL_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_DATE_RANGE_BEFORE_EQUAL); + private static final String WITHIN_RANGE_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_DATE_RANGE_WITHIN_RANGE); + + private final Date min; + private final Date max; + private final int minConstraint; + private final int maxConstraint; + private final String validationMessage; + private String formattedValidationMessage; + private final DateFormat format; + + /** + * Single constructor which supports the individual date range types. + * + * @param min + * The min date of the range or null in case no + * lower bound is defined. + * @param max + * The max date of the range or null in case no + * upper bound is defined. + * @param minConstraint + * The type of constraint imposed by the lower bound of the + * range. + * @param maxConstraint + * The type of constraint imposed by the upper bound of the + * range. + * @param validationMessage + * The validation message pattern to use. Can be parameterized by + * the lower and upper bound of the range, if defined. + * @param format + * The date format to use for formatting dates in the validation + * message. + */ + 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; + } + + /** + * Creates a validator which checks that an input date is after the given + * date. + * + * @param date + * The reference date of the after constraint. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the given reference date. + * @param format + * The display format to use for formatting dates in the + * validation message. + * @return The validator instance. + */ + public static DateRangeValidator after(Date date, String validationMessage, + DateFormat format) { + return new DateRangeValidator(date, null, AFTER, UNDEFINED, + defaultIfNull(validationMessage, AFTER_MESSAGE), format); + } + + /** + * Creates a validator which checks that an input date is after or on the + * given date. + * + * @param date + * The reference date of the after-equal constraint. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the given reference date. + * @param format + * The display format to use for formatting dates in the + * validation message. + * @return The validator instance. + */ + public static DateRangeValidator afterEqual(Date date, + String validationMessage, DateFormat format) { + return new DateRangeValidator(date, null, AFTER_EQUAL, UNDEFINED, + defaultIfNull(validationMessage, AFTER_EQUAL_MESSAGE), format); + } + + /** + * Creates a validator which checks that an input date is before the given + * date. + * + * @param date + * The reference date of the before constraint. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the given reference date. + * @param format + * The display format to use for formatting dates in the + * validation message. + * @return The validator instance. + */ + public static DateRangeValidator before(Date date, + String validationMessage, DateFormat format) { + return new DateRangeValidator(null, date, UNDEFINED, BEFORE, + defaultIfNull(validationMessage, BEFORE_MESSAGE), format); + } + + /** + * Creates a validator which checks that an input date is before or on the + * given date. + * + * @param date + * The reference date of the before-equal constraint. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the given reference date. + * @param format + * The display format to use for formatting dates in the + * validation message. + * @return The validator instance. + */ + public static DateRangeValidator beforeEqual(Date date, + String validationMessage, DateFormat format) { + return new DateRangeValidator(null, date, UNDEFINED, BEFORE_EQUAL, + defaultIfNull(validationMessage, BEFORE_EQUAL_MESSAGE), format); + } + + /** + * Creates a validator which checks that an input date is within the given + * range. + * + * @param min + * The lower bound of the range (inclusive). + * @param max + * The upper bound of the range (inclusive). + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the range's lower and upper bound. + * @param format + * The display format to use for formatting dates in the + * validation message. + * @return The validator instance. + */ + 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))) { + return ValidationStatus.error(getFormattedValidationMessage()); + } + } + 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 synchronized String getFormattedValidationMessage() { + if (formattedValidationMessage == null) { + formattedValidationMessage = MessageFormat.format( + validationMessage, getValidationMessageArguments()); + } + return formattedValidationMessage; + } + + private String[] getValidationMessageArguments() { + synchronized (format) { + 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/internal/databinding/validation/StringLengthValidator.java =================================================================== RCS file: src/org/eclipse/core/internal/databinding/validation/StringLengthValidator.java diff -N src/org/eclipse/core/internal/databinding/validation/StringLengthValidator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/internal/databinding/validation/StringLengthValidator.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,167 @@ +/******************************************************************************* + * 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.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; + +import com.ibm.icu.text.NumberFormat; + +/** + * Provides validations for strings which must have a specified length. + * + * @since 1.3 + */ +public class StringLengthValidator implements IValidator { + + // The default validation messages. + private static final String MIN_LENGTH_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_STRING_LENGTH_MIN); + private static final String MAX_LENGTH_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_STRING_LENGTH_MAX); + private static final String LENGTH_RANGE_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_STRING_LENGTH_RANGE); + + private final Integer minLength; + private final Integer maxLength; + private final String validationMessage; + private String formattedValidationMessage; + private final NumberFormat format; + + /** + * Creates a string length validator defining a minimum and/or maximum + * length for a string. + * + * @param minLength + * The minimum length of the string or null in case + * no minimum length is defined. + * @param maxLength + * The maximum length of the string or null in case + * no maximum length is defined. + * @param validationMessage + * The validation message pattern to use. Can be parameterized by + * the lower and upper bound of the range, if defined. + * @param format + * The number format to use for formatting integers (the length + * bounds) in the validation message. + */ + private StringLengthValidator(Integer minLength, Integer maxLength, + String validationMessage, NumberFormat format) { + this.minLength = minLength; + this.maxLength = maxLength; + this.validationMessage = validationMessage; + this.format = format; + } + + /** + * Creates a validator which checks that an input string has the given + * minimum length. + * + * @param minLength + * The minimum length which the input string must have. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the given minimum length. + * @param format + * The display format to use for formatting integers (the minimum + * length) in the validation message. + * @return The validator instance. + */ + public static StringLengthValidator min(int minLength, + String validationMessage, NumberFormat format) { + return new StringLengthValidator(new Integer(minLength), null, + defaultIfNull(validationMessage, MIN_LENGTH_MESSAGE), format); + } + + /** + * Creates a validator which checks that an input string has the given + * maximum length. + * + * @param maxLength + * The maximum length which the input string must have. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the given maximum length. + * @param format + * The display format to use for formatting integers (the maximum + * length) in the validation message. + * @return The validator instance. + */ + public static StringLengthValidator max(int maxLength, + String validationMessage, NumberFormat format) { + return new StringLengthValidator(null, new Integer(maxLength), + defaultIfNull(validationMessage, MAX_LENGTH_MESSAGE), format); + } + + /** + * Creates a validator which checks that the length of an input string lies + * in the given range. + * + * @param minLength + * The minimum length which the input string must have. + * @param maxLength + * The maximum length which the input string must have. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the given minimum and maximum lengths. + * @param format + * The display format to use for formatting integers (the + * minimum/maximum length) in the validation message. + * @return The validator instance. + */ + public static StringLengthValidator range(int minLength, int maxLength, + String validationMessage, NumberFormat format) { + return new StringLengthValidator(new Integer(minLength), new Integer( + maxLength), defaultIfNull(validationMessage, + LENGTH_RANGE_MESSAGE), format); + } + + public IStatus validate(Object value) { + String input = (String) value; + if (input != null) { + int inputLength = input.length(); + if ((minLength != null && inputLength < minLength.intValue()) + || maxLength != null && inputLength > maxLength.intValue()) { + return ValidationStatus.error(getFormattedValidationMessage()); + } + } + return ValidationStatus.ok(); + } + + private synchronized String getFormattedValidationMessage() { + if (formattedValidationMessage == null) { + formattedValidationMessage = MessageFormat.format( + validationMessage, getValidationMessageArguments()); + } + return formattedValidationMessage; + } + + private String[] getValidationMessageArguments() { + synchronized (format) { + if (minLength == null) { + return new String[] { format.format(maxLength) }; + } else if (maxLength == null) { + return new String[] { format.format(minLength) }; + } + return new String[] { format.format(minLength), + format.format(maxLength) }; + } + } + + private static String defaultIfNull(String string, String defaultString) { + return (string != null) ? string : defaultString; + } +} Index: src/org/eclipse/core/databinding/validation/constraint/CharacterConstraints.java =================================================================== RCS file: src/org/eclipse/core/databinding/validation/constraint/CharacterConstraints.java diff -N src/org/eclipse/core/databinding/validation/constraint/CharacterConstraints.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/databinding/validation/constraint/CharacterConstraints.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,172 @@ +/******************************************************************************* + * 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.validation.constraint; + +import org.eclipse.core.internal.databinding.validation.CharacterValidator; + +/** + * Provides a set of constraints to apply to Characters. + * + *

    + * Instances of this class can be used to define a set of constraints to apply + * to characters. In addition, the validation messages to be issued for the + * individual constraints can be configured. + *

    + * + * @noextend This class is not intended to be subclassed by clients. + * @since 1.3 + */ +public class CharacterConstraints extends + BaseObjectConstraints { + + private String noWhitespaceMessage = null; + + private String noSpaceMessage = null; + + private String letterMessage = null; + + private String digitMessage = null; + + private String letterOrDigitMessage = null; + + /** + * Adds a validator ensuring that the character is no + * {@link Character#isWhitespace(char) whitespace}. Uses the current + * {@link #noWhitespaceMessage(String) validation message}. + * + * @return This constraints instance for method chaining. + */ + public CharacterConstraints noWhitespace() { + addValidator(CharacterValidator.noWhitespace(noWhitespaceMessage)); + return this; + } + + /** + * Sets the validation message for the {@link #noWhitespace()} constraint. + * + * @param message + * The validation message for the {@link #noWhitespace()} + * constraint. + * @return This constraints instance for method chaining. + * + * @see #noWhitespace() + */ + public CharacterConstraints noWhitespaceMessage(String message) { + this.noWhitespaceMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the character is no + * {@link Character#isSpaceChar(char) space}. Uses the current + * {@link #noSpaceMessage(String) validation message}. + * + * @return This constraints instance for method chaining. + */ + public CharacterConstraints noSpace() { + addValidator(CharacterValidator.noSpace(noSpaceMessage)); + return this; + } + + /** + * Sets the validation message for the {@link #noSpace()} constraint. + * + * @param message + * The validation message for the {@link #noSpace()} constraint. + * @return This constraints instance for method chaining. + * + * @see #noSpace() + */ + public CharacterConstraints noSpaceMessage(String message) { + this.noSpaceMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the character is a + * {@link Character#isLetter(char) letter}. Uses the current + * {@link #letterMessage(String) validation message}. + * + * @return This constraints instance for method chaining. + */ + public CharacterConstraints letter() { + addValidator(CharacterValidator.letter(letterMessage)); + return this; + } + + /** + * Sets the validation message for the {@link #letter()} constraint. + * + * @param message + * The validation message for the {@link #letter()} constraint. + * @return This constraints instance for method chaining. + * + * @see #letter() + */ + public CharacterConstraints letterMessage(String message) { + this.letterMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the character is a + * {@link Character#isDigit(char) digit}. Uses the current + * {@link #digitMessage(String) validation message}. + * + * @return This constraints instance for method chaining. + */ + public CharacterConstraints digit() { + addValidator(CharacterValidator.digit(digitMessage)); + return this; + } + + /** + * Sets the validation message for the {@link #digit()} constraint. + * + * @param message + * The validation message for the {@link #digit()} constraint. + * @return This constraints instance for method chaining. + * + * @see #digit() + */ + public CharacterConstraints digitMessage(String message) { + this.digitMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the character is a + * {@link Character#isLetterOrDigit(char) letter or digit}. Uses the current + * {@link #letterOrDigitMessage(String) validation message}. + * + * @return This constraints instance for method chaining. + */ + public CharacterConstraints letterOrDigit() { + addValidator(CharacterValidator.letterOrDigit(letterOrDigitMessage)); + return this; + } + + /** + * Sets the validation message for the {@link #letterOrDigit()} constraint. + * + * @param message + * The validation message for the {@link #letterOrDigit()} + * constraint. + * @return This constraints instance for method chaining. + * + * @see #letterOrDigit() + */ + public CharacterConstraints letterOrDigitMessage(String message) { + this.letterOrDigitMessage = message; + return this; + } +} Index: src/org/eclipse/core/databinding/validation/constraint/DateConstraints.java =================================================================== RCS file: src/org/eclipse/core/databinding/validation/constraint/DateConstraints.java diff -N src/org/eclipse/core/databinding/validation/constraint/DateConstraints.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/databinding/validation/constraint/DateConstraints.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,233 @@ +/******************************************************************************* + * 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.validation.constraint; + +import java.util.Date; + +import org.eclipse.core.internal.databinding.validation.DateRangeValidator; + +import com.ibm.icu.text.DateFormat; + +/** + * Provides a set of constraints to apply to Dates. + * + *

    + * Instances of this class can be used to define a set of constraints to apply + * to dates. In addition, the validation messages to be issued for the + * individual constraints as well as the way in which model objects are + * formatted in the validation messages can be configured. + *

    + * + * @see Date + * + * @noextend This class is not intended to be subclassed by clients. + * @since 1.3 + */ +public class DateConstraints extends + BaseObjectConstraints { + + private DateFormat dateFormat = DateFormat.getDateInstance(); + + private String afterMessage = null; + + private String afterEqualMessage = null; + + private String beforeMessage = null; + + private String beforeEqualMessage = null; + + private String rangeMessage = null; + + /** + * Sets the format to use when formatting a date to be included in any of + * the validation messages. + * + * @param format + * The format to use for displaying dates in validation messages. + * @return This constraints instance for method chaining. + */ + public DateConstraints dateFormat(DateFormat format) { + this.dateFormat = format; + return this; + } + + /** + * Adds a validator ensuring that the input date is after the given date. + * Uses the current {@link #afterMessage(String) validation message} and + * {@link #dateFormat(DateFormat) date format}. + * + * @param date + * The date which the input date must be after. + * @return This constraints instance for method chaining. + * + * @see Date#after(Date) + */ + public DateConstraints after(Date date) { + addValidator(DateRangeValidator.after(date, afterMessage, dateFormat)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #after(Date)} + * constraint. + * + * @param message + * The validation message pattern for the {@link #after(Date)} + * constraint. Can be parameterized with the date ({0}) which the + * input date must be after. + * @return This constraints instance for method chaining. + * + * @see #after(Date) + */ + public DateConstraints afterMessage(String message) { + this.afterMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the input date is after or on the given + * date. Uses the current {@link #afterEqualMessage(String) validation + * message} and {@link #dateFormat(DateFormat) date format}. + * + * @param date + * The date which the input date must be after or on. + * @return This constraints instance for method chaining. + * + * @see Date#after(Date) + * @see Date#equals(Object) + */ + public DateConstraints afterEqual(Date date) { + addValidator(DateRangeValidator.afterEqual(date, afterEqualMessage, + dateFormat)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #afterEqual(Date)} + * constraint. + * + * @param message + * The validation message pattern for the + * {@link #afterEqual(Date)} constraint. Can be parameterized + * with the date ({0}) which the input date must be after or on. + * @return This constraints instance for method chaining. + * + * @see #afterEqual(Date) + */ + public DateConstraints afterEqualMessage(String message) { + this.afterEqualMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the input date is before the given date. + * Uses the current {@link #beforeMessage(String) validation message} and + * {@link #dateFormat(DateFormat) date format}. + * + * @param date + * The date which the input date must be before + * @return This constraints instance for method chaining. + * + * @see Date#before(Date) + */ + public DateConstraints before(Date date) { + addValidator(DateRangeValidator.before(date, beforeMessage, dateFormat)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #before(Date)} + * constraint. + * + * @param message + * The validation message pattern for the {@link #before(Date)} + * constraint. Can be parameterized with the date ({0}) which the + * input date must be before. + * @return This constraints instance for method chaining. + * + * @see #before(Date) + */ + public DateConstraints beforeMessage(String message) { + this.beforeMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the input date is before or on the given + * date. Uses the current {@link #beforeEqualMessage(String) validation + * message} and {@link #dateFormat(DateFormat) date format}. + * + * @param date + * The date which the input date must be before or on. + * @return This constraints instance for method chaining. + * + * @see Date#before(Date) + * @see Date#equals(Object) + */ + public DateConstraints beforeEqual(Date date) { + addValidator(DateRangeValidator.beforeEqual(date, beforeEqualMessage, + dateFormat)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #beforeEqual(Date)} + * constraint. + * + * @param message + * The validation message pattern for the + * {@link #beforeEqual(Date)} constraint. Can be parameterized + * ({0}) with the date which the input date must be before or on. + * @return This constraints instance for method chaining. + * + * @see #beforeEqual(Date) + */ + public DateConstraints beforeEqualMessage(String message) { + this.beforeEqualMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the input date is within the given range. + * Uses the current {@link #rangeMessage(String) validation message} and + * {@link #dateFormat(DateFormat) date format}. + * + * @param minDate + * The lower bound of the range (inclusive). + * @param maxDate + * The upper bound of the range (inclusive). + * @return This constraints instance for method chaining. + */ + public DateConstraints range(Date minDate, Date maxDate) { + addValidator(DateRangeValidator.range(minDate, maxDate, rangeMessage, + dateFormat)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #range(Date, Date)} + * constraint. + * + * @param message + * The validation message pattern for the + * {@link #range(Date, Date)} constraint. Can be parameterized + * with the min ({0}) and max ({1}) dates of the range in which + * the input date must lie. + * @return This constraints instance for method chaining. + * + * @see #range(Date, Date) + */ + public DateConstraints rangeMessage(String message) { + this.rangeMessage = message; + return this; + } +} Index: src/org/eclipse/core/internal/databinding/conversion/StringTrimConverter.java =================================================================== RCS file: src/org/eclipse/core/internal/databinding/conversion/StringTrimConverter.java diff -N src/org/eclipse/core/internal/databinding/conversion/StringTrimConverter.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/internal/databinding/conversion/StringTrimConverter.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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 StringTrimConverter extends Converter { + + private final boolean trimToNull; + + /** + * + * @param trimToNull + */ + public StringTrimConverter(boolean trimToNull) { + super(String.class, String.class); + this.trimToNull = trimToNull; + } + + public Object convert(Object fromObject) { + String string = (String) fromObject; + + if (string != null) { + string = string.trim(); + + if (trimToNull && string.length() == 0) { + string = null; + } + } + + return string; + } +} Index: src/org/eclipse/core/databinding/editing/BooleanEditing.java =================================================================== RCS file: src/org/eclipse/core/databinding/editing/BooleanEditing.java diff -N src/org/eclipse/core/databinding/editing/BooleanEditing.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/databinding/editing/BooleanEditing.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,161 @@ +/******************************************************************************* + * 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.validation.constraint.BooleanConstraints; +import org.eclipse.core.databinding.validation.constraint.StringConstraints; +import org.eclipse.core.internal.databinding.conversion.BooleanToStringConverter; +import org.eclipse.core.internal.databinding.conversion.StringToBooleanConverter; +import org.eclipse.core.internal.databinding.validation.StringToBooleanValidator; + +/** + * @noextend This class is not intended to be subclassed by clients. + * @since 1.3 + */ +public class BooleanEditing extends Editing { + + /** + * Creates a new editing object for booleans. + * + * @param trueValues + * The set of strings representing a true value. + * @param falseValues + * The set of strings representing a false value. + * @param parseErrorMessage + * The validation message issued in case the input string cannot + * be parsed to a boolean. + * + * @noreference This constructor is not intended to be referenced by + * clients. + */ + protected BooleanEditing(String[] trueValues, String[] falseValues, + String parseErrorMessage) { + StringToBooleanConverter targetConverter = new StringToBooleanConverter(); + BooleanToStringConverter modelConverter = new BooleanToStringConverter( + Boolean.class); + + StringToBooleanValidator targetValidator = new StringToBooleanValidator(); + if (parseErrorMessage != null) { + targetValidator.setParseErrorMessage(parseErrorMessage); + } + + if (trueValues != null && falseValues != null) { + targetValidator.setSourceStrings(trueValues, falseValues); + targetConverter.setSourceStrings(trueValues, falseValues); + modelConverter.setTargetStrings(trueValues[0], falseValues[0]); + } + + setTargetConverter(targetConverter); + setModelConverter(modelConverter); + targetConstraints().addValidator(targetValidator); + } + + /** + * Creates a new editing object which uses the default set of string + * representations for boolean values for parsing and displaying. + * + * @return The new editing object for the default set of string + * representations for boolean values. + */ + public static BooleanEditing withDefaults() { + return withDefaults(null); + } + + /** + * Creates a new editing object which uses the default set of string + * representations for boolean values for parsing and displaying. Uses the + * specified custom validation message. + * + * @param parseErrorMessage + * The validation message issued in case the input string cannot + * be parsed to a boolean. + * @return The new editing object for the default set of string + * representations for boolean values. + */ + public static BooleanEditing withDefaults(String parseErrorMessage) { + return new BooleanEditing(null, null, parseErrorMessage); + } + + /** + * Creates a new editing object which uses the given set of string + * representations for boolean values for parsing and displaying. + * + *

    + * For parsing, the given string values will be considered valid + * representations of {@code true} and {@code false}, respectively. For + * displaying, the first element of the respective array will be used for + * representing the corresponding boolean value. + *

    + * + * @param trueValues + * The set of strings representing a true value. + * @param falseValues + * The set of strings representing a false value. + * @return The new editing object for the given set of string + * representations for boolean values. + */ + public static BooleanEditing forStringValues(String[] trueValues, + String[] falseValues) { + return forStringValues(trueValues, falseValues, null); + } + + /** + * Creates a new editing object which uses the given set of string + * representations for boolean values for parsing and displaying. Uses the + * specified custom validation message. + * + *

    + * For parsing, the given string values will be considered valid + * representations of {@code true} and {@code false}, respectively. For + * displaying, the first element of the respective array will be used for + * representing the corresponding boolean value. + *

    + * + * @param trueValues + * The set of strings representing a true value. + * @param falseValues + * The set of strings representing a false value. + * @param parseErrorMessage + * The validation message issued in case the input string cannot + * be parsed to a boolean. + * @return The new editing object for the given set of string + * representations for boolean values. + */ + public static BooleanEditing forStringValues(String[] trueValues, + String[] falseValues, String parseErrorMessage) { + return new BooleanEditing(trueValues, falseValues, parseErrorMessage); + } + + public StringConstraints targetConstraints() { + return (StringConstraints) super.targetConstraints(); + } + + public BooleanConstraints modelConstraints() { + return (BooleanConstraints) super.modelConstraints(); + } + + public BooleanConstraints beforeSetModelConstraints() { + return (BooleanConstraints) super.beforeSetModelConstraints(); + } + + protected StringConstraints createTargetConstraints() { + return new StringConstraints(); + } + + protected BooleanConstraints createModelConstraints() { + return new BooleanConstraints(); + } + + protected BooleanConstraints createBeforeSetModelConstraints() { + return new BooleanConstraints(); + } +} Index: src/org/eclipse/core/databinding/validation/constraint/BooleanConstraints.java =================================================================== RCS file: src/org/eclipse/core/databinding/validation/constraint/BooleanConstraints.java diff -N src/org/eclipse/core/databinding/validation/constraint/BooleanConstraints.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/databinding/validation/constraint/BooleanConstraints.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,30 @@ +/******************************************************************************* + * 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.validation.constraint; + +/** + * Provides a set of constraints to apply to Booleans. + * + *

    + * Instances of this class can be used to define a set of constraints to apply + * to booleans. In addition, the validation messages to be issued for the + * individual constraints can be configured. + *

    + * + * @noextend This class is not intended to be subclassed by clients. + * @since 1.3 + */ +public class BooleanConstraints extends + BaseObjectConstraints { + + // no additional constraints than those inherited from the subclass +} Index: src/org/eclipse/core/internal/databinding/validation/DecimalPrecisionValidator.java =================================================================== RCS file: src/org/eclipse/core/internal/databinding/validation/DecimalPrecisionValidator.java diff -N src/org/eclipse/core/internal/databinding/validation/DecimalPrecisionValidator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/internal/databinding/validation/DecimalPrecisionValidator.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,108 @@ +/******************************************************************************* + * 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.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; + +import com.ibm.icu.math.BigDecimal; +import com.ibm.icu.text.NumberFormat; + +/** + * Provides validations for the precision of decimal numbers. + * + * @since 1.3 + */ +public class DecimalPrecisionValidator implements IValidator { + + private static final String MAX_PRECISION_VALIDATION_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_DECIMAL_MAX_PRECISION); + + private final int maxPrecision; + + private final String validationMessage; + + private String formattedValidationMessage; + + private final NumberFormat precisionFormat; + + private DecimalPrecisionValidator(int maxPrecision, + String validationMessage, NumberFormat precisionFormat) { + this.maxPrecision = maxPrecision; + this.validationMessage = validationMessage != null ? validationMessage + : MAX_PRECISION_VALIDATION_MESSAGE; + this.precisionFormat = precisionFormat; + } + + /** + * Creates a validator which checks that an input decimal has a precision + * which is less or equal to the given precision. + * + * @param maxPrecision + * The maximum precision to enforce on the input decimal. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the maximum precision to be enforced. + * @param precisionFormat + * The display format to use for formatting the precision in the + * validation message. + * @return The validator instance. + */ + public static DecimalPrecisionValidator max(int maxPrecision, + String validationMessage, NumberFormat precisionFormat) { + return new DecimalPrecisionValidator(maxPrecision, validationMessage, + precisionFormat); + } + + public IStatus validate(Object value) { + if (value != null) { + Number number = (Number) value; + final int precision; + if (number instanceof BigDecimal) { + precision = computePrecision(((BigDecimal) number)); + } else { + precision = computePrecision(new BigDecimal(number + .doubleValue())); + } + if (precision > maxPrecision) { + return ValidationStatus.error(getFormattedValidationMessage()); + } + } + return ValidationStatus.ok(); + } + + private static int computePrecision(BigDecimal bigDecimal) { + // In Java 1.4, there is no direct API for retrieving the precision of + // a BigDecimal so we use the length of the string representation of the + // unscaled BigInteger value of the BigDecimal to actually compute it. + return bigDecimal.unscaledValue().abs().toString().length(); + } + + private synchronized String getFormattedValidationMessage() { + if (formattedValidationMessage == null) { + formattedValidationMessage = MessageFormat.format( + validationMessage, getValidationMessageArguments()); + } + return formattedValidationMessage; + } + + private String[] getValidationMessageArguments() { + synchronized (precisionFormat) { + return new String[] { precisionFormat.format(new Integer( + maxPrecision)) }; + } + } +} Index: src/org/eclipse/core/internal/databinding/validation/StringToBigDecimalValidator.java =================================================================== RCS file: src/org/eclipse/core/internal/databinding/validation/StringToBigDecimalValidator.java diff -N src/org/eclipse/core/internal/databinding/validation/StringToBigDecimalValidator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/internal/databinding/validation/StringToBigDecimalValidator.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2007 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; + +/** + * Validates that a string is of the appropriate format for a BigDecimal. + * + * @since 1.3 + */ +public class StringToBigDecimalValidator extends + AbstractStringToNumberValidator { + + /** + * @param converter + */ + public StringToBigDecimalValidator(NumberFormatConverter converter) { + super(converter, null, null); + } + + protected boolean isInRange(Number number) { + return true; + } +} Index: src/org/eclipse/core/internal/databinding/validation/StringToBooleanValidator.java =================================================================== RCS file: src/org/eclipse/core/internal/databinding/validation/StringToBooleanValidator.java diff -N src/org/eclipse/core/internal/databinding/validation/StringToBooleanValidator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/internal/databinding/validation/StringToBooleanValidator.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,117 @@ +/******************************************************************************* + * 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.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.StringTokenizer; + +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; + +/** + * @since 1.3 + */ +public class StringToBooleanValidator implements IValidator { + + // TODO: Some of this code is duplicated from StringToBooleanPrimitiveConverter! + private static final String[] DEFAULT_TRUE_VALUES; + private static final String[] DEFAULT_FALSE_VALUES; + + static { + String delimiter = BindingMessages.getString(BindingMessages.VALUE_DELIMITER); + String values = BindingMessages.getString(BindingMessages.TRUE_STRING_VALUES); + DEFAULT_TRUE_VALUES = valuesToSortedArray(delimiter, values); + + values = BindingMessages.getString(BindingMessages.FALSE_STRING_VALUES); + DEFAULT_FALSE_VALUES = valuesToSortedArray(delimiter, values); + } + + private String[] trueValues = DEFAULT_TRUE_VALUES; + private String[] falseValues = DEFAULT_FALSE_VALUES; + + private String parseErrorMessage = BindingMessages.getString(BindingMessages.VALIDATE_INVALID_BOOLEAN_STRING); + + /** + * Returns a sorted array with all values converted to upper case. + * + * @param delimiter + * @param values + * @return sorted array of values + */ + private static String[] valuesToSortedArray(String delimiter, String values) { + List list = new LinkedList(); + StringTokenizer tokenizer = new StringTokenizer(values, delimiter); + while (tokenizer.hasMoreTokens()) { + list.add(tokenizer.nextToken().toUpperCase()); + } + + String[] array = (String[]) list.toArray(new String[list.size()]); + Arrays.sort(array); + + return array; + } + + /** + * Sets the validation message to be used in case the source string does not + * represent a valid boolean value. + * + * @param message + * The validation message to be used in case the source string + * does not represent a valid boolean value. + */ + public final void setParseErrorMessage(String message) { + this.parseErrorMessage = message; + } + + /** + * Sets the string values which are considered to represent a + * true and false value, respectively. + * + *

    + * Note that the capitalization of the provided strings is ignored. + *

    + * + * @param trueValues + * The set of strings representing a true value. + * @param falseValues + * The set of strings representing a false value. + */ + public final void setSourceStrings(String[] trueValues, String[] falseValues) { + this.trueValues = new String[trueValues.length]; + for (int i = 0; i < trueValues.length; i++) { + this.trueValues[i] = trueValues[i].toUpperCase(); + } + Arrays.sort(this.trueValues); // for binary search + + this.falseValues = new String[falseValues.length]; + for (int i = 0; i < falseValues.length; i++) { + this.falseValues[i] = falseValues[i].toUpperCase(); + } + Arrays.sort(this.falseValues); // for binary search + } + + public IStatus validate(Object value) { + String source = (String) value; + if (source != null && source.length() != 0) { + source = source.toUpperCase(); + if (Arrays.binarySearch(trueValues, source) < 0 + && Arrays.binarySearch(falseValues, source) < 0) { + return ValidationStatus.error(parseErrorMessage); + } + } + return ValidationStatus.ok(); + } +} Index: src/org/eclipse/core/databinding/validation/constraint/Constraints.java =================================================================== RCS file: src/org/eclipse/core/databinding/validation/constraint/Constraints.java diff -N src/org/eclipse/core/databinding/validation/constraint/Constraints.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/databinding/validation/constraint/Constraints.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,228 @@ +/******************************************************************************* + * 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.validation.constraint; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.databinding.util.Policy; +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; + +/** + * This class allows to aggregate a set of {@link IValidator validators} and to + * combine their individual validation statuses in different ways. + * + *

    + * The set of validators to be applied can be {@link #addValidator(IValidator) + * added} to a constraints object from which an aggregate validator can then be + * {@link #createValidator() created} which combines the statuses of all the + * accumulated validators as dictated by the specified + * {@link #aggregationPolicy(Aggregation) aggregation policy}. + *

    + * + * @param + * + * @see IValidator + * + * @since 1.3 + */ +public class Constraints { + + /** + * The supported aggregation policies to apply to the individual validations + * which constitute the set of constraints. + */ + public static enum Aggregation { + + /** + * Constant denoting an aggregation strategy that merges multiple non-OK + * status objects in a {@link MultiStatus}. Returns an OK status result + * if all statuses from the set of validators to apply are an OK status. + * Returns a single status if there is only one non-OK status. + * + * @see #aggregationPolicy(Aggregation) + */ + MERGED, + + /** + * Constant denoting an aggregation strategy that always returns the + * most severe status from the set of validators to apply. If + * there is more than one status at the same severity level, it picks + * the first one it encounters. + * + * @see #aggregationPolicy(Aggregation) + */ + MAX_SEVERITY, + + /** + * Constant denoting an aggregation strategy that always returns the + * least severe status from the set of validators to apply. If + * there is more than one status at the same severity level, it picks + * the first one it encounters. + * + * @see #aggregationPolicy(Aggregation) + */ + MIN_SEVERITY; + } + + // Will be initialized lazily. + private List validators = null; + + private IValidator cachedValidator = null; + + private Aggregation aggregationPolicy = Aggregation.MAX_SEVERITY; + + /** + * Adds a custom validator to the set of constraints to apply. + * + * @param validator + * The custom validator to add to the set of constraints. + * @return This constraints instance for method chaining. + */ + public Constraints addValidator(IValidator validator) { + if (validators == null) { + validators = new ArrayList(2); + } + validators.add(validator); + cachedValidator = null; + return this; + } + + /** + * Sets the aggregation policy to apply to the individual validations which + * constitute this set of constraints. + * + * @param policy + * The validation aggregation policy to set. Must be one of + * {@link Aggregation#MERGED}, {@link Aggregation#MAX_SEVERITY} + * or {@link Aggregation#MIN_SEVERITY}. + * @return This constraints instance for method chaining. + * + * @see Aggregation#MERGED + * @see Aggregation#MAX_SEVERITY + * @see Aggregation#MIN_SEVERITY + */ + public Constraints aggregationPolicy(Aggregation policy) { + this.aggregationPolicy = policy; + cachedValidator = null; + return this; + } + + /** + * Creates a validator which enforces the current set of constraints. + * + *

    + * Note that this method will return null in case the set of + * constraints to apply is empty. + *

    + * + * @return A validator which enforces the current set of constraints. May be + * null. + */ + public final IValidator createValidator() { + if (validators != null && !validators.isEmpty()) { + if (cachedValidator == null) { + if (validators.size() == 1) { + cachedValidator = validators.get(0); + } else { + IValidator[] currentValidators = validators + .toArray(new IValidator[validators.size()]); + cachedValidator = new ConstraintsValidator( + currentValidators, aggregationPolicy); + } + } + return cachedValidator; + } + return null; + } + + private static final class ConstraintsValidator implements IValidator { + + private final IValidator[] validators; + + private final Aggregation aggregationPolicy; + + public ConstraintsValidator(IValidator[] validators, + Aggregation aggregationPolicy) { + this.validators = validators; + this.aggregationPolicy = aggregationPolicy; + } + + public IStatus validate(Object value) { + switch (aggregationPolicy) { + case MERGED: + return validateMerged(value); + case MAX_SEVERITY: + return validateMaxSeverity(value); + case MIN_SEVERITY: + return validateMinSeverity(value); + default: + throw new IllegalArgumentException( + "Unsupported aggregationPolicy: " + aggregationPolicy); //$NON-NLS-1$ + } + } + + private IStatus validateMerged(Object value) { + List statuses = new ArrayList(); + for (IValidator validator : validators) { + IStatus status = validator.validate(value); + if (!status.isOK()) { + statuses.add(status); + } + } + + if (statuses.size() == 1) { + return statuses.get(0); + } + + if (!statuses.isEmpty()) { + MultiStatus result = new MultiStatus(Policy.JFACE_DATABINDING, + 0, BindingMessages + .getString(BindingMessages.MULTIPLE_PROBLEMS), + null); + for (IStatus status : statuses) { + result.merge(status); + } + return result; + } + + return Status.OK_STATUS; + } + + private IStatus validateMaxSeverity(Object value) { + IStatus maxStatus = Status.OK_STATUS; + for (IValidator validator : validators) { + IStatus status = validator.validate(value); + if (status.getSeverity() > maxStatus.getSeverity()) { + maxStatus = status; + } + } + return maxStatus; + } + + private IStatus validateMinSeverity(Object value) { + IStatus minStatus = null; + for (IValidator validator : validators) { + IStatus status = validator.validate(value); + if (minStatus == null + || status.getSeverity() < minStatus.getSeverity()) { + minStatus = status; + } + } + return minStatus != null ? minStatus : Status.OK_STATUS; + } + } +} Index: src/org/eclipse/core/databinding/validation/constraint/StringConstraints.java =================================================================== RCS file: src/org/eclipse/core/databinding/validation/constraint/StringConstraints.java diff -N src/org/eclipse/core/databinding/validation/constraint/StringConstraints.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/databinding/validation/constraint/StringConstraints.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,245 @@ +/******************************************************************************* + * 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.validation.constraint; + +import java.util.regex.Pattern; + +import org.eclipse.core.internal.databinding.validation.NonEmptyStringValidator; +import org.eclipse.core.internal.databinding.validation.StringLengthValidator; +import org.eclipse.core.internal.databinding.validation.StringRegexValidator; + +import com.ibm.icu.text.NumberFormat; + +/** + * Provides a set of constraints to apply to Strings. + * + *

    + * Instances of this class can be used to define a set of constraints to apply + * to strings. In addition, the validation messages to be issued for the + * individual constraints as well as the way in which model objects are + * formatted in the validation messages can be configured. + *

    + * + * @noextend This class is not intended to be subclassed by clients. + * @since 1.3 + */ +public class StringConstraints extends + BaseObjectConstraints { + + private NumberFormat lengthFormat = NumberFormat.getIntegerInstance(); + + private String nonEmptyMessage = null; + + private String minLengthMessage = null; + + private String maxLengthMessage = null; + + private String lengthRangeMessage = null; + + private String matchesMessage = null; + + /** + * Adds a validator ensuring that the input integer is not empty. Uses the + * current {@link #nonEmptyMessage(String) validation message}. + * + * @return This constraints instance for method chaining. + */ + public StringConstraints nonEmpty() { + addValidator(new NonEmptyStringValidator(nonEmptyMessage)); + return this; + } + + /** + * Sets the validation message for the {@link #nonEmpty()} constraint. + * + * @param message + * The validation message for the {@link #nonEmpty()} constraint. + * @return This constraints instance for method chaining. + * + * @see #nonEmpty() + */ + public StringConstraints nonEmptyMessage(String message) { + this.nonEmptyMessage = message; + return this; + } + + /** + * Sets the format to use when formatting the string length in any of the + * validation messages stemming from a length constraint. + * + * @param format + * The format to use for displaying the string length in + * validation messages stemming from a length constraint. + * messages. + * @return This constraints instance for method chaining. + * + * @see #minLength(int) + * @see #maxLength(int) + * @see #lengthRange(int, int) + */ + public StringConstraints lengthFormat(NumberFormat format) { + this.lengthFormat = format; + return this; + } + + /** + * Adds a validator ensuring that the input string has at least the + * specified amount of characters. Uses the current + * {@link #minLengthMessage(String) validation message} and + * {@link #lengthFormat(NumberFormat) length format}. + * + * @param minLength + * The minimal length to be enforced on the input string. + * @return This constraints instance for method chaining. + */ + public StringConstraints minLength(int minLength) { + addValidator(StringLengthValidator.min(minLength, minLengthMessage, + lengthFormat)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #minLength(int)} + * constraint. + * + * @param message + * The validation message pattern for the {@link #minLength(int)} + * constraint. Can be parameterized with the minimum string + * length ({0}). + * @return This constraints instance for method chaining. + * + * @see #minLength(int) + */ + public StringConstraints minLengthMessage(String message) { + this.minLengthMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the input string has not more than the + * specified amount of characters. Uses the current + * {@link #maxLengthMessage(String) validation message} and + * {@link #lengthFormat(NumberFormat) length format}. + * + * @param maxLength + * The maximum length to be enforced on the input string. + * @return This constraints instance for method chaining. + */ + public StringConstraints maxLength(int maxLength) { + addValidator(StringLengthValidator.max(maxLength, maxLengthMessage, + lengthFormat)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #maxLength(int)} + * constraint. + * + * @param message + * The validation message pattern for the {@link #maxLength(int)} + * constraint. Can be parameterized with the maximum string + * length ({0}). + * @return This constraints instance for method chaining. + * + * @see #maxLength(int) + */ + public StringConstraints maxLengthMessage(String message) { + this.maxLengthMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the length of the input string is between + * the given bounds. Uses the current {@link #lengthRangeMessage(String) + * validation message} and {@link #lengthFormat(NumberFormat) length format} + * . + * + * @param minLength + * The minimal length to be enforced on the input string. + * @param maxLength + * The maximum length to be enforced on the input string. + * @return This constraints instance for method chaining. + */ + public StringConstraints lengthRange(int minLength, int maxLength) { + addValidator(StringLengthValidator.range(minLength, maxLength, + lengthRangeMessage, lengthFormat)); + return this; + } + + /** + * Sets the validation message pattern for the + * {@link #lengthRange(int, int)} constraint. + * + * @param message + * The validation message pattern for the + * {@link #lengthRange(int, int)} constraint. Can be + * parameterized with the minimum ({0}) and maximum ({1}) string + * lengths. + * @return This constraints instance for method chaining. + * + * @see #lengthRange(int, int) + */ + public StringConstraints lengthRangeMessage(String message) { + this.lengthRangeMessage = message; + return this; + } + + /** + * Adds a 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 constraints instance for method chaining. + * + * @see Pattern#matches(String, CharSequence) + */ + public StringConstraints matches(String regex) { + addValidator(new StringRegexValidator(regex, matchesMessage)); + return this; + } + + /** + * Adds a validator ensuring that the input string + * {@link Pattern#matches(String, CharSequence) matches} the given pattern. + * Uses the current {@link #matchesMessage(String) validation message}. + * + * @param pattern + * The pattern which the input string must match. + * @return This constraints instance for method chaining. + * + * @see Pattern#matches(String, CharSequence) + */ + public StringConstraints matches(Pattern pattern) { + addValidator(new StringRegexValidator(pattern, matchesMessage)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #matches(String)} + * constraint. + * + * @param message + * The validation message pattern for the + * {@link #matches(String)} constraint. Can be parameterized with + * the pattern string ({0}) which the input integer must match. + * @return This constraints instance for method chaining. + * + * @see #matches(String) + */ + public StringConstraints matchesMessage(String message) { + this.matchesMessage = 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,970 @@ +/******************************************************************************* + * 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.Binding; +import org.eclipse.core.databinding.DataBindingContext; +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.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.databinding.validation.constraint.Constraints; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; + +/** + * @param + * @param + * + * @since 1.3 + */ +public class Editing { + + private Constraints targetConstraints; + + private Constraints modelConstraints; + + private Constraints beforeSetModelConstraints; + + private IConverter targetConverter; + + private IConverter modelConverter; + + /** + * Default constructor which creates an unconfigured editing instance. + */ + protected Editing() { + // empty editing instance + } + + /** + * Creates an unconfigured editing instance. + * + * @return A new, unconfigured editing instance. + * + * @param + * @param + */ + public static Editing create() { + return new Editing(); + } + + /** + * Creates an editing instance with the given target-to-model and + * model-to-target converters. + * + * @param t2mConverter + * The target-to-model converter to set. May be null + * . + * @param m2tConverter + * The model-to-target converter to set. May be null + * . + * @return A new editing instance with the given target-to-model and + * model-to-target converters. + * + * @param + * @param + */ + public static Editing withConverters( + IConverter t2mConverter, IConverter m2tConverter) { + Editing editing = new Editing(); + editing.setTargetConverter(t2mConverter); + editing.setModelConverter(m2tConverter); + return editing; + } + + /** + * Returns the target constraints to apply. + * + * @return The target constraints to apply. + * + * @see #createTargetConstraints() + */ + public Constraints targetConstraints() { + if (targetConstraints == null) { + targetConstraints = createTargetConstraints(); + } + return targetConstraints; + } + + /** + * Returns the model constraints to apply. + * + * @return The model constraints to apply. + * + * @see #createModelConstraints() + */ + public Constraints modelConstraints() { + if (modelConstraints == null) { + modelConstraints = createModelConstraints(); + } + return modelConstraints; + } + + /** + * Returns the before-set model constraints to apply. + * + * @return The before-set model constraints to apply. + * + * @see #createBeforeSetModelConstraints() + */ + public Constraints beforeSetModelConstraints() { + if (beforeSetModelConstraints == null) { + beforeSetModelConstraints = createBeforeSetModelConstraints(); + } + return beforeSetModelConstraints; + } + + /** + * Creates the target constraints to apply. + * + *

    + * The constraints object created by this method will be exposed as the + * {@link #targetConstraints()} of this editing object. By default, this + * method returns a plain {@link Constraints} object. + *

    + * + *

    + * This method will be called lazily the first time the target constraints + * are accessed. Subclasses may overwrite this method in order to create a + * custom constraints object which will then be typically exposed in the + * subclass as API in order to allow for a typesafe access. + *

    + * + * @return The target constraints to apply. + * + * @see #targetConstraints() + */ + protected Constraints createTargetConstraints() { + return new Constraints(); + } + + /** + * Creates the model constraints to apply. + * + *

    + * The constraints object created by this method will be exposed as the + * {@link #modelConstraints()} of this editing object. By default, this + * method returns a plain {@link Constraints} object. + *

    + * + *

    + * This method will be called lazily the first time the model constraints + * are accessed. Subclasses may overwrite this method in order to create a + * custom constraints object which will then be typically exposed in the + * subclass as API in order to allow for a typesafe access. + *

    + * + * @return The model constraints to apply. + * + * @see #modelConstraints() + */ + protected Constraints createModelConstraints() { + return new Constraints(); + } + + /** + * Creates the before-set model constraints to apply. + * + *

    + * The constraints object created by this method will be exposed as the + * {@link #beforeSetModelConstraints()} of this editing object. By default, + * this method returns a plain {@link Constraints} object. + *

    + * + *

    + * This method will be called lazily the first time the before-set model + * constraints are accessed. Subclasses may overwrite this method in order + * to create a custom constraints object which will then be typically + * exposed in the subclass as API in order to allow for a typesafe access. + *

    + * + * @return The before-set model constraints to apply. + * + * @see #beforeSetModelConstraints() + */ + protected Constraints createBeforeSetModelConstraints() { + return new Constraints(); + } + + /** + * Sets the target-to-model converter for this editing instance. + * + * @param converter + * The target-to-model converter to set. + */ + protected final void setTargetConverter(IConverter converter) { + this.targetConverter = converter; + } + + /** + * Sets the model-to-target converter for this editing instance. + * + * @param converter + * The model-to-target converter to set. + */ + 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() + * @see #adaptT2MValueStrategy(UpdateValueStrategy) + */ + public final UpdateValueStrategy createT2MValueStrategy() { + return adaptT2MValueStrategy(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) + * @see #adaptT2MValueStrategy(UpdateValueStrategy) + */ + public final UpdateValueStrategy createT2MValueStrategy(int updatePolicy) { + return adaptT2MValueStrategy(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() + * @see #adaptM2TValueStrategy(UpdateValueStrategy) + */ + public final UpdateValueStrategy createM2TValueStrategy() { + return adaptM2TValueStrategy(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) + * @see #adaptM2TValueStrategy(UpdateValueStrategy) + */ + public final UpdateValueStrategy createM2TValueStrategy(int updatePolicy) { + return adaptM2TValueStrategy(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() + * @see #adaptT2MListStrategy(UpdateListStrategy) + */ + public final UpdateListStrategy createT2MListStrategy() { + return adaptT2MListStrategy(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) + * @see #adaptT2MListStrategy(UpdateListStrategy) + */ + public final UpdateListStrategy createT2MListStrategy(int updatePolicy) { + return adaptT2MListStrategy(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() + * @see #adaptM2TListStrategy(UpdateListStrategy) + */ + public final UpdateListStrategy createM2TListStrategy() { + return adaptM2TListStrategy(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) + * @see #adaptM2TListStrategy(UpdateListStrategy) + */ + public final UpdateListStrategy createM2TListStrategy(int updatePolicy) { + return adaptM2TListStrategy(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() + * @see #adaptT2MSetStrategy(UpdateSetStrategy) + */ + public final UpdateSetStrategy createT2MSetStrategy() { + return adaptT2MSetStrategy(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) + * @see #adaptT2MSetStrategy(UpdateSetStrategy) + */ + public final UpdateSetStrategy createT2MSetStrategy(int updatePolicy) { + return adaptT2MSetStrategy(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() + * @see #adaptM2TSetStrategy(UpdateSetStrategy) + */ + public final UpdateSetStrategy createM2TSetStrategy() { + return adaptM2TSetStrategy(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) + * @see #adaptM2TSetStrategy(UpdateSetStrategy) + */ + public final UpdateSetStrategy createM2TSetStrategy(int updatePolicy) { + return adaptM2TSetStrategy(new UpdateSetStrategy(updatePolicy)); + } + + /** + * Configures an existing target-to-model {@link UpdateValueStrategy} with + * the current state of this editing object. + * + *

    + * The configuration is done as follows: + *

      + *
    • + * The {@link Constraints#createValidator() validator} of the + * {@link #targetConstraints() target constraints} is set as the + * {@link UpdateValueStrategy#setAfterGetValidator(IValidator) after-get + * validator} of the update strategy.
    • + *
    • The {@link #setTargetConverter(IConverter) target converter} is set + * as the {@link UpdateValueStrategy#setConverter(IConverter) converter} of + * the update strategy.
    • + *
    • + * The {@link Constraints#createValidator() validator} of the + * {@link #modelConstraints() model constraints} is set as the + * {@link UpdateValueStrategy#setAfterConvertValidator(IValidator) + * after-convert validator} of the update strategy.
    • + *
    • The {@link Constraints#createValidator() validator} of the + * {@link #beforeSetModelConstraints() before-set model constraints} is set + * as the {@link UpdateValueStrategy#setBeforeSetValidator(IValidator) + * before-set validator} of the update strategy.
    • + *
    + *

    + * + * @param updateStrategy + * The {@link UpdateValueStrategy} to configure. + * @return The passed-in, configured target-to-model + * {@link UpdateValueStrategy}. + * + * @see UpdateValueStrategy#setAfterGetValidator(IValidator) + * @see UpdateValueStrategy#setConverter(IConverter) + * @see UpdateValueStrategy#setAfterConvertValidator(IValidator) + * @see UpdateValueStrategy#setBeforeSetValidator(IValidator) + */ + public final UpdateValueStrategy adaptT2MValueStrategy( + UpdateValueStrategy updateStrategy) { + updateStrategy.setAfterGetValidator(createValidator(targetConstraints)); + updateStrategy.setConverter(targetConverter); + updateStrategy + .setAfterConvertValidator(createValidator(modelConstraints)); + updateStrategy + .setBeforeSetValidator(createValidator(beforeSetModelConstraints)); + return updateStrategy; + } + + /** + * Configures an existing model-to-target {@link UpdateValueStrategy} with + * the current state of this editing object by setting the + * {@link #setModelConverter(IConverter) model converter} as the + * {@link UpdateValueStrategy#setConverter(IConverter) converter} of the + * update strategy. + * + * @param updateStrategy + * The {@link UpdateValueStrategy} to configure. + * @return The passed-in, configured model-to-target + * {@link UpdateValueStrategy}. + * + * @see UpdateValueStrategy#setConverter(IConverter) + */ + public final UpdateValueStrategy adaptM2TValueStrategy( + UpdateValueStrategy updateStrategy) { + updateStrategy.setConverter(modelConverter); + return updateStrategy; + } + + /** + * Configures an existing target-to-model {@link UpdateListStrategy} with + * the current state of this editing object by setting the + * {@link #setTargetConverter(IConverter) target converter} as the + * {@link UpdateListStrategy#setConverter(IConverter) converter} of the + * update strategy. + * + * @param updateStrategy + * The {@link UpdateListStrategy} to configure. + * @return The passed-in, configured target-to-model + * {@link UpdateListStrategy}. + * + * @see UpdateListStrategy#setConverter(IConverter) + */ + public final UpdateListStrategy adaptT2MListStrategy( + UpdateListStrategy updateStrategy) { + updateStrategy.setConverter(targetConverter); + return updateStrategy; + } + + /** + * Configures an existing model-to-target {@link UpdateListStrategy} with + * the current state of this editing object by setting the + * {@link #setModelConverter(IConverter) model converter} as the + * {@link UpdateListStrategy#setConverter(IConverter) converter} of the + * update strategy. + * + * @param updateStrategy + * The {@link UpdateListStrategy} to configure. + * @return The passed-in, configured model-to-target + * {@link UpdateListStrategy}. + * + * @see UpdateListStrategy#setConverter(IConverter) + */ + public final UpdateListStrategy adaptM2TListStrategy( + UpdateListStrategy updateStrategy) { + updateStrategy.setConverter(modelConverter); + return updateStrategy; + } + + /** + * Configures an existing target-to-model {@link UpdateListStrategy} with + * the current state of this editing object by setting the + * {@link #setTargetConverter(IConverter) target converter} as the + * {@link UpdateSetStrategy#setConverter(IConverter) converter} of the + * update strategy. + * + * @param updateStrategy + * The {@link UpdateSetStrategy} to configure. + * @return The passed-in, configured target-to-model + * {@link UpdateSetStrategy}. + * + * @see UpdateSetStrategy#setConverter(IConverter) + */ + public final UpdateSetStrategy adaptT2MSetStrategy( + UpdateSetStrategy updateStrategy) { + updateStrategy.setConverter(targetConverter); + return updateStrategy; + } + + /** + * Configures an existing model-to-target {@link UpdateSetStrategy} with the + * current state of this editing object by setting the + * {@link #setModelConverter(IConverter) model converter} as the + * {@link UpdateSetStrategy#setConverter(IConverter) converter} of the + * update strategy. + * + * @param updateStrategy + * The {@link UpdateSetStrategy} to configure. + * @return The passed-in, configured model-to-target + * {@link UpdateSetStrategy}. + * + * @see UpdateSetStrategy#setConverter(IConverter) + */ + public final UpdateSetStrategy adaptM2TSetStrategy( + UpdateSetStrategy updateStrategy) { + updateStrategy.setConverter(modelConverter); + return updateStrategy; + } + + /** + * Converts a target value to a model value by performing the following + * steps: + *
      + *
    • + * Applying all the {@link #targetConstraints() target constraints} to the + * given target value.
    • + *
    • + * {@link #setTargetConverter(IConverter) Converting} the target value to + * the model value.
    • + *
    • + * Applying all the {@link #modelConstraints() model constraints} to the + * converted model value.
    • + *
    • + * Applying all the {@link #beforeSetModelConstraints() before-set model + * constraints} to the converted model value.
    • + *
    + * + *

    + * 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#merge(IStatus) + * aggregated} on the given validationStatus. + *

    + * + * @param targetValue + * The target value to be converted to a model value. + * @param validationStatus + * Aggregate validation status to which to add the validations + * produced during the conversion process. May be + * null. + * @return The converted model value or null in case the + * conversion has been aborted due to a validation error. + */ + public final M convertToModel(T targetValue, MultiStatus validationStatus) { + if (!applyConstraints(targetConstraints, targetValue, validationStatus)) { + return null; + } + + M modelValue = (M) applyConverter(targetConverter, targetValue); + + if (!applyConstraints(modelConstraints, modelValue, validationStatus)) { + return null; + } + + if (!applyConstraints(beforeSetModelConstraints, modelValue, + validationStatus)) { + return null; + } + + return modelValue; + } + + /** + * {@link #setModelConverter(IConverter) Converts} a model value to a target + * value. + * + * @param modelValue + * The model value to be converted to a target value. + * @return The converted target value. + */ + public final T convertToTarget(M modelValue) { + return (T) applyConverter(modelConverter, modelValue); + } + + /** + * Creates a binding between a target and model observable value on the + * given binding context by creating new update strategies which will be + * both configured with the state of this editing object before passing them + * to the binding. + * + *

    + * The target-to-model and model-to-target update strategies for the binding + * will be created by the methods {@link #createT2MValueStrategy()} and + * {@link #createM2TValueStrategy()}, respectively. + *

    + * + * @param dbc + * The binding context on which to create the value binding. + * @param targetObservableValue + * The target observable value of the binding. + * @param modelObservableValue + * The model observable value of the binding. + * @return The new value binding. + * + * @see #createT2MValueStrategy() + * @see #createM2TValueStrategy() + * @see DataBindingContext#bindValue(IObservableValue, IObservableValue, + * UpdateValueStrategy, UpdateValueStrategy) + */ + public final Binding bindValue(DataBindingContext dbc, + IObservableValue targetObservableValue, + IObservableValue modelObservableValue) { + return dbc.bindValue(targetObservableValue, modelObservableValue, + createT2MValueStrategy(), createM2TValueStrategy()); + } + + /** + * Creates a binding between a target and model observable value on the + * given binding context by creating new update strategies with the provided + * update policies which will be both configured with the state of this + * editing object before passing them to the binding. + * + * @param dbc + * The binding context on which to create the value binding. + * @param targetObservableValue + * The target observable value of the binding. + * @param modelObservableValue + * The model observable value of the binding. + * @param t2mUpdatePolicy + * The update policy for the target-to-model + * {@link UpdateValueStrategy} which is + * {@link #createT2MValueStrategy(int) created} and passed to the + * new binding. + * @param m2tUpdatePolicy + * The update policy for the model-to-target + * {@link UpdateValueStrategy} which is + * {@link #createM2TValueStrategy(int) created} and passed to the + * new binding. + * @return The new value binding. + * + * @see #createT2MValueStrategy(int) + * @see #createM2TValueStrategy(int) + * @see DataBindingContext#bindValue(IObservableValue, IObservableValue, + * UpdateValueStrategy, UpdateValueStrategy) + */ + public final Binding bindValue(DataBindingContext dbc, + IObservableValue targetObservableValue, + IObservableValue modelObservableValue, int t2mUpdatePolicy, + int m2tUpdatePolicy) { + return dbc.bindValue(targetObservableValue, modelObservableValue, + createT2MValueStrategy(t2mUpdatePolicy), + createM2TValueStrategy(m2tUpdatePolicy)); + } + + /** + * Creates a binding between a target and model observable value on the + * given binding context by using the provided update strategies which will + * be both configured with the state of this editing object before passing + * them to the binding. + * + * @param dbc + * The binding context on which to create the value binding. + * @param targetObservableValue + * The target observable value of the binding. + * @param modelObservableValue + * The model observable value of the binding. + * @param t2mUpdateStrategy + * The target-to-model {@link UpdateValueStrategy} of the binding + * to be {@link #adaptT2MValueStrategy(UpdateValueStrategy) + * configured} with the state of this editing object before + * passing it to the binding. + * @param m2tUpdateStrategy + * The model-to-target {@link UpdateValueStrategy} of the binding + * to be {@link #adaptM2TValueStrategy(UpdateValueStrategy) + * configured} with the state of this editing object before + * passing it to the binding. + * @return The new value binding. + * + * @see #adaptT2MValueStrategy(UpdateValueStrategy) + * @see #adaptM2TValueStrategy(UpdateValueStrategy) + * @see DataBindingContext#bindValue(IObservableValue, IObservableValue, + * UpdateValueStrategy, UpdateValueStrategy) + */ + public final Binding bindValue(DataBindingContext dbc, + IObservableValue targetObservableValue, + IObservableValue modelObservableValue, + UpdateValueStrategy t2mUpdateStrategy, + UpdateValueStrategy m2tUpdateStrategy) { + return dbc.bindValue(targetObservableValue, modelObservableValue, + adaptT2MValueStrategy(t2mUpdateStrategy), + adaptM2TValueStrategy(m2tUpdateStrategy)); + } + + /** + * Creates a binding between a target and model observable list on the given + * binding context by creating new update strategies which will be both + * configured with the state of this editing object before passing them to + * the binding. + * + *

    + * The target-to-model and model-to-target update strategies for the binding + * will be created by the methods {@link #createT2MListStrategy()} and + * {@link #createM2TListStrategy()}, respectively. + *

    + * + * @param dbc + * The binding context on which to create the list binding. + * @param targetObservableList + * The target observable list of the binding. + * @param modelObservableList + * The model observable list of the binding. + * @return The new list binding. + * + * @see #createT2MListStrategy() + * @see #createM2TListStrategy() + * @see DataBindingContext#bindList(IObservableList, IObservableList, + * UpdateListStrategy, UpdateListStrategy) + */ + public final Binding bindList(DataBindingContext dbc, + IObservableList targetObservableList, + IObservableList modelObservableList) { + return dbc.bindList(targetObservableList, modelObservableList, + createT2MListStrategy(), createM2TListStrategy()); + } + + /** + * Creates a binding between a target and model observable list on the given + * binding context by creating new update strategies with the provided + * update policies which will be both configured with the state of this + * editing object before passing them to the binding. + * + * @param dbc + * The binding context on which to create the list binding. + * @param targetObservableList + * The target observable list of the binding. + * @param modelObservableList + * The model observable list of the binding. + * @param t2mUpdatePolicy + * The update policy for the target-to-model + * {@link UpdateListStrategy} which is + * {@link #createT2MListStrategy(int) created} and passed to the + * new binding. + * @param m2tUpdatePolicy + * The update policy for the model-to-target + * {@link UpdateListStrategy} which is + * {@link #createM2TListStrategy(int) created} and passed to the + * new binding. + * @return The new list binding. + * + * @see #createT2MListStrategy(int) + * @see #createM2TListStrategy(int) + * @see DataBindingContext#bindList(IObservableList, IObservableList, + * UpdateListStrategy, UpdateListStrategy) + */ + public final Binding bindList(DataBindingContext dbc, + IObservableList targetObservableList, + IObservableList modelObservableList, int t2mUpdatePolicy, + int m2tUpdatePolicy) { + return dbc.bindList(targetObservableList, modelObservableList, + createT2MListStrategy(t2mUpdatePolicy), + createM2TListStrategy(m2tUpdatePolicy)); + } + + /** + * Creates a binding between a target and model observable list on the given + * binding context by using the provided update strategies which will be + * both configured with the state of this editing object before passing them + * to the binding. + * + * @param dbc + * The binding context on which to create the list binding. + * @param targetObservableList + * The target observable list of the binding. + * @param modelObservableList + * The model observable list of the binding. + * @param t2mUpdateStrategy + * The target-to-model {@link UpdateListStrategy} of the binding + * to be {@link #adaptT2MListStrategy(UpdateListStrategy) + * configured} with the state of this editing object before + * passing it to the binding. + * @param m2tUpdateStrategy + * The model-to-target {@link UpdateListStrategy} of the binding + * to be {@link #adaptM2TListStrategy(UpdateListStrategy) + * configured} with the state of this editing object before + * passing it to the binding. + * @return The new list binding. + * + * @see #adaptT2MListStrategy(UpdateListStrategy) + * @see #adaptM2TListStrategy(UpdateListStrategy) + * @see DataBindingContext#bindList(IObservableList, IObservableList, + * UpdateListStrategy, UpdateListStrategy) + */ + public final Binding bindList(DataBindingContext dbc, + IObservableList targetObservableList, + IObservableList modelObservableList, + UpdateListStrategy t2mUpdateStrategy, + UpdateListStrategy m2tUpdateStrategy) { + return dbc.bindList(targetObservableList, modelObservableList, + adaptT2MListStrategy(t2mUpdateStrategy), + adaptM2TListStrategy(m2tUpdateStrategy)); + } + + /** + * Creates a binding between a target and model observable set on the given + * binding context by creating new update strategies which will be both + * configured with the state of this editing object before passing them to + * the binding. + * + *

    + * The target-to-model and model-to-target update strategies for the binding + * will be created by the methods {@link #createT2MSetStrategy()} and + * {@link #createM2TSetStrategy()}, respectively. + *

    + * + * @param dbc + * The binding context on which to create the set binding. + * @param targetObservableSet + * The target observable set of the binding. + * @param modelObservableSet + * The model observable set of the binding. + * @return The new set binding. + * + * @see #createT2MSetStrategy() + * @see #createM2TSetStrategy() + * @see DataBindingContext#bindSet(IObservableSet, IObservableSet, + * UpdateSetStrategy, UpdateSetStrategy) + */ + public final Binding bindSet(DataBindingContext dbc, + IObservableSet targetObservableSet, + IObservableSet modelObservableSet) { + return dbc.bindSet(targetObservableSet, modelObservableSet, + createT2MSetStrategy(), createM2TSetStrategy()); + } + + /** + * Creates a binding between a target and model observable set on the given + * binding context by creating new update strategies with the provided + * update policies which will be both configured with the state of this + * editing object before passing them to the binding. + * + * @param dbc + * The binding context on which to create the set binding. + * @param targetObservableSet + * The target observable set of the binding. + * @param modelObservableSet + * The model observable set of the binding. + * @param t2mUpdatePolicy + * The update policy for the target-to-model + * {@link UpdateSetStrategy} which is + * {@link #createT2MSetStrategy(int) created} and passed to the + * new binding. + * @param m2tUpdatePolicy + * The update policy for the model-to-target + * {@link UpdateSetStrategy} which is + * {@link #createM2TSetStrategy(int) created} and passed to the + * new binding. + * @return The new set binding. + * + * @see #createT2MSetStrategy(int) + * @see #createM2TSetStrategy(int) + * @see DataBindingContext#bindSet(IObservableSet, IObservableSet, + * UpdateSetStrategy, UpdateSetStrategy) + */ + public final Binding bindSet(DataBindingContext dbc, + IObservableSet targetObservableSet, + IObservableSet modelObservableSet, int t2mUpdatePolicy, + int m2tUpdatePolicy) { + return dbc.bindSet(targetObservableSet, modelObservableSet, + createT2MSetStrategy(t2mUpdatePolicy), + createM2TSetStrategy(m2tUpdatePolicy)); + } + + /** + * Creates a binding between a target and model observable set on the given + * binding context by using the provided update strategies which will be + * both configured with the state of this editing object before passing them + * to the binding. + * + * @param dbc + * The binding context on which to create the set binding. + * @param targetObservableSet + * The target observable set of the binding. + * @param modelObservableSet + * The model observable set of the binding. + * @param t2mUpdateStrategy + * The target-to-model {@link UpdateSetStrategy} of the binding + * to be {@link #adaptT2MSetStrategy(UpdateSetStrategy) + * configured} with the state of this editing object before + * passing it to the binding. + * @param m2tUpdateStrategy + * The model-to-target {@link UpdateSetStrategy} of the binding + * to be {@link #adaptM2TSetStrategy(UpdateSetStrategy) + * configured} with the state of this editing object before + * passing it to the binding. + * @return The new set binding. + * + * @see #adaptT2MSetStrategy(UpdateSetStrategy) + * @see #adaptM2TSetStrategy(UpdateSetStrategy) + * @see DataBindingContext#bindSet(IObservableSet, IObservableSet, + * UpdateSetStrategy, UpdateSetStrategy) + */ + public final Binding bindSet(DataBindingContext dbc, + IObservableSet targetObservableSet, + IObservableSet modelObservableSet, + UpdateSetStrategy t2mUpdateStrategy, + UpdateSetStrategy m2tUpdateStrategy) { + return dbc.bindSet(targetObservableSet, modelObservableSet, + adaptT2MSetStrategy(t2mUpdateStrategy), + adaptM2TSetStrategy(m2tUpdateStrategy)); + } + + private static IValidator createValidator(Constraints constraints) { + return constraints != null ? constraints.createValidator() : null; + } + + private static boolean applyConstraints(Constraints constraints, + Object value, MultiStatus aggregateStatus) { + IValidator validator = createValidator(constraints); + if (validator != null) { + IStatus validationStatus = validator.validate(value); + if (aggregateStatus != null && !validationStatus.isOK()) { + aggregateStatus.merge(validationStatus); + } + return isValid(validationStatus); + } + return true; + } + + private static Object applyConverter(IConverter converter, Object value) { + return converter != null ? converter.convert(value) : value; + } + + private static boolean isValid(IStatus status) { + return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING); + } +} Index: src/org/eclipse/core/databinding/validation/constraint/NumberConstraints.java =================================================================== RCS file: src/org/eclipse/core/databinding/validation/constraint/NumberConstraints.java diff -N src/org/eclipse/core/databinding/validation/constraint/NumberConstraints.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/databinding/validation/constraint/NumberConstraints.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,490 @@ +/******************************************************************************* + * 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.validation.constraint; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import org.eclipse.core.internal.databinding.validation.DecimalPrecisionValidator; +import org.eclipse.core.internal.databinding.validation.DecimalScaleValidator; +import org.eclipse.core.internal.databinding.validation.NumberRangeValidator; + +import com.ibm.icu.text.NumberFormat; + +/** + * Provides a set of constraints to apply to number numbers. + * + *

    + * Instances of this class can be used to define a set of constraints to apply + * to numbers. In addition, the validation messages to be issued for the + * individual constraints as well as the way in which model objects are + * formatted in the validation messages can be configured. + *

    + * + * @param + * + * @noextend This class is not intended to be subclassed by clients. + * @since 1.3 + */ +public class NumberConstraints> extends + BaseObjectConstraints> { + + private NumberFormat numberFormat = NumberFormat.getNumberInstance(); + + private NumberFormat scaleFormat = NumberFormat.getIntegerInstance(); + + private NumberFormat precisionFormat = NumberFormat.getIntegerInstance(); + + 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 String maxScaleMessage = null; + + private String maxPrecisionMessage = null; + + private final N zeroNumber; + + private final N oneNumber; + + /** + * + * @param zeroNumber + * @param oneNumber + * + * @noreference This constructor is not intended to be referenced by + * clients. + */ + protected NumberConstraints(N zeroNumber, N oneNumber) { + this.zeroNumber = zeroNumber; + this.oneNumber = oneNumber; + } + + /** + * Creates a constraints object for Longs. + * + * @return The constraints object for Longs. + */ + public static NumberConstraints forLong() { + return new NumberConstraints(new Long(0), new Long(1)); + } + + /** + * Creates a constraints object for Integers. + * + * @return The constraints object for Integers. + */ + public static NumberConstraints forInteger() { + return new NumberConstraints(new Integer(0), new Integer(1)); + } + + /** + * Creates a constraints object for Shorts. + * + * @return The constraints object for Shorts. + */ + public static NumberConstraints forShort() { + return new NumberConstraints(new Short((short) 0), new Short( + (short) 1)); + } + + /** + * Creates a constraints object for Bytes. + * + * @return The constraints object for Bytes. + */ + public static NumberConstraints forByte() { + return new NumberConstraints(new Byte((byte) 0), new Byte( + (byte) 1)); + } + + /** + * Creates a constraints object for Doubles. + * + * @return The constraints object for Doubles. + */ + public static NumberConstraints forDouble() { + return new NumberConstraints(new Double(0.0), new Double(1.0)); + } + + /** + * Creates a constraints object for Floats. + * + * @return The constraints object for Floats. + */ + public static NumberConstraints forFloat() { + return new NumberConstraints(new Float(0.0f), new Float(1.0f)); + } + + /** + * Creates a constraints object for BigIntegers. + * + * @return The constraints object for BigIntegers. + */ + public static NumberConstraints forBigInteger() { + return new NumberConstraints(BigInteger.ZERO, + BigInteger.ONE); + } + + /** + * Creates a constraints object for BigDecimals. + * + * @return The constraints object for BigDecimals. + */ + public static NumberConstraints forBigDecimal() { + return new NumberConstraints(BigDecimal.ZERO, + BigDecimal.ONE); + } + + /** + * Sets the format to use when formatting a number to be included in any of + * the validation messages. + * + * @param format + * The format to use for displaying numbers in validation + * messages. + * @return This constraints instance for method chaining. + */ + public NumberConstraints numberFormat(NumberFormat format) { + this.numberFormat = format; + return this; + } + + /** + * Adds a validator ensuring that the input number is greater than the given + * number. Uses the current {@link #greaterMessage(String) validation + * message} and {@link #numberFormat(NumberFormat) number format}. + * + * @param number + * The number which the input number must be greater than. + * @return This constraints instance for method chaining. + */ + public NumberConstraints greater(N number) { + addValidator(NumberRangeValidator.greater(number, greaterMessage, + numberFormat)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #greater(Number)} + * constraint. + * + * @param message + * The validation message pattern for the + * {@link #greater(Number)} constraint. Can be parameterized with + * the number ({0}) which the input number must be greater than. + * @return This constraints instance for method chaining. + * + * @see #greater(Number) + */ + public NumberConstraints greaterMessage(String message) { + this.greaterMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the input number is greater than or equal + * to the given number. Uses the current + * {@link #greaterEqualMessage(String) validation message} and + * {@link #numberFormat(NumberFormat) number format}. + * + * @param number + * The number which the input number must be greater than or + * equal to. + * @return This constraints instance for method chaining. + */ + public NumberConstraints greaterEqual(N number) { + addValidator(NumberRangeValidator.greaterEqual(number, + greaterEqualMessage, numberFormat)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #greaterEqual(Number)} + * constraint. + * + * @param message + * The validation message pattern for the + * {@link #greaterEqual(Number)} constraint. Can be parameterized + * with the number ({0}) which the input number must be greater + * than or equal to. + * @return This constraints instance for method chaining. + * + * @see #greaterEqual(Number) + */ + public NumberConstraints greaterEqualMessage(String message) { + this.greaterEqualMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the input number is less than the given + * number. Uses the current {@link #lessMessage(String) validation message} + * and {@link #numberFormat(NumberFormat) number format}. + * + * @param number + * The number which the input number must be less than. + * @return This constraints instance for method chaining. + */ + public NumberConstraints less(N number) { + addValidator(NumberRangeValidator.less(number, lessMessage, + numberFormat)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #less(Number)} + * constraint. + * + * @param message + * The validation message pattern for the {@link #less(Number)} + * constraint. Can be parameterized with the number ({0}) which + * the input number must be less than. + * @return This constraints instance for method chaining. + * + * @see #less(Number) + */ + public NumberConstraints lessMessage(String message) { + this.lessMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the input number is less than or equal to + * the given number. Uses the current {@link #lessEqualMessage(String) + * validation message} and {@link #numberFormat(NumberFormat) number format} + * . + * + * @param number + * The number which the input number must be less than or equal + * to. + * @return This constraints instance for method chaining. + */ + public NumberConstraints lessEqual(N number) { + addValidator(NumberRangeValidator.greaterEqual(number, + lessEqualMessage, numberFormat)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #lessEqual(Number)} + * constraint. + * + * @param message + * The validation message pattern for the + * {@link #lessEqual(Number)} constraint. Can be parameterized + * with the number ({0}) which the input number must be less than + * or equal to. + * @return This constraints instance for method chaining. + * + * @see #lessEqual(Number) + */ + public NumberConstraints lessEqualMessage(String message) { + this.lessEqualMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the input number is within the given + * range. Uses the current {@link #rangeMessage(String) validation message} + * and {@link #numberFormat(NumberFormat) number format}. + * + * @param min + * The lower bound of the range (inclusive). + * @param max + * The upper bound of the range (inclusive). + * @return This constraints instance for method chaining. + */ + public NumberConstraints range(N min, N max) { + addValidator(NumberRangeValidator.range(min, max, rangeMessage, + numberFormat)); + return this; + } + + /** + * Sets the validation message pattern for the + * {@link #range(Number, Number)} constraint. + * + * @param message + * The validation message pattern for the + * {@link #range(Number, Number)} constraint. Can be + * parameterized with the min ({0}) and max ({1}) values of the + * range in which the input number must lie. + * @return This constraints instance for method chaining. + * + * @see #range(Number, Number) + */ + public NumberConstraints rangeMessage(String message) { + this.rangeMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the input number is positive. Uses the + * current {@link #positiveMessage(String) validation message}. + * + * @return This constraints instance for method chaining. + */ + public NumberConstraints positive() { + addValidator(NumberRangeValidator.positive(oneNumber, positiveMessage, + numberFormat)); + return this; + } + + /** + * Sets the validation message for the {@link #positive()} constraint. + * + * @param message + * The validation message for the {@link #positive()} constraint. + * @return This constraints instance for method chaining. + * + * @see #positive() + */ + public NumberConstraints positiveMessage(String message) { + this.positiveMessage = message; + return this; + } + + /** + * Adds a validator ensuring that the input number is non-negative. Uses the + * current {@link #nonNegativeMessage(String) validation message}. + * + * @return This constraints instance for method chaining. + */ + public NumberConstraints nonNegative() { + addValidator(NumberRangeValidator.nonNegative(zeroNumber, + nonNegativeMessage, numberFormat)); + return this; + } + + /** + * Sets the validation message for the {@link #nonNegative()} constraint. + * + * @param message + * The validation message for the {@link #nonNegative()} + * constraint. + * @return This constraints instance for method chaining. + * + * @see #nonNegative() + */ + public NumberConstraints nonNegativeMessage(String message) { + this.nonNegativeMessage = message; + return this; + } + + /** + * Sets the format to use when formatting the scale in any of the validation + * messages stemming from a scale constraint. + * + * @param format + * The format to use for displaying the scale in validation + * messages stemming from a scale constraint. + * @return This constraints instance for method chaining. + * + * @see #maxScale(int) + */ + public NumberConstraints scaleFormat(NumberFormat format) { + this.scaleFormat = format; + return this; + } + + /** + * Adds a validator ensuring that the input number's scale is not greater + * than the given one. Uses the current {@link #maxScaleMessage(String) + * validation message} and {@link #scaleFormat(NumberFormat) scale format}. + * + * @param scale + * The maximum scale to enforce on the input number. + * @return This constraints instance for method chaining. + */ + public NumberConstraints maxScale(int scale) { + addValidator(DecimalScaleValidator.max(scale, maxScaleMessage, + scaleFormat)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #maxScale(int)} + * constraint. + * + * @param message + * The validation message pattern for the {@link #maxScale(int)} + * constraint. Can be parameterized with the maximum scale ({0}) + * of the number. + * @return This constraints instance for method chaining. + * + * @see #maxScale(int) + */ + public NumberConstraints maxScaleMessage(String message) { + this.maxScaleMessage = message; + return this; + } + + /** + * Sets the format to use when formatting the precision in any of the + * validation messages stemming from a precision constraint. + * + * @param format + * The format to use for displaying the precision in validation + * messages stemming from a precision constraint. + * @return This constraints instance for method chaining. + * + * @see #maxPrecision(int) + */ + public NumberConstraints precisionFormat(NumberFormat format) { + this.precisionFormat = format; + return this; + } + + /** + * Adds a validator ensuring that the input number's precision is not + * greater than the given one. Uses the current + * {@link #maxPrecisionMessage(String) validation message} and + * {@link #precisionFormat(NumberFormat) precision format}. + * + * @param precision + * The maximum precision to enforce on the input number. + * @return This constraints instance for method chaining. + */ + public NumberConstraints maxPrecision(int precision) { + addValidator(DecimalPrecisionValidator.max(precision, + maxPrecisionMessage, precisionFormat)); + return this; + } + + /** + * Sets the validation message pattern for the {@link #maxPrecision(int)} + * constraint. + * + * @param message + * The validation message pattern for the + * {@link #maxPrecision(int)} constraint. Can be parameterized + * with the maximum precision ({0}) of the number. + * @return This constraints instance for method chaining. + * + * @see #maxPrecision(int) + */ + public NumberConstraints maxPrecisionMessage(String message) { + this.maxPrecisionMessage = message; + return this; + } +} Index: src/org/eclipse/core/databinding/validation/constraint/BaseObjectConstraints.java =================================================================== RCS file: src/org/eclipse/core/databinding/validation/constraint/BaseObjectConstraints.java diff -N src/org/eclipse/core/databinding/validation/constraint/BaseObjectConstraints.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/databinding/validation/constraint/BaseObjectConstraints.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,67 @@ +/******************************************************************************* + * 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.validation.constraint; + +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.internal.databinding.validation.NonNullValidator; + +/** + * @param + * @param + * + * @since 1.3 + */ +public class BaseObjectConstraints> + extends Constraints { + + private String requiredMessage = null; + + public C addValidator(IValidator validator) { + super.addValidator(validator); + return getThis(); + } + + public C aggregationPolicy(Aggregation aggregation) { + super.aggregationPolicy(aggregation); + return getThis(); + } + + /** + * Adds a validator ensuring that the input number is not null + * . Uses the current {@link #requiredMessage(String) validation message}. + * + * @return This constraints instance for method chaining. + */ + public C required() { + addValidator(new NonNullValidator(requiredMessage)); + return getThis(); + } + + /** + * Sets the validation message for the {@link #required()} constraint. + * + * @param message + * The validation message for the {@link #required()} constraint. + * @return This constraints instance for method chaining. + * + * @see #required() + */ + public C requiredMessage(String message) { + this.requiredMessage = message; + return getThis(); + } + + @SuppressWarnings("unchecked") + private C getThis() { + return (C) this; + } +} Index: src/org/eclipse/core/internal/databinding/validation/DecimalScaleValidator.java =================================================================== RCS file: src/org/eclipse/core/internal/databinding/validation/DecimalScaleValidator.java diff -N src/org/eclipse/core/internal/databinding/validation/DecimalScaleValidator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/internal/databinding/validation/DecimalScaleValidator.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,99 @@ +/******************************************************************************* + * 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.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; + +import com.ibm.icu.math.BigDecimal; +import com.ibm.icu.text.NumberFormat; + +/** + * Provides validations for the scale of decimal numbers. + * + * @since 1.3 + */ +public class DecimalScaleValidator implements IValidator { + + private static final String MAX_SCALE_VALIDATION_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_DECIMAL_MAX_SCALE); + + private final int maxScale; + + private final String validationMessage; + + private String formattedValidationMessage; + + private final NumberFormat scaleFormat; + + private DecimalScaleValidator(int maxScale, String validationMessage, + NumberFormat scaleFormat) { + this.maxScale = maxScale; + this.validationMessage = validationMessage != null ? validationMessage + : MAX_SCALE_VALIDATION_MESSAGE; + this.scaleFormat = scaleFormat; + } + + /** + * Creates a validator which checks that an input decimal has a scale which + * is less or equal to the given scale. + * + * @param maxScale + * The maximum scale to enforce on the input decimal. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the maximum scale to be enforced. + * @param scaleFormat + * The display format to use for formatting the scale in the + * validation message. + * @return The validator instance. + */ + public static DecimalScaleValidator max(int maxScale, + String validationMessage, NumberFormat scaleFormat) { + return new DecimalScaleValidator(maxScale, validationMessage, + scaleFormat); + } + + public IStatus validate(Object value) { + if (value != null) { + Number number = (Number) value; + final int scale; + if (number instanceof BigDecimal) { + scale = ((BigDecimal) number).scale(); + } else { + scale = new BigDecimal(number.doubleValue()).scale(); + } + if (scale > maxScale) { + return ValidationStatus.error(getFormattedValidationMessage()); + } + } + return ValidationStatus.ok(); + } + + private synchronized String getFormattedValidationMessage() { + if (formattedValidationMessage == null) { + formattedValidationMessage = MessageFormat.format( + validationMessage, getValidationMessageArguments()); + } + return formattedValidationMessage; + } + + private String[] getValidationMessageArguments() { + synchronized (scaleFormat) { + return new String[] { scaleFormat.format(new Integer(maxScale)) }; + } + } +} 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,155 @@ +/******************************************************************************* + * 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.databinding.validation.constraint.DateConstraints; +import org.eclipse.core.databinding.validation.constraint.StringConstraints; +import org.eclipse.core.internal.databinding.conversion.DateToStringConverter; +import org.eclipse.core.internal.databinding.conversion.StringToDateConverter; +import org.eclipse.core.internal.databinding.validation.StringToDateValidator; + +import com.ibm.icu.text.DateFormat; + +/** + * @noextend This class is not intended to be subclassed by clients. + * @since 1.3 + */ +public class DateEditing extends Editing { + + private final DateFormat displayFormat; + + /** + * Creates a new editing object for Dates. + * + * @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. + * + * @noreference This constructor is not intended to be referenced by + * clients. + */ + protected DateEditing(DateFormat[] inputFormats, String parseErrorMessage, + DateFormat displayFormat) { + this.displayFormat = displayFormat; + + StringToDateConverter targetConverter = new StringToDateConverter(); + if (inputFormats != null) { + targetConverter.setFormatters(inputFormats); + } + + DateToStringConverter modelConverter = new DateToStringConverter(); + if (displayFormat != null) { + modelConverter.setFormatters(new DateFormat[] { displayFormat }); + } + + StringToDateValidator targetValidator = new StringToDateValidator( + targetConverter); + if (parseErrorMessage != null) { + targetValidator.setParseErrorMessage(parseErrorMessage); + } + + setTargetConverter(targetConverter); + setModelConverter(modelConverter); + targetConstraints().addValidator(targetValidator); + } + + /** + * 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); + } + + public StringConstraints targetConstraints() { + return (StringConstraints) super.targetConstraints(); + } + + public DateConstraints modelConstraints() { + return (DateConstraints) super.modelConstraints(); + } + + public DateConstraints beforeSetModelConstraints() { + return (DateConstraints) super.beforeSetModelConstraints(); + } + + protected StringConstraints createTargetConstraints() { + return new StringConstraints(); + } + + protected DateConstraints createModelConstraints() { + return new DateConstraints().dateFormat(displayFormat); + } + + protected DateConstraints createBeforeSetModelConstraints() { + return new DateConstraints().dateFormat(displayFormat); + } +} 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,83 @@ +/******************************************************************************* + * 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.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; + +/** + * Provides validations for strings which must match a given regular expression. + * + * @since 1.3 + */ +public class StringRegexValidator implements IValidator { + + private static final String REGEX_VALIDATION_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_STRING_REGEX); + + private final Pattern pattern; + private final String validationMessage; + private String formattedValidationMessage; + + /** + * Creates a new regex validator. + * + * @param regex + * The regular expression which the input string must match. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the given regular expression string. + */ + public StringRegexValidator(String regex, String validationMessage) { + this(Pattern.compile(regex), validationMessage); + } + + /** + * Creates a new regex validator. + * + * @param pattern + * The pattern which the input string must match. + * @param validationMessage + * The validation message pattern to use. Can be parameterized + * with the given regular expression string. + */ + public StringRegexValidator(Pattern pattern, String validationMessage) { + this.pattern = pattern; + 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()) { + return ValidationStatus.error(getFormattedValidationMessage()); + } + } + return ValidationStatus.ok(); + } + + private synchronized String getFormattedValidationMessage() { + if (formattedValidationMessage == null) { + formattedValidationMessage = MessageFormat.format( + validationMessage, new String[] { pattern.pattern() }); + } + return formattedValidationMessage; + } +} Index: src/org/eclipse/core/internal/databinding/validation/CharacterValidator.java =================================================================== RCS file: src/org/eclipse/core/internal/databinding/validation/CharacterValidator.java diff -N src/org/eclipse/core/internal/databinding/validation/CharacterValidator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/internal/databinding/validation/CharacterValidator.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,145 @@ +/******************************************************************************* + * 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.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; + +/** + * @since 1.3 + */ +public class CharacterValidator implements IValidator { + + private static final int VALIDATE_NO_WHITESPACE = 0; + private static final int VALIDATE_NO_SPACE = 1; + private static final int VALIDATE_LETTER = 2; + private static final int VALIDATE_DIGIT = 3; + private static final int VALIDATE_LETTER_OR_DIGIT = 4; + + private static final String NO_WHITESPACE_VALIDATION_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_CHARACTER_NO_WHITESPACE); + private static final String NO_SPACE_VALIDATION_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_CHARACTER_NO_SPACE); + private static final String LETTER_VALIDATION_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_CHARACTER_LETTER); + private static final String DIGIT_VALIDATION_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_CHARACTER_DIGIT); + private static final String LETTER_OR_DIGIT_VALIDATION_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_CHARACTER_LETTER_OR_DIGIT); + + private final int validation; + private final String validationMessage; + + private CharacterValidator(int validation, String validationMessage) { + this.validation = validation; + this.validationMessage = validationMessage; + } + + /** + * Creates a validator which checks that an input character is no + * {@link Character#isWhitespace(char) whitespace}. + * + * @param validationMessage + * The validation message to use. + * @return The validator instance. + */ + public static CharacterValidator noWhitespace(String validationMessage) { + return new CharacterValidator(VALIDATE_NO_WHITESPACE, defaultIfNull( + NO_WHITESPACE_VALIDATION_MESSAGE, validationMessage)); + } + + /** + * Creates a validator which checks that an input character is no + * {@link Character#isSpaceChar(char) space}. + * + * @param validationMessage + * The validation message to use. + * @return The validator instance. + */ + public static CharacterValidator noSpace(String validationMessage) { + return new CharacterValidator(VALIDATE_NO_SPACE, defaultIfNull( + NO_SPACE_VALIDATION_MESSAGE, validationMessage)); + } + + /** + * Creates a validator which checks that an input character is a + * {@link Character#isLetter(char) letter}. + * + * @param validationMessage + * The validation message to use. + * @return The validator instance. + */ + public static CharacterValidator letter(String validationMessage) { + return new CharacterValidator(VALIDATE_LETTER, defaultIfNull( + LETTER_VALIDATION_MESSAGE, validationMessage)); + } + + /** + * Creates a validator which checks that an input character is a + * {@link Character#isDigit(char) digit}. + * + * @param validationMessage + * The validation message to use. + * @return The validator instance. + */ + public static CharacterValidator digit(String validationMessage) { + return new CharacterValidator(VALIDATE_DIGIT, defaultIfNull( + DIGIT_VALIDATION_MESSAGE, validationMessage)); + } + + /** + * Creates a validator which checks that an input character is a + * {@link Character#isLetterOrDigit(char) letter or ditig}. + * + * @param validationMessage + * The validation message to use. + * @return The validator instance. + */ + public static CharacterValidator letterOrDigit(String validationMessage) { + return new CharacterValidator(VALIDATE_LETTER_OR_DIGIT, defaultIfNull( + LETTER_OR_DIGIT_VALIDATION_MESSAGE, validationMessage)); + } + + public IStatus validate(Object value) { + if (value != null) { + char character = ((Character) value).charValue(); + if (!isValid(character)) { + return ValidationStatus.error(validationMessage); + } + } + return ValidationStatus.ok(); + } + + private boolean isValid(char character) { + switch (validation) { + case VALIDATE_NO_WHITESPACE: + return !Character.isWhitespace(character); + case VALIDATE_NO_SPACE: + return !Character.isSpaceChar(character); + case VALIDATE_LETTER: + return Character.isLetter(character); + case VALIDATE_DIGIT: + return Character.isDigit(character); + case VALIDATE_LETTER_OR_DIGIT: + return Character.isLetterOrDigit(character); + default: + throw new IllegalArgumentException( + "Unsupported validation: " + validation); //$NON-NLS-1$ + } + } + + private static String defaultIfNull(String string, String defaultString) { + return (string != null) ? string : defaultString; + } +} Index: src/org/eclipse/core/databinding/editing/CharacterEditing.java =================================================================== RCS file: src/org/eclipse/core/databinding/editing/CharacterEditing.java diff -N src/org/eclipse/core/databinding/editing/CharacterEditing.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/databinding/editing/CharacterEditing.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,100 @@ +/******************************************************************************* + * 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.validation.constraint.CharacterConstraints; +import org.eclipse.core.databinding.validation.constraint.StringConstraints; +import org.eclipse.core.internal.databinding.conversion.CharacterToStringConverter; +import org.eclipse.core.internal.databinding.conversion.StringToCharacterConverter; +import org.eclipse.core.internal.databinding.validation.StringToCharacterValidator; + +/** + * @noextend This class is not intended to be subclassed by clients. + * @since 1.3 + */ +public class CharacterEditing extends Editing { + + /** + * Creates a new editing object for booleans. + * + * @param parseErrorMessage + * The validation message issued in case the input string is not + * a valid character. + * + * @noreference This constructor is not intended to be referenced by + * clients. + */ + protected CharacterEditing(String parseErrorMessage) { + StringToCharacterConverter targetConverter = new StringToCharacterConverter( + false); + CharacterToStringConverter modelConverter = CharacterToStringConverter + .fromCharacter(false); + + StringToCharacterValidator targetValidator = new StringToCharacterValidator( + targetConverter); + if (parseErrorMessage != null) { + targetValidator.setParseErrorMessage(parseErrorMessage); + } + + setTargetConverter(targetConverter); + setModelConverter(modelConverter); + targetConstraints().addValidator(targetValidator); + } + + /** + * Creates a new editing object which uses the default validations and + * conversions for the editing of characters. + * + * @return The new editing object for the default editing of characters. + */ + public static CharacterEditing withDefaults() { + return withDefaults(null); + } + + /** + * Creates a new editing object which uses the default validations and + * conversions for the editing of characters. Uses the specified custom + * validation message. + * + * @param parseErrorMessage + * The validation message issued in case the input string is not + * a valid character. + * @return The new editing object for the default editing of characters. + */ + public static CharacterEditing withDefaults(String parseErrorMessage) { + return new CharacterEditing(parseErrorMessage); + } + + public StringConstraints targetConstraints() { + return (StringConstraints) super.targetConstraints(); + } + + public CharacterConstraints modelConstraints() { + return (CharacterConstraints) super.modelConstraints(); + } + + public CharacterConstraints beforeSetModelConstraints() { + return (CharacterConstraints) super.beforeSetModelConstraints(); + } + + protected StringConstraints createTargetConstraints() { + return new StringConstraints(); + } + + protected CharacterConstraints createModelConstraints() { + return new CharacterConstraints(); + } + + protected CharacterConstraints createBeforeSetModelConstraints() { + return new CharacterConstraints(); + } +} Index: src/org/eclipse/core/internal/databinding/validation/StringToBigIntegerValidator.java =================================================================== RCS file: src/org/eclipse/core/internal/databinding/validation/StringToBigIntegerValidator.java diff -N src/org/eclipse/core/internal/databinding/validation/StringToBigIntegerValidator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/internal/databinding/validation/StringToBigIntegerValidator.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2007 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; + +/** + * Validates that a string is of the appropriate format for a BigInteger. + * + * @since 1.3 + */ +public class StringToBigIntegerValidator extends + AbstractStringToNumberValidator { + + /** + * @param converter + */ + public StringToBigIntegerValidator(NumberFormatConverter converter) { + super(converter, null, null); + } + + protected boolean isInRange(Number number) { + return true; + } +} Index: src/org/eclipse/core/databinding/editing/NumberEditing.java =================================================================== RCS file: src/org/eclipse/core/databinding/editing/NumberEditing.java diff -N src/org/eclipse/core/databinding/editing/NumberEditing.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/databinding/editing/NumberEditing.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,762 @@ +/******************************************************************************* + * 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.math.BigDecimal; +import java.math.BigInteger; + +import org.eclipse.core.databinding.conversion.NumberToStringConverter; +import org.eclipse.core.databinding.conversion.StringToNumberConverter; +import org.eclipse.core.databinding.validation.constraint.NumberConstraints; +import org.eclipse.core.databinding.validation.constraint.StringConstraints; +import org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator; +import org.eclipse.core.internal.databinding.validation.StringToBigDecimalValidator; +import org.eclipse.core.internal.databinding.validation.StringToBigIntegerValidator; +import org.eclipse.core.internal.databinding.validation.StringToByteValidator; +import org.eclipse.core.internal.databinding.validation.StringToDoubleValidator; +import org.eclipse.core.internal.databinding.validation.StringToFloatValidator; +import org.eclipse.core.internal.databinding.validation.StringToIntegerValidator; +import org.eclipse.core.internal.databinding.validation.StringToLongValidator; +import org.eclipse.core.internal.databinding.validation.StringToShortValidator; + +import com.ibm.icu.text.NumberFormat; + +/** + * @param + * + * @noextend This class is not intended to be subclassed by clients. + * @since 1.3 + */ +public class NumberEditing> extends + Editing { + + private final NumberFormat displayFormat; + + private final Class numberType; + + /** + * Creates a new editing object for integer numbers. + * + * @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. + * @param numberType + * The specific integer type for which to set up an editing + * instance. + * + * @noreference This constructor is not intended to be referenced by + * clients. + */ + protected NumberEditing(NumberFormat format, String parseErrorMessage, + String outOfRangeMessage, Class numberType) { + this.displayFormat = format; + this.numberType = numberType; + + final StringToNumberConverter targetConverter; + final NumberToStringConverter modelConverter; + final AbstractStringToNumberValidator targetValidator; + if (Long.class.equals(numberType)) { + targetConverter = StringToNumberConverter.toLong(format, false); + modelConverter = NumberToStringConverter.fromLong(format, false); + targetValidator = new StringToLongValidator(targetConverter); + } else if (Integer.class.equals(numberType)) { + targetConverter = StringToNumberConverter.toInteger(format, false); + modelConverter = NumberToStringConverter.fromInteger(format, false); + targetValidator = new StringToIntegerValidator(targetConverter); + } else if (Short.class.equals(numberType)) { + targetConverter = StringToNumberConverter.toShort(format, false); + modelConverter = NumberToStringConverter.fromShort(format, false); + targetValidator = new StringToShortValidator(targetConverter); + } else if (Byte.class.equals(numberType)) { + targetConverter = StringToNumberConverter.toByte(format, false); + modelConverter = NumberToStringConverter.fromByte(format, false); + targetValidator = new StringToByteValidator(targetConverter); + } else if (Double.class.equals(numberType)) { + targetConverter = StringToNumberConverter.toDouble(format, false); + modelConverter = NumberToStringConverter.fromDouble(format, false); + targetValidator = new StringToDoubleValidator(targetConverter); + } else if (Float.class.equals(numberType)) { + targetConverter = StringToNumberConverter.toFloat(format, false); + modelConverter = NumberToStringConverter.fromFloat(format, false); + targetValidator = new StringToFloatValidator(targetConverter); + } else if (BigInteger.class.equals(numberType)) { + targetConverter = StringToNumberConverter.toBigInteger(format); + modelConverter = NumberToStringConverter.fromBigInteger(format); + targetValidator = new StringToBigIntegerValidator(targetConverter); + } else if (BigDecimal.class.equals(numberType)) { + targetConverter = StringToNumberConverter.toBigDecimal(format); + modelConverter = NumberToStringConverter.fromBigDecimal(format); + targetValidator = new StringToBigDecimalValidator(targetConverter); + } else { + throw new IllegalArgumentException( + "Unsupported number type: " + numberType); //$NON-NLS-1$ + } + + if (parseErrorMessage != null) { + targetValidator.setParseErrorMessage(parseErrorMessage); + } + if (outOfRangeMessage != null) { + targetValidator.setOutOfRangeMessage(outOfRangeMessage); + } + + setTargetConverter(targetConverter); + setModelConverter(modelConverter); + targetConstraints().addValidator(targetValidator); + } + + /** + * Creates a new editing object for {@link Long}s 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 NumberEditing withLongDefaults() { + return withLongDefaults(null, null); + } + + /** + * Creates a new editing object for {@link Long}s 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 Long.MIN_VALUE ({0}) and + * Long.MAX_VALUE ({1}) values. + * @return The new editing object using the default validations and + * conversions for editing. + * + * @see NumberFormat#getIntegerInstance() + */ + public static NumberEditing withLongDefaults( + String parseErrorMessage, String outOfRangeMessage) { + return new NumberEditing(NumberFormat.getIntegerInstance(), + parseErrorMessage, outOfRangeMessage, Long.class); + } + + /** + * Creates a new editing object for {@link Long}s 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 NumberEditing forLongFormat(NumberFormat format) { + return forLongFormat(format, null, null); + } + + /** + * Creates a new editing object for {@link Long}s 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 Long.MIN_VALUE ({0}) and + * Long.MAX_VALUE ({1}) values. + * @return The new editing object configured by the given integer format. + */ + public static NumberEditing forLongFormat(NumberFormat format, + String parseErrorMessage, String outOfRangeMessage) { + return new NumberEditing(format, parseErrorMessage, + outOfRangeMessage, Long.class); + } + + /** + * Creates a new editing object for {@link Integer}s 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 NumberEditing withIntegerDefaults() { + return withIntegerDefaults(null, null); + } + + /** + * Creates a new editing object for {@link Integer}s 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 ({0}) and + * Integer.MAX_VALUE ({1}) values. + * @return The new editing object using the default validations and + * conversions for editing. + * + * @see NumberFormat#getIntegerInstance() + */ + public static NumberEditing withIntegerDefaults( + String parseErrorMessage, String outOfRangeMessage) { + return new NumberEditing(NumberFormat.getIntegerInstance(), + parseErrorMessage, outOfRangeMessage, Integer.class); + } + + /** + * Creates a new editing object for {@link Integer}s 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 NumberEditing forIntegerFormat(NumberFormat format) { + return forIntegerFormat(format, null, null); + } + + /** + * Creates a new editing object for {@link Integer}s 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 ({0}) and + * Integer.MAX_VALUE ({1}) values. + * @return The new editing object configured by the given integer format. + */ + public static NumberEditing forIntegerFormat(NumberFormat format, + String parseErrorMessage, String outOfRangeMessage) { + return new NumberEditing(format, parseErrorMessage, + outOfRangeMessage, Integer.class); + } + + /** + * Creates a new editing object for {@link Short}s 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 NumberEditing withShortDefaults() { + return withShortDefaults(null, null); + } + + /** + * Creates a new editing object for {@link Short}s 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 Short.MIN_VALUE ({0}) and + * Short.MAX_VALUE ({1}) values. + * @return The new editing object using the default validations and + * conversions for editing. + * + * @see NumberFormat#getIntegerInstance() + */ + public static NumberEditing withShortDefaults( + String parseErrorMessage, String outOfRangeMessage) { + return new NumberEditing(NumberFormat.getIntegerInstance(), + parseErrorMessage, outOfRangeMessage, Short.class); + } + + /** + * Creates a new editing object for {@link Short}s 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 NumberEditing forShortFormat(NumberFormat format) { + return forShortFormat(format, null, null); + } + + /** + * Creates a new editing object for {@link Short}s 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 Short.MIN_VALUE ({0}) and + * Short.MAX_VALUE ({1}) values. + * @return The new editing object configured by the given integer format. + */ + public static NumberEditing forShortFormat(NumberFormat format, + String parseErrorMessage, String outOfRangeMessage) { + return new NumberEditing(format, parseErrorMessage, + outOfRangeMessage, Short.class); + } + + /** + * Creates a new editing object for {@link Byte}s 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 NumberEditing withByteDefaults() { + return withByteDefaults(null, null); + } + + /** + * Creates a new editing object for {@link Byte}s 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 Byte.MIN_VALUE ({0}) and + * Byte.MAX_VALUE ({1}) values. + * @return The new editing object using the default validations and + * conversions for editing. + * + * @see NumberFormat#getIntegerInstance() + */ + public static NumberEditing withByteDefaults( + String parseErrorMessage, String outOfRangeMessage) { + return new NumberEditing(NumberFormat.getIntegerInstance(), + parseErrorMessage, outOfRangeMessage, Byte.class); + } + + /** + * Creates a new editing object for {@link Byte}s 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 NumberEditing forByteFormat(NumberFormat format) { + return forByteFormat(format, null, null); + } + + /** + * Creates a new editing object for {@link Byte}s 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 Byte.MIN_VALUE ({0}) and + * Byte.MAX_VALUE ({1}) values. + * @return The new editing object configured by the given integer format. + */ + public static NumberEditing forByteFormat(NumberFormat format, + String parseErrorMessage, String outOfRangeMessage) { + return new NumberEditing(format, parseErrorMessage, + outOfRangeMessage, Byte.class); + } + + /** + * Creates a new editing object for {@link Double}s 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#getNumberInstance() + */ + public static NumberEditing withDoubleDefaults() { + return withDoubleDefaults(null, null); + } + + /** + * Creates a new editing object for {@link Double}s 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 a double. + * @param outOfRangeMessage + * The validation message issued in case the input string + * represents a double whose value is out of range. Can be + * parameterized by the -Double.MAX_VALUE ({0}) and + * +Double.MAX_VALUE ({1}) values. + * @return The new editing object using the default validations and + * conversions for editing. + * + * @see NumberFormat#getNumberInstance() + */ + public static NumberEditing withDoubleDefaults( + String parseErrorMessage, String outOfRangeMessage) { + return new NumberEditing(NumberFormat.getNumberInstance(), + parseErrorMessage, outOfRangeMessage, Double.class); + } + + /** + * Creates a new editing object for {@link Double}s whose validations and + * conversions used for editing are based on the given number format. Uses + * the default validation messages. + * + * @param format + * The number format defining the validations and conversions + * used for editing. + * @return The new editing object configured by the given number format. + */ + public static NumberEditing forDoubleFormat(NumberFormat format) { + return forDoubleFormat(format, null, null); + } + + /** + * Creates a new editing object for {@link Double}s whose validations and + * conversions used for editing are based on the given number format. Uses + * the specified custom validation messages. + * + * @param format + * The number format defining the validations and conversions + * used for editing. + * @param parseErrorMessage + * The validation message issued in case the input string cannot + * be parsed to a double. + * @param outOfRangeMessage + * The validation message issued in case the input string + * represents a double whose value is out of range. Can be + * parameterized by the -Double.MAX_VALUE ({0}) and + * +Double.MAX_VALUE ({1}) values. + * @return The new editing object configured by the given number format. + */ + public static NumberEditing forDoubleFormat(NumberFormat format, + String parseErrorMessage, String outOfRangeMessage) { + return new NumberEditing(format, parseErrorMessage, + outOfRangeMessage, Double.class); + } + + /** + * Creates a new editing object for {@link Float}s 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#getNumberInstance() + */ + public static NumberEditing withFloatDefaults() { + return withFloatDefaults(null, null); + } + + /** + * Creates a new editing object for {@link Float}s 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 a double. + * @param outOfRangeMessage + * The validation message issued in case the input string + * represents a float whose value is out of range. Can be + * parameterized by the -Float.MAX_VALUE ({0}) and + * +Float.MAX_VALUE ({1}) values. + * @return The new editing object using the default validations and + * conversions for editing. + * + * @see NumberFormat#getNumberInstance() + */ + public static NumberEditing withFloatDefaults( + String parseErrorMessage, String outOfRangeMessage) { + return new NumberEditing(NumberFormat.getNumberInstance(), + parseErrorMessage, outOfRangeMessage, Float.class); + } + + /** + * Creates a new editing object for {@link Float}s whose validations and + * conversions used for editing are based on the given number format. Uses + * the default validation messages. + * + * @param format + * The number format defining the validations and conversions + * used for editing. + * @return The new editing object configured by the given number format. + */ + public static NumberEditing forFloatFormat(NumberFormat format) { + return forFloatFormat(format, null, null); + } + + /** + * Creates a new editing object for {@link Float}s whose validations and + * conversions used for editing are based on the given number format. Uses + * the specified custom validation messages. + * + * @param format + * The number format defining the validations and conversions + * used for editing. + * @param parseErrorMessage + * The validation message issued in case the input string cannot + * be parsed to a float. + * @param outOfRangeMessage + * The validation message issued in case the input string + * represents a float whose value is out of range. Can be + * parameterized by the -Float.MAX_VALUE ({0}) and + * +Float.MAX_VALUE ({1}) values. + * @return The new editing object configured by the given number format. + */ + public static NumberEditing forFloatFormat(NumberFormat format, + String parseErrorMessage, String outOfRangeMessage) { + return new NumberEditing(format, parseErrorMessage, + outOfRangeMessage, Float.class); + } + + /** + * Creates a new editing object for {@link BigInteger}s 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 NumberEditing withBigIntegerDefaults() { + return withBigIntegerDefaults(null); + } + + /** + * Creates a new editing object for {@link BigInteger}s which defaults the + * validations and conversions used for editing based on the platform's + * locale. Uses the specified custom validation message. + * + * @param parseErrorMessage + * The validation message issued in case the input string cannot + * be parsed to a BigInteger. + * @return The new editing object using the default validations and + * conversions for editing. + * + * @see NumberFormat#getIntegerInstance() + */ + public static NumberEditing withBigIntegerDefaults( + String parseErrorMessage) { + return new NumberEditing(NumberFormat.getIntegerInstance(), + parseErrorMessage, null, BigInteger.class); + } + + /** + * Creates a new editing object for {@link BigInteger}s whose validations + * and conversions used for editing are based on the given BigInteger + * format. Uses the default validation messages. + * + * @param format + * The BigInteger format defining the validations and conversions + * used for editing. + * @return The new editing object configured by the given BigInteger format. + */ + public static NumberEditing forBigIntegerFormat( + NumberFormat format) { + return forBigIntegerFormat(format, null); + } + + /** + * Creates a new editing object for {@link BigInteger}s whose validations + * and conversions used for editing are based on the given BigInteger + * format. Uses the specified custom validation message. + * + * @param format + * The BigInteger format defining the validations and conversions + * used for editing. + * @param parseErrorMessage + * The validation message issued in case the input string cannot + * be parsed to a BigInteger. + * @return The new editing object configured by the given BigInteger format. + */ + public static NumberEditing forBigIntegerFormat( + NumberFormat format, String parseErrorMessage) { + return new NumberEditing(format, parseErrorMessage, null, + BigInteger.class); + } + + /** + * Creates a new editing object for {@link BigDecimal}s 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#getNumberInstance() + */ + public static NumberEditing withBigDecimalDefaults() { + return withBigDecimalDefaults(null); + } + + /** + * Creates a new editing object for {@link BigDecimal}s which defaults the + * validations and conversions used for editing based on the platform's + * locale. Uses the specified custom validation message. + * + * @param parseErrorMessage + * The validation message issued in case the input string cannot + * be parsed to a BigDecimal. + * @return The new editing object using the default validations and + * conversions for editing. + * + * @see NumberFormat#getNumberInstance() + */ + public static NumberEditing withBigDecimalDefaults( + String parseErrorMessage) { + return new NumberEditing(NumberFormat.getNumberInstance(), + parseErrorMessage, null, BigDecimal.class); + } + + /** + * Creates a new editing object for {@link BigDecimal}s whose validations + * and conversions used for editing are based on the given BigDecimal + * format. Uses the default validation messages. + * + * @param format + * The BigDecimal format defining the validations and conversions + * used for editing. + * @return The new editing object configured by the given BigDecimal format. + */ + public static NumberEditing forBigDecimalFormat( + NumberFormat format) { + return forBigDecimalFormat(format, null); + } + + /** + * Creates a new editing object for {@link BigDecimal}s whose validations + * and conversions used for editing are based on the given BigDecimal + * format. Uses the specified custom validation message. + * + * @param format + * The BigDecimal format defining the validations and conversions + * used for editing. + * @param parseErrorMessage + * The validation message issued in case the input string cannot + * be parsed to a BigDecimal. + * @return The new editing object configured by the given BigDecimal format. + */ + public static NumberEditing forBigDecimalFormat( + NumberFormat format, String parseErrorMessage) { + return new NumberEditing(format, parseErrorMessage, null, + BigDecimal.class); + } + + public StringConstraints targetConstraints() { + return (StringConstraints) super.targetConstraints(); + } + + public NumberConstraints modelConstraints() { + return (NumberConstraints) super.modelConstraints(); + } + + public NumberConstraints beforeSetModelConstraints() { + return (NumberConstraints) super.beforeSetModelConstraints(); + } + + protected StringConstraints createTargetConstraints() { + return new StringConstraints(); + } + + @SuppressWarnings("unchecked") + protected NumberConstraints createModelConstraints() { + return (NumberConstraints) createModelConstraints(numberType) + .numberFormat(displayFormat); + } + + @SuppressWarnings("unchecked") + protected NumberConstraints createBeforeSetModelConstraints() { + return (NumberConstraints) createModelConstraints(numberType) + .numberFormat(displayFormat); + } + + private NumberConstraints createModelConstraints(Class numberType) { + if (Long.class.equals(numberType)) { + return NumberConstraints.forLong(); + } else if (Integer.class.equals(numberType)) { + return NumberConstraints.forInteger(); + } else if (Short.class.equals(numberType)) { + return NumberConstraints.forShort(); + } else if (Byte.class.equals(numberType)) { + return NumberConstraints.forByte(); + } else if (Double.class.equals(numberType)) { + return NumberConstraints.forDouble(); + } else if (Float.class.equals(numberType)) { + return NumberConstraints.forFloat(); + } else if (BigInteger.class.equals(numberType)) { + return NumberConstraints.forBigInteger(); + } else if (BigDecimal.class.equals(numberType)) { + return NumberConstraints.forBigDecimal(); + } else { + throw new IllegalArgumentException( + "Unsupported number type: " + numberType); //$NON-NLS-1$ + } + } +} 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,43 @@ +/******************************************************************************* + * 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.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; + +/** + * @since 1.3 + */ +public class NonNullValidator implements IValidator { + + private static final String NON_NULL_VALIDATION_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_NON_NULL); + + private final String validationMessage; + + /** + * @param validationMessage + */ + public NonNullValidator(String validationMessage) { + this.validationMessage = validationMessage != null ? validationMessage + : NON_NULL_VALIDATION_MESSAGE; + } + + public IStatus validate(Object value) { + if (value == null) { + return ValidationStatus.error(validationMessage); + } + return ValidationStatus.ok(); + } +} Index: src/org/eclipse/core/internal/databinding/conversion/BooleanToStringConverter.java =================================================================== RCS file: src/org/eclipse/core/internal/databinding/conversion/BooleanToStringConverter.java diff -N src/org/eclipse/core/internal/databinding/conversion/BooleanToStringConverter.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/internal/databinding/conversion/BooleanToStringConverter.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,60 @@ +/******************************************************************************* + * 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 BooleanToStringConverter extends Converter { + + // Those are the defaults used up to now in UpdateStrategy where a simple + // ObjectToStringConverter was used for converting booleans to strings. + // FIXME: Should/may we change this to convert to a localized string? + private String trueValue = Boolean.TRUE.toString(); + private String falseValue = Boolean.FALSE.toString(); + + /** + * Creates a new converter which converts a boolean to its default string + * representation. + * + * @param booleanType + * The boolean type. Must be one of {@code Boolean.TYPE} or + * {@code Boolean.class}. + */ + public BooleanToStringConverter(Class booleanType) { + super(booleanType, String.class); + } + + /** + * Sets the string values to which a true and + * false value should be converted. + * + * @param trueValue + * The string to which to convert a true value. + * @param falseValue + * The string to which to convert a false value. + */ + public final void setTargetStrings(String trueValue, String falseValue) { + this.trueValue = trueValue; + this.falseValue = falseValue; + } + + public Object convert(Object source) { + Boolean value = (Boolean) source; + if (value != null) { + return value.booleanValue() ? trueValue : falseValue; + } + return ""; //$NON-NLS-1$ + } +} Index: src/org/eclipse/core/internal/databinding/validation/NonEmptyStringValidator.java =================================================================== RCS file: src/org/eclipse/core/internal/databinding/validation/NonEmptyStringValidator.java diff -N src/org/eclipse/core/internal/databinding/validation/NonEmptyStringValidator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/internal/databinding/validation/NonEmptyStringValidator.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,51 @@ +/******************************************************************************* + * 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.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; + +/** + * @since 1.3 + */ +public class NonEmptyStringValidator implements IValidator { + + private static final String NON_EMPTY_STRING_VALIDATION_MESSAGE = BindingMessages + .getString(BindingMessages.VALIDATE_NON_EMPTY_STRING); + + private final String validationMessage; + + /** + * + */ + public NonEmptyStringValidator() { + this(null); + } + + /** + * @param validationMessage + */ + public NonEmptyStringValidator(String validationMessage) { + this.validationMessage = validationMessage != null ? validationMessage + : NON_EMPTY_STRING_VALIDATION_MESSAGE; + } + + public IStatus validate(Object value) { + String input = (String) value; + if (input == null || input.length() == 0) { + return ValidationStatus.error(validationMessage); + } + return ValidationStatus.ok(); + } +} 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 >= stripStart + && Character.isWhitespace(string.charAt(stripEnd))) { + stripEnd--; + } + + if (stripStart <= stripEnd) { + string = string.substring(stripStart, stripEnd + 1); + } else { + string = ""; //$NON-NLS-1$ + } + } + + if (stripToNull && string != null && string.length() == 0) { + string = null; + } + + return string; + } +} #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 1 Mar 2010 21:29:26 -0000 @@ -12,6 +12,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Ovidio Mallo - bug 183055 ******************************************************************************/ /** @@ -21,13 +22,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)); } } Index: .settings/org.eclipse.jdt.ui.prefs =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface.tests.databinding/.settings/org.eclipse.jdt.ui.prefs,v retrieving revision 1.6 diff -u -r1.6 org.eclipse.jdt.ui.prefs --- .settings/org.eclipse.jdt.ui.prefs 16 Feb 2009 23:03:22 -0000 1.6 +++ .settings/org.eclipse.jdt.ui.prefs 1 Mar 2010 21:29:26 -0000 @@ -1,4 +1,4 @@ -#Tue Feb 10 16:06:05 MST 2009 +#Thu Jan 07 19:11:15 CET 2010 cleanup.add_default_serial_version_id=true cleanup.add_generated_serial_version_id=false cleanup.add_missing_annotations=true @@ -51,7 +51,7 @@ cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile cleanup_settings_version=2 eclipse.preferences.version=1 -editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=false formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile formatter_settings_version=11 org.eclipse.jdt.ui.exception.name=e