Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 82783 Details for
Bug 197807
[DataBinding] Common Constraint Validator with Dependency Support
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
[patch]
Inter-Observable Validation Dependency Support with Snippet
DependentValidationConstraints_proto1.patch (text/plain), 81.06 KB, created by
Matt Carter
on 2007-11-13 11:57:08 EST
(
hide
)
Description:
Inter-Observable Validation Dependency Support with Snippet
Filename:
MIME Type:
Creator:
Matt Carter
Created:
2007-11-13 11:57:08 EST
Size:
81.06 KB
patch
obsolete
>### Eclipse Workspace Patch 1.0 >#P org.eclipse.jface.examples.databinding >Index: src/org/eclipse/jface/examples/databinding/snippets/Snippet016DependentConstraints.java >=================================================================== >RCS file: src/org/eclipse/jface/examples/databinding/snippets/Snippet016DependentConstraints.java >diff -N src/org/eclipse/jface/examples/databinding/snippets/Snippet016DependentConstraints.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/jface/examples/databinding/snippets/Snippet016DependentConstraints.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,657 @@ >+/******************************************************************************* >+ * Copyright (c) 2007 Matt Carter. >+ * 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: >+ * Matt Carter - initial API and implementation >+ *******************************************************************************/ >+package org.eclipse.jface.examples.databinding.snippets; >+ >+import java.beans.PropertyChangeListener; >+import java.beans.PropertyChangeSupport; >+import java.io.PrintWriter; >+import java.io.StringWriter; >+import java.util.HashMap; >+import java.util.Iterator; >+import java.util.Map; >+import java.util.Set; >+ >+import org.eclipse.core.databinding.Binding; >+import org.eclipse.core.databinding.DataBindingContext; >+import org.eclipse.core.databinding.UpdateValueStrategy; >+import org.eclipse.core.databinding.beans.BeansObservables; >+import org.eclipse.core.databinding.constraints.ConstrainedObservableGroup; >+import org.eclipse.core.databinding.constraints.ConstraintBindingContext; >+import org.eclipse.core.databinding.constraints.ConstraintValidator; >+import org.eclipse.core.databinding.constraints.INamedObservableProvider; >+import org.eclipse.core.databinding.constraints.ObservableGroup; >+import org.eclipse.core.databinding.constraints.ObservableStringPair; >+import org.eclipse.core.databinding.observable.IObservable; >+import org.eclipse.core.databinding.observable.Realm; >+import org.eclipse.core.databinding.observable.map.IObservableMap; >+import org.eclipse.core.databinding.observable.value.IObservableValue; >+import org.eclipse.core.internal.databinding.internal.beans.JavaBeanObservableValue; >+import org.eclipse.core.runtime.IStatus; >+import org.eclipse.jface.databinding.swt.SWTObservables; >+import org.eclipse.jface.internal.databinding.provisional.swt.ControlUpdater; >+import org.eclipse.swt.SWT; >+import org.eclipse.swt.events.SelectionAdapter; >+import org.eclipse.swt.events.SelectionEvent; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.swt.graphics.Font; >+import org.eclipse.swt.graphics.FontData; >+import org.eclipse.swt.layout.GridData; >+import org.eclipse.swt.layout.GridLayout; >+import org.eclipse.swt.widgets.Button; >+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; >+ >+/** >+ * Demonstrates creation of dependent constraints between bound observables. >+ * >+ * @author matt >+ * >+ */ >+public class Snippet016DependentConstraints { >+ >+ public static void main(String[] args) { >+ new Snippet016DependentConstraints().run(); >+ } >+ >+ Shell shell; >+ Realm realm; >+ DataBindingContext dbc; >+ ConstraintBindingContext cbc; >+ private Map/*<String, IObservable>*/ targetObservables = new HashMap(); >+ Model model = new Model(); >+ >+ final INamedObservableProvider namedObservableProvider = new INamedObservableProvider() { >+ >+ public IObservable getObservable(String observableName) >+ throws IllegalArgumentException { >+ return (IObservable) targetObservables.get(observableName); >+ } >+ >+ public ObservableStringPair getObservableAndDescription( >+ String observableName) throws IllegalArgumentException { >+ return new ObservableStringPair( >+ (IObservable) targetObservables >+ .get(observableName), observableName); >+ } >+ >+ public String getObservableDescription(String observableName) >+ throws IllegalArgumentException { >+ return observableName; >+ } >+ >+ }; >+ >+ Font boldFont; >+ >+ public void run() { >+ final Display display = Display.getDefault(); >+ shell = new Shell(display); >+ shell.setLayout(new GridLayout(3, false)); >+ shell.setText("Dependent Constraints Example Snippet"); >+ realm = SWTObservables.getRealm(display); >+ dbc = new DataBindingContext(realm); >+ cbc = new ConstraintBindingContext(); >+ // Get bold font >+ FontData sysFontData = display.getSystemFont().getFontData()[0]; >+ boldFont = new Font(display, sysFontData >+ .getName(), sysFontData.getHeight(), SWT.BOLD); >+ >+ // Create demo controls and bindings >+ ConstraintValidator cv; >+ Group group; >+ ConstrainedObservableGroup observableGroup; >+ group = mkGroup(shell, "Required Field"); >+ cv = new ConstraintValidator(realm, namedObservableProvider); >+ cv.setRequiredField(true); >+ createDemoControl("value1", group, cv, "Required Field", null); >+ >+ group = mkGroup(shell, "All required if any field set"); >+ observableGroup = cbc.createObservableGroup(realm, >+ namedObservableProvider); >+ observableGroup.setAllRequiredIfAny(true); >+ cv = new ConstraintValidator(realm, namedObservableProvider, >+ observableGroup); >+ createDemoControl("value2", group, cv, "Deliver To", observableGroup); >+ cv = new ConstraintValidator(realm, namedObservableProvider, >+ observableGroup); >+ createDemoControl("value3", group, cv, "Delivery Address", >+ observableGroup); >+ cv = new ConstraintValidator(realm, namedObservableProvider, >+ observableGroup); >+ createDemoControl("value4", group, cv, "Delivery Zip Code", >+ observableGroup); >+ >+ group = mkGroup(shell, "One or more required"); >+ observableGroup = cbc.createObservableGroup(realm, >+ namedObservableProvider); >+ observableGroup.setOneOrMoreRequired(true); >+ cv = new ConstraintValidator(realm, namedObservableProvider, >+ observableGroup); >+ createDemoControl("value5", group, cv, "Set this", observableGroup); >+ cv = new ConstraintValidator(realm, namedObservableProvider, >+ observableGroup); >+ createDemoControl("value6", group, cv, "Or this", observableGroup); >+ >+ group = mkGroup(shell, "String length restrictions"); >+ cv = new ConstraintValidator(realm, namedObservableProvider); >+ cv.setRange(true, 5L, 5L, "5 characters exactly please!"); >+ createDemoControl("value7", group, cv, "Exact length of 5 chars", null); >+ cv = new ConstraintValidator(realm, namedObservableProvider); >+ cv.setRange(true, 6L, 12L, "6-12 characters please."); >+ createDemoControl("value8", group, cv, "value8: 6-12 char password", >+ null); >+ >+ group = mkGroup(shell, "Numeric range restrictions"); >+ cv = new ConstraintValidator(realm, namedObservableProvider); >+ cv.setRange(true, 0L, 255L, "Integer between 0 and 255 please"); >+ createDemoControl("int1", group, cv, "Integer between 0 and 255", null); >+ cv = new ConstraintValidator(realm, namedObservableProvider); >+ cv.setRange(true, -5L, 5L, null); >+ createDemoControl("int2", group, cv, "Number between -5 and +5 please", >+ null); >+ >+ group = mkGroup(shell, >+ "Required/Not Allowed if another field(s) are set"); >+ cv = new ConstraintValidator(realm, namedObservableProvider); >+ cv.setNotAllowedIf(new String[] { "value8" }, true); >+ createDemoControl("value9", group, cv, >+ "This field is not allowed if the password field is set", null); >+ cv = new ConstraintValidator(realm, namedObservableProvider); >+ cv.setRequiredIf(new String[] { "value8" }, true); >+ createDemoControl("value10", group, cv, >+ "This field is required if the password field is set", null); >+ >+ group = mkGroup(shell, "4 fields for the next example"); >+ observableGroup = cbc.createObservableGroup(realm, >+ namedObservableProvider); >+ cv = new ConstraintValidator(realm, namedObservableProvider, >+ observableGroup); >+ createDemoControl("value11", group, cv, "Value A", observableGroup); >+ cv = new ConstraintValidator(realm, namedObservableProvider, >+ observableGroup); >+ createDemoControl("value12", group, cv, "Value B", observableGroup); >+ cv = new ConstraintValidator(realm, namedObservableProvider, >+ observableGroup); >+ createDemoControl("value13", group, cv, "Value C", observableGroup); >+ cv = new ConstraintValidator(realm, namedObservableProvider, >+ observableGroup); >+ createDemoControl("value14", group, cv, "Value D", observableGroup); >+ >+ group = mkGroup(shell, "All fields in above group required if.."); >+ >+ // We create this control without the helper method createDemoControl() >+ // to illustrate the simplest form of creation. >+ Button checkbox1 = new Button(group, SWT.CHECK); >+ checkbox1 >+ .setText("Require all fields A-D if checked, which are otherwise optional"); >+ IObservableValue observable = SWTObservables >+ .observeSelection(checkbox1); >+ // We maintain a map of observables by name. >+ targetObservables.put("mandate1", observable); >+ >+ // The variable 'observableGroup' contains the A-D controls above. So we >+ // apply an >+ // "All Required If" constraint to the group, enabling the constraint >+ // when our >+ // 'mandate1' checkbox is checked. >+ // The setBooleanFalseIsNull() call is important here. >+ // If true it means that a boolean value when false is considered to be >+ // "not present" >+ // in the same way as null or an empty string. If it is set to false, >+ // because both false and true are non-null, a value is always >+ // considered to be present. >+ observableGroup.setBooleanFalseIsNull(true); >+ observableGroup.setAllRequiredIf(new String[] { "mandate1" }, false); >+ >+ // Bind our checkbox to the model property "mandate1". >+ dbc.bindValue(observable, BeansObservables.observeValue(realm, model, >+ "mandate1"), null, null); >+ // (We could also depend on the mandate1 control from other controls >+ // without binding it to a model, just as long as it can be resolved >+ // through the INamedObservableProvider like the other controls). The >+ // validation dependencies are set up between observables, but they >+ // affect bindings. >+ >+ // Last one. Lets go nuts and add a checkbox which if ticked, disallows >+ // a value >+ // in every single other field. >+ >+ Button checkbox2 = new Button(group, SWT.CHECK); >+ checkbox2.setText("Disallow values in A-D"); >+ observable = SWTObservables.observeSelection(checkbox2); >+ // Bind our checkbox to the model property "excludeAll". >+ dbc.bindValue(observable, >+ BeansObservables.observeValue(realm, model, >+ "excludeAll"), null, null); >+ targetObservables.put("excludeAll", observable); >+ // Add a "None Allowed If Present" constraint to A-D observables >+ observableGroup.setNoneAllowedIf(new String[] { "excludeAll" }, false); >+ >+ >+ // Finally we activate all the constraints. They have to resolve >+ // references to each other. >+ // This is done because observables referenced by some constraints >+ // may not exist until the very end when all observables have been created. >+ cbc.activateDependentConstraints(); >+ >+ // Create a button which will dump validation status to the console >+ Button btnDump = new Button(shell, SWT.DEFAULT); >+ btnDump.setText("Dump status map (to console)"); >+ btnDump.addSelectionListener(new SelectionAdapter() { >+ public void widgetSelected(SelectionEvent e) { >+ dumpValidationStatusMap(dbc); >+ } >+ }); >+ // Display the shell >+ shell.pack(); >+ shell.layout(); >+ shell.open(); >+ >+ /* >+ * The following method call is needed to work around a very strange >+ * initial validation status bug. The method just calls >+ * binding.validateTargetToModel() on each binding via asyncExec(). >+ * >+ * Take it out and see what happens.... >+ * >+ * One binding (perhaps the last one?) has the wrong initial validation >+ * status (== OK), but as soon as you change a value, it corrects. Click >+ * "Dump" to verify it is status and not a ControlUpdater. >+ * >+ * This workaround has existed for a while.. and I've been unable to >+ * track down the cause, whether it's in this code or deeper in JDB. >+ * - Matt >+ */ >+ ConstraintBindingContext.revalidateAllBindings(dbc, realm); >+ >+ // Main loop >+ while (!shell.isDisposed()) { >+ if (!display.readAndDispatch()) >+ display.sleep(); >+ } >+ shell.dispose(); >+ dbc.dispose(); >+ boldFont.dispose(); >+ display.dispose(); >+ } >+ >+ private static Group mkGroup(Composite parent, String label) { >+ Group group = new Group(parent, SWT.NONE); >+ group.setText(label); >+ group >+ .setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, >+ 3, 1)); >+ group.setLayout(new GridLayout(3, false)); >+ return group; >+ } >+ >+ private IObservableValue createDemoControl(String modelProperty, >+ Composite parent, ConstraintValidator constraintValidator, >+ String label, ObservableGroup group) { >+ Label l = new Label(parent, SWT.LEFT); >+ l.setText(label); >+ Text text = new Text(parent, SWT.BORDER); >+ GridData gd = new GridData(); >+ gd.widthHint = 100; >+ text.setLayoutData(gd); >+ UpdateValueStrategy targetToModel = new UpdateValueStrategy( >+ UpdateValueStrategy.POLICY_UPDATE); >+ UpdateValueStrategy modelToTarget = new UpdateValueStrategy( >+ UpdateValueStrategy.POLICY_UPDATE); >+ // Bind observables (with constraint validator) >+ targetToModel.setAfterConvertValidator(constraintValidator); >+ IObservableValue controlObservable = SWTObservables.observeText(text, >+ SWT.Modify); >+ targetObservables.put(modelProperty, controlObservable); >+ Binding binding = cbc.bindValue(dbc, controlObservable, >+ BeansObservables.observeValue(realm, model, modelProperty), >+ targetToModel, modelToTarget, constraintValidator, false); >+ // Create status label showing current validation status >+ Label statusLabel = new Label(parent, SWT.LEFT); >+ statusLabel.setBackground(GREY); >+ statusLabel.setFont(boldFont); >+ gd = new GridData(SWT.FILL, SWT.CENTER, true, false); >+ gd.widthHint = 400; >+ statusLabel.setLayoutData(gd); >+ addValidationIndicationToControl(dbc, statusLabel, text, binding); >+ // If an observable group was passed, add this control observable to the >+ // group with a name. >+ if (group != null) >+ group.addObservable(controlObservable, modelProperty); >+ >+ return controlObservable; >+ } >+ >+ /** >+ * Model. >+ * >+ * @author matt >+ * >+ */ >+ private static class Model { >+ >+ private final PropertyChangeSupport pcs = new PropertyChangeSupport( >+ this); >+ >+ String value1 = null; >+ String value2 = ""; >+ String value3 = "Model value 3"; >+ String value4 = "Model value 4"; >+ String value5 = null; >+ String value6 = null; >+ String value7 = "Model value 7"; >+ String value8 = "Model value 8"; >+ String value9 = "Model value 9"; >+ String value10 = ""; >+ String value11 = "A"; >+ String value12 = null; >+ String value13 = "C"; >+ String value14 = "D"; >+ int int1 = 4096; >+ int int2 = 65536; >+ boolean mandate1; >+ boolean excludeAll; >+ >+ public boolean isExcludeAll() { >+ return excludeAll; >+ } >+ >+ public void setExcludeAll(boolean excludeAll) { >+ boolean old = excludeAll; >+ this.excludeAll = excludeAll; >+ pcs.firePropertyChange("excludeAll", old, excludeAll); >+ } >+ >+ public boolean isMandate1() { >+ return mandate1; >+ } >+ >+ public void setMandate1(boolean mandate1) { >+ boolean old = mandate1; >+ this.mandate1 = mandate1; >+ pcs.firePropertyChange("mandate1", old, mandate1); >+ } >+ >+ public int getInt1() { >+ return int1; >+ } >+ >+ public void setInt1(int int1) { >+ int old = int1; >+ this.int1 = int1; >+ pcs.firePropertyChange("int1", old, int1); >+ } >+ >+ public int getInt2() { >+ return int2; >+ } >+ >+ public void setInt2(int int2) { >+ int old = int2; >+ this.int2 = int2; >+ pcs.firePropertyChange("int2", old, int2); >+ } >+ >+ public String getValue1() { >+ return value1; >+ } >+ >+ public void setValue1(String value1) { >+ String old = value1; >+ this.value1 = value1; >+ pcs.firePropertyChange("value1", old, value1); >+ } >+ >+ public String getValue2() { >+ return value2; >+ } >+ >+ public void setValue2(String value2) { >+ String old = value2; >+ this.value2 = value2; >+ pcs.firePropertyChange("value2", old, value2); >+ } >+ >+ public String getValue3() { >+ return value3; >+ } >+ >+ public void setValue3(String value3) { >+ String old = value3; >+ this.value3 = value3; >+ pcs.firePropertyChange("value3", old, value3); >+ } >+ >+ public String getValue4() { >+ return value4; >+ } >+ >+ public void setValue4(String value4) { >+ String old = value4; >+ this.value4 = value4; >+ pcs.firePropertyChange("value4", old, value4); >+ } >+ >+ public String getValue5() { >+ return value5; >+ } >+ >+ public void setValue5(String value5) { >+ String old = value5; >+ this.value5 = value5; >+ pcs.firePropertyChange("value5", old, value5); >+ } >+ >+ public String getValue6() { >+ return value6; >+ } >+ >+ public void setValue6(String value6) { >+ String old = value6; >+ this.value6 = value6; >+ pcs.firePropertyChange("value6", old, value6); >+ } >+ >+ public String getValue7() { >+ return value7; >+ } >+ >+ public void setValue7(String value7) { >+ String old = value7; >+ this.value7 = value7; >+ pcs.firePropertyChange("value7", old, value7); >+ } >+ >+ public String getValue8() { >+ return value8; >+ } >+ >+ public void setValue8(String value8) { >+ String old = value8; >+ this.value8 = value8; >+ pcs.firePropertyChange("value8", old, value8); >+ } >+ >+ public String getValue9() { >+ return value9; >+ } >+ >+ public void setValue9(String value9) { >+ String old = value9; >+ this.value9 = value9; >+ pcs.firePropertyChange("value9", old, value9); >+ } >+ >+ public String getValue10() { >+ return value10; >+ } >+ >+ public void setValue10(String value10) { >+ String old = value10; >+ this.value10 = value10; >+ pcs.firePropertyChange("value10", old, value10); >+ } >+ >+ public void addPropertyChangeListener(PropertyChangeListener listener) { >+ this.pcs.addPropertyChangeListener(listener); >+ } >+ >+ public void removePropertyChangeListener(PropertyChangeListener listener) { >+ this.pcs.removePropertyChangeListener(listener); >+ } >+ >+ public String getValue11() { >+ return value11; >+ } >+ >+ public void setValue11(String value11) { >+ String old = value11; >+ this.value11 = value11; >+ pcs.firePropertyChange("value11", old, value11); >+ } >+ >+ public String getValue12() { >+ return value12; >+ } >+ >+ public void setValue12(String value12) { >+ String old = value12; >+ this.value12 = value12; >+ pcs.firePropertyChange("value12", old, value12); >+ } >+ >+ public String getValue13() { >+ return value13; >+ } >+ >+ public void setValue13(String value13) { >+ String old = value13; >+ this.value13 = value13; >+ pcs.firePropertyChange("value13", old, value13); >+ } >+ >+ public String getValue14() { >+ return value14; >+ } >+ >+ public void setValue14(String value14) { >+ String old = value14; >+ this.value14 = value14; >+ pcs.firePropertyChange("value14", old, value14); >+ } >+ >+ } >+ >+ /** >+ * Visually indicate validation failure on the given Label control. >+ */ >+ private static class ValidationStatusColourUpdater extends ControlUpdater { >+ final Control fControl; >+ final IObservableValue validationStatus; >+ final Color origColour; >+ final boolean foreground; >+ >+ public ValidationStatusColourUpdater(Control fControl, Binding binding, >+ boolean foreground) { >+ super(fControl); >+ this.fControl = fControl; >+ validationStatus = binding.getValidationStatus(); >+ this.foreground = foreground; >+ this.origColour = foreground ? fControl.getForeground() : fControl >+ .getBackground(); >+ } >+ >+ protected void updateControl() { >+ IStatus status = (IStatus) validationStatus.getValue(); >+ if (status.isOK()) { >+ if (foreground) >+ fControl.setForeground(origColour); >+ else >+ fControl.setBackground(origColour); >+ } else { >+ if (foreground) >+ fControl.setForeground(RED); >+ else >+ fControl.setBackground(RED); >+ } >+ ((Label) fControl).setText(status.getMessage()); >+ } >+ }; >+ >+ static final private Display display = Display.getDefault(); >+ static final public Color RED = display.getSystemColor(SWT.COLOR_RED); >+ static final public Color GREY = display.getSystemColor(SWT.COLOR_GRAY); >+ >+ private void addValidationIndicationToControl( >+ DataBindingContext dbc, Label label, Control control, >+ Binding binding) { >+ Control target = label; >+ if (label == null && control instanceof Button) >+ target = control; // Check box; special case >+ // Validation status colouring on label >+ new ValidationStatusColourUpdater(target, binding, true); >+ // Set tooltipText to validation status message >+ dbc.bindValue(SWTObservables.observeTooltipText(target), binding >+ .getValidationStatus(), new UpdateValueStrategy( >+ UpdateValueStrategy.POLICY_NEVER)/* >+ * Do not try to update >+ * model >+ */, null); >+ } >+ >+ private static void dumpValidationStatusMap(DataBindingContext dbc) { >+ StringBuffer sb = new StringBuffer(); >+ sb.append("Validation status dump: \n"); >+ IObservableMap map = dbc.getValidationStatusMap(); >+ Set entrySet = map.entrySet(); >+ sb.append("# bindings: " + entrySet.size() + "\n"); >+ Iterator iter = entrySet.iterator(); >+ while(iter.hasNext()) { >+ Map.Entry entry = (Map.Entry) iter.next(); >+ final Binding binding = (Binding) entry.getKey(); >+ IStatus status = (IStatus) entry.getValue(); >+ IObservable model = binding.getModel(); >+ String modelName = "<" + model.getClass().getSimpleName() + ">"; >+ if (model instanceof JavaBeanObservableValue) >+ modelName = "Property " >+ + ((JavaBeanObservableValue) model) >+ .getPropertyDescriptor().getName(); >+ sb.append(" Binding: ").append(modelName); >+ sb.append(" has status: ").append(status); >+ sb.append(", Value=["); >+ IObservable target = binding.getTarget(); >+ if (target instanceof IObservableValue) { >+ Object value = ((IObservableValue) binding.getTarget()) >+ .getValue(); >+ sb.append(value == null ? "(null)" : value); >+ sb.append("], type=["); >+ sb.append(value == null ? "(null)" : value.getClass() >+ .getSimpleName()); >+ } else >+ sb.append("?, Not an IObservableValue"); >+ sb.append("]"); >+ Throwable ex = status.getException(); >+ if (ex != null) { >+ StringWriter sw = new StringWriter(); >+ PrintWriter pw = new PrintWriter(sw); >+ ex.printStackTrace(pw); >+ sb.append("\nException: ").append(sw.toString()); >+ } >+ sb.append("\n"); >+ } >+ System.out.println(sb.toString()); >+ } >+} >#P org.eclipse.core.databinding >Index: META-INF/MANIFEST.MF >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/META-INF/MANIFEST.MF,v >retrieving revision 1.11 >diff -u -r1.11 MANIFEST.MF >--- META-INF/MANIFEST.MF 20 Oct 2007 20:44:43 -0000 1.11 >+++ META-INF/MANIFEST.MF 13 Nov 2007 16:56:16 -0000 >@@ -7,6 +7,7 @@ > Bundle-Vendor: %providerName > Bundle-Localization: plugin > Export-Package: org.eclipse.core.databinding, >+ org.eclipse.core.databinding.constraints, > org.eclipse.core.databinding.conversion;x-internal:=false, > org.eclipse.core.databinding.observable, > org.eclipse.core.databinding.observable.list;x-internal:=false, >Index: src/org/eclipse/core/databinding/constraints/ConstraintValidator.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/constraints/ConstraintValidator.java >diff -N src/org/eclipse/core/databinding/constraints/ConstraintValidator.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/constraints/ConstraintValidator.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,547 @@ >+/******************************************************************************* >+ * Copyright (c) 2007 Matt Carter. >+ * 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: >+ * Matt Carter - initial API and implementation >+ *******************************************************************************/ >+package org.eclipse.core.databinding.constraints; >+ >+import java.math.BigDecimal; >+import java.math.BigInteger; >+ >+import org.eclipse.core.databinding.Binding; >+import org.eclipse.core.databinding.observable.Realm; >+import org.eclipse.core.databinding.observable.value.IObservableValue; >+import org.eclipse.core.databinding.validation.IValidator; >+import org.eclipse.core.databinding.validation.ValidationStatus; >+import org.eclipse.core.runtime.IStatus; >+import org.eclipse.core.runtime.Status; >+ >+/** >+ * A general constraint validator that is capable of validating the following >+ * conditions on or between observables: >+ * <ul> >+ * <li>Range (Minimum and/or Maximum numeric values)</li> >+ * <li>Field is required (Required field)</li> >+ * <li>Field is required if any of the referenced fields are also set.</li> >+ * <li>Field is not allowed if any of the referenced fields are also set.</li> >+ * <li>All fields are required if any referenced fields are set.</li> >+ * <li>One or more fields are required if any referenced fields are set.</li> >+ * <li>Group to fields Constraint: All fields are required to be set if any of >+ * the referenced fields are set.</li> >+ * <li>Group to fields Constraint: One or more fields are required to be set if >+ * any of the referenced fields are set.</li> >+ * </ul> >+ * >+ * TODO: Merge status into MultiStatus. >+ * >+ */ >+public class ConstraintValidator implements IValidator { >+ >+ protected final Realm realm; >+ >+ private final INamedObservableProvider context; >+ >+ /* Single observable constraints */ >+ private boolean requiredField = false; >+ >+ private boolean rangeEnabled = false; >+ private long rangeMin = Long.MIN_VALUE; >+ private long rangeMax = Long.MAX_VALUE; >+ private String rangeMessage = null; >+ >+ /* One to Many observable constraints */ >+ private String[] requiredIfTargetNames = null; >+ private boolean requiredIfBooleanFalseIsNull = false; >+ private ObservableGroup requiredIf = null; >+ >+ private String[] notAllowedIfTargetNames = null; >+ private boolean notAllowedIfBooleanFalseIsNull = false; >+ private ObservableGroup notAllowedIf = null; >+ >+ /* Group constraints */ >+ protected ConstrainedObservableGroup groupObservables = null; >+ >+ // Binding to validate >+ private Binding binding = null; >+ private boolean excludedFromGroupConstraints; >+ >+ // Debug >+ /* >+ * public void makeDirty() { if (allRequiredIf != null) >+ * allRequiredIf.makeDirty(); if (oneOrMoreRequiredIf != null) >+ * oneOrMoreRequiredIf.makeDirty(); } >+ */ >+ >+ /** >+ * >+ */ >+ public void validateTargetToModel() { >+ /* >+ * if(allRequiredIf != null) { allRequiredIf.allPresent().getValue(); >+ * allRequiredIf.anyPresent().getValue(); } if(oneOrMoreRequiredIf != >+ * null) { oneOrMoreRequiredIf.allPresent().getValue(); >+ * oneOrMoreRequiredIf.anyPresent().getValue(); } >+ */ >+ realm.asyncExec(new Runnable() { >+ public void run() { >+ binding.validateTargetToModel(); >+ } >+ }); >+ } >+ >+ // Hint strings >+ private final static String requiredFieldHint = "Required field"; //$NON-NLS-1$ >+ private final static String requiredIfFieldHintPrefix = "This field is required with the field(s) "; //$NON-NLS-1$ >+ private final static String requiredIfFieldHintSuffix = " set"; //$NON-NLS-1$ >+ private final static String notAllowedIfFieldHintPrefix = "This field cannot be set with the field(s) "; //$NON-NLS-1$ >+ private final static String notAllowedIfFieldHintSuffix = " set"; //$NON-NLS-1$ >+ >+ // Debug tool. Associate name of observable with this validator. >+ // private String observableName = "(Unnamed)"; >+ >+ /** >+ * Create a constraint validator. >+ * >+ * @param realm >+ * JDB realm in which this validator resides. >+ * @param context >+ * Interface to a named observable provider where target >+ * observables can be requested by name. >+ */ >+ public ConstraintValidator(final Realm realm, >+ final INamedObservableProvider context) { >+ this.realm = realm; >+ this.context = context; >+ } >+ >+ /** >+ * Create a constraint validator for an observable which is a member of an >+ * observable group. >+ * >+ * @param realm >+ * JDB realm in which this validator resides. >+ * @param context >+ * Interface to a named observable provider where target >+ * observables can be requested by name. >+ * @param group >+ * Optional group in which the observable to which this constraint validator applies is a member. >+ * @see #setObservableGroup >+ */ >+ public ConstraintValidator(final Realm realm, >+ final INamedObservableProvider context, >+ ConstrainedObservableGroup group) { >+ this.realm = realm; >+ this.context = context; >+ this.groupObservables = group; >+ } >+ >+ /** >+ * Sets whether this is a required field or not. >+ * >+ * @param required >+ */ >+ public void setRequiredField(boolean required) { >+ this.requiredField = required; >+ } >+ >+ /** >+ * Set minimum and maximum values for numeric observables, or a minimum and >+ * maximum length in characters on a String field. >+ * >+ * @param constrained >+ * Whether the range constraint is enabled. >+ * @param min >+ * Minimum value, specified as a long integer. >+ * @param max >+ * Maximum value, specified as a long integer. >+ * @param message >+ * Optional validation status message for out of range numbers. >+ * @see #setMin >+ * @see #setMax >+ */ >+ public void setRange(boolean constrained, long min, long max, String message) { >+ this.rangeEnabled = constrained; >+ this.rangeMin = min; >+ this.rangeMax = max; >+ this.rangeMessage = message; >+ } >+ >+ /** >+ * Set a minimum range for numeric observables or a minimum length in >+ * characters on a String field. Convenience method. >+ * >+ * @param min >+ * Minimum value, specified as a long integer. >+ * @see #setRange >+ */ >+ public void setMin(long min) { >+ this.rangeEnabled = true; >+ this.rangeMin = min; >+ } >+ >+ /** >+ * Set a maximum value for numeric observables or a maximum length in >+ * characters on a String field. Convenience method. >+ * >+ * @param max >+ * Maximum value, specified as a long integer. >+ * @see #setRange >+ */ >+ public void setMax(long max) { >+ this.rangeEnabled = true; >+ this.rangeMax = max; >+ } >+ >+ /** >+ * Sets the observable group in which the constrained observable resides and >+ * across which group constraints apply. >+ * >+ * @param observableGroup >+ */ >+ public void setObservableGroup(ConstrainedObservableGroup observableGroup) { >+ this.groupObservables = observableGroup; >+ } >+ >+ /** >+ * Resolve all observable references now and activate the constraints. >+ */ >+ public void resolveReferences() { >+ >+ // Required If >+ if (requiredIfTargetNames != null && requiredIfTargetNames.length > 0) { >+ requiredIf = new ObservableGroup(realm); >+ if (requiredIfBooleanFalseIsNull) >+ requiredIf.setBooleanFalseIsNull(true); >+ for (int i=0; i < requiredIfTargetNames.length; i++) >+ requiredIf.addObservable(context >+ .getObservableAndDescription(requiredIfTargetNames[i])); >+ } else { >+ requiredIf = null; >+ } >+ >+ // Not allowed if >+ if (notAllowedIfTargetNames != null >+ && notAllowedIfTargetNames.length > 0) { >+ notAllowedIf = new ObservableGroup(realm); >+ if (notAllowedIfBooleanFalseIsNull) >+ notAllowedIf.setBooleanFalseIsNull(true); >+ for (int i=0; i < notAllowedIfTargetNames.length; i++) >+ notAllowedIf.addObservable(context >+ .getObservableAndDescription(notAllowedIfTargetNames[i])); >+ } else { >+ notAllowedIf = null; >+ } >+ } >+ >+ /** >+ * Enables a "Required If" constraint on this validator. >+ * If any of the named target observables (targetNames) have a value set, >+ * then this observable must also have a value. >+ * @param targetNames Names of target observables >+ * @param booleanFalseIsNull When set to true, a boolean value of False is treated as "value not present" just like empty strings and NULL values. >+ */ >+ public void setRequiredIf(String[] targetNames, boolean booleanFalseIsNull) { >+ if (targetNames == null || targetNames.length == 0) >+ throw new IllegalArgumentException( >+ "RequiredIf constraint has no target names"); //$NON-NLS-1$ >+ requiredIfTargetNames = targetNames; >+ requiredIfBooleanFalseIsNull = booleanFalseIsNull; >+ } >+ >+ /** >+ * Enabled a "Not Allowed If" constraint on this validator. >+ * If any of the named target observables (targetNames) have a value set, >+ * then this observable is not allowed to have a value set. >+ * @param targetNames Names of target observables >+ * @param booleanFalseIsNull When set to true, a boolean value of False is treated as "value not present" just like empty strings and NULL values. >+ */ >+ public void setNotAllowedIf(String[] targetNames, boolean booleanFalseIsNull) { >+ if (targetNames == null || targetNames.length == 0) >+ throw new IllegalArgumentException( >+ "NotAllowedIf constraint has no target names"); //$NON-NLS-1$ >+ notAllowedIfTargetNames = targetNames; >+ notAllowedIfBooleanFalseIsNull = booleanFalseIsNull; >+ } >+ >+ /** >+ * Debug tool. >+ * >+ * @param observableName >+ */ >+ /* >+ * public void setObservableName(String observableName) { >+ * this.observableName = observableName; } >+ */ >+ >+ /** >+ * Validate the constraints. >+ */ >+ public IStatus validate(Object value) { >+ IStatus result = doValidate(value); >+ // Useful for debugging: >+ /* >+ * logger.debug("oneOrMore: ["+(oneOrMoreRequiredIf != null ? >+ * oneOrMoreRequiredIf.size():"-")+ "] allIf: ["+(allRequiredIf != null ? >+ * allRequiredIf.size():"-")+ "] obsG: ["+(groupObservables != null ? >+ * groupObservables.size():"-")+ "] validate("+observableName+") result >+ * ["+(result.isOK()?"OK":"ERROR/OTHER")+"]"); >+ */ >+ return result; >+ } >+ >+ /** >+ * Validate all applied constraints. >+ * >+ * TODO: MultiStatus. Currently returns >+ * the first error only. >+ * >+ * @param value Value to validate >+ * @return Validation status object >+ */ >+ protected IStatus doValidate(Object value) { >+ // Required field constraint >+ if (requiredField) { >+ if (value == null) { >+ return ValidationStatus.error(requiredFieldHint); >+ } else { >+ if (value instanceof String) { >+ if ("".equals(value)) //$NON-NLS-1$ >+ return ValidationStatus.error(requiredFieldHint); >+ } >+ } >+ } >+ // RequiredIf constraint >+ if (requiredIf != null) { >+ boolean anyOtherPresent = ((Boolean) requiredIf.anyPresent() >+ .getValue()).booleanValue(); >+ // logger.debug("anyOtherPresent=["+allPresent+"]"); >+ if (anyOtherPresent) { >+ // Required field >+ if (value == null) { >+ return ValidationStatus.error(requiredIfFieldHintPrefix >+ + requiredIf.asText() + requiredIfFieldHintSuffix); >+ } else { >+ if (value instanceof String) >+ if ("".equals(value)) //$NON-NLS-1$ >+ return ValidationStatus >+ .error(requiredIfFieldHintPrefix >+ + requiredIf.asText() >+ + requiredIfFieldHintSuffix); >+ } >+ } >+ } >+ // NotAllowedIf constraint >+ if (notAllowedIf != null) { >+ boolean anyOtherPresent = ((Boolean) notAllowedIf.anyPresent() >+ .getValue()).booleanValue(); >+ // logger.debug("anyOtherPresent=["+allPresent+"]"); >+ if (anyOtherPresent) { >+ // Not Allowed field >+ if (value != null) { >+ if (value instanceof String) { >+ if (!"".equals(value)) //$NON-NLS-1$ >+ return ValidationStatus >+ .error(notAllowedIfFieldHintPrefix >+ + notAllowedIf.asText() >+ + notAllowedIfFieldHintSuffix); >+ } else if (value instanceof Boolean >+ && notAllowedIf.isBooleanFalseNull()) { >+ if (((Boolean) value).booleanValue() == true) >+ return ValidationStatus >+ .error(notAllowedIfFieldHintPrefix >+ + notAllowedIf.asText() >+ + notAllowedIfFieldHintSuffix); >+ } else { >+ return ValidationStatus >+ .error(notAllowedIfFieldHintPrefix >+ + notAllowedIf.asText() >+ + notAllowedIfFieldHintSuffix); >+ } >+ } >+ } >+ } >+ // Range constraint >+ if (rangeEnabled && value != null) { >+ long min = rangeMin; >+ long max = rangeMax; >+ boolean outOfRange = false; >+ if (min != Long.MIN_VALUE) { >+ // Apply minimum constraint >+ if (value instanceof Integer) { >+ if (((Integer) value).intValue() < min) >+ outOfRange = true; >+ } else if (value instanceof Long) { >+ if (((Long) value).longValue() < min) >+ outOfRange = true; >+ } else if (value instanceof Short) { >+ if (((Short) value).longValue() < min) >+ outOfRange = true; >+ } else if (value instanceof Byte) { >+ if (((Byte) value).longValue() < min) >+ outOfRange = true; >+ } else if (value instanceof Double) { >+ if (((Double) value).doubleValue() < min) >+ outOfRange = true; >+ } else if (value instanceof Float) { >+ if (((Float) value).floatValue() < min) >+ outOfRange = true; >+ } else if (value instanceof BigInteger) { >+ if (((BigInteger) value).compareTo(BigInteger.valueOf(min)) < 0) >+ outOfRange = true; >+ } else if (value instanceof BigDecimal) { >+ if (((BigDecimal) value).compareTo(BigDecimal.valueOf(min)) < 0) >+ outOfRange = true; >+ } else if (value instanceof String) { >+ if (((String) value).length() < min) >+ outOfRange = true; >+ } else { >+ throw new IllegalArgumentException( >+ "Unsupported type for @Range constraint: " //$NON-NLS-1$ >+ + value.getClass().getName()); >+ } >+ } >+ if (max != Long.MAX_VALUE && !outOfRange) { >+ // Apply maximum constraint >+ if (value instanceof Integer) { >+ if (((Integer) value).intValue() > max) >+ outOfRange = true; >+ } else if (value instanceof Long) { >+ if (((Long) value).longValue() > max) >+ outOfRange = true; >+ } else if (value instanceof Short) { >+ if (((Short) value).longValue() > max) >+ outOfRange = true; >+ } else if (value instanceof Byte) { >+ if (((Byte) value).longValue() > max) >+ outOfRange = true; >+ } else if (value instanceof Double) { >+ if (((Double) value).doubleValue() > max) >+ outOfRange = true; >+ } else if (value instanceof Float) { >+ if (((Float) value).floatValue() > max) >+ outOfRange = true; >+ } else if (value instanceof BigInteger) { >+ if (((BigInteger) value).compareTo(BigInteger.valueOf(max)) > 0) >+ outOfRange = true; >+ } else if (value instanceof BigDecimal) { >+ if (((BigDecimal) value).compareTo(BigDecimal.valueOf(max)) > 0) >+ outOfRange = true; >+ } else if (value instanceof String) { >+ if (((String) value).length() > max) >+ outOfRange = true; >+ } else { >+ throw new IllegalArgumentException( >+ "Unsupported type for @Range constraint: " //$NON-NLS-1$ >+ + value.getClass().getName()); >+ } >+ } >+ if (outOfRange) { >+ String msg = rangeMessage; >+ // Build default message if no custom message was provided >+ if ("".equals(msg)) { //$NON-NLS-1$ >+ if (value instanceof String) { >+ // String >+ if (min != Long.MIN_VALUE) { >+ if (max != Long.MAX_VALUE) >+ msg = "Value must have between " + min //$NON-NLS-1$ >+ + " and " + max + " characters"; //$NON-NLS-1$ //$NON-NLS-2$ >+ else >+ msg = "Minimum " + min + " characters"; //$NON-NLS-1$ //$NON-NLS-2$ >+ } else { >+ msg = "Maximum " + max + " characters"; //$NON-NLS-1$ //$NON-NLS-2$ >+ } >+ } else { >+ // Number >+ if (min != Long.MIN_VALUE) { >+ if (max != Long.MAX_VALUE) >+ msg = "Value must be between " + min + " and " //$NON-NLS-1$ //$NON-NLS-2$ >+ + max; >+ else >+ msg = "Minimum value is " + min; //$NON-NLS-1$ >+ } else { >+ msg = "Maximum value is " + max; //$NON-NLS-1$ >+ } >+ } >+ } >+ return ValidationStatus.error(msg); >+ } >+ } >+ // Group constraints >+ if (groupObservables != null) { >+ return groupObservables.validate(value); >+ } >+ return Status.OK_STATUS; >+ } >+ >+ /** >+ * Apply any relation constraint dependencies, which requires the binding >+ * object. >+ * >+ * @param binding >+ */ >+ private void applyRelationalConstraints(Binding binding, >+ boolean excludeGroupConstraints) { >+ // Create one validation dependency object to enforce dependencies that >+ // are necessary for each of the available constraint types. >+ ValidationDependency.create(binding, joinArrays( >+ getValidationDependencies(excludeGroupConstraints), >+ groupObservables == null || excludeGroupConstraints ? null >+ : groupObservables.getValidationDependencies()), true, >+ false); >+ } >+ >+ private IObservableValue[] getValidationDependencies( >+ boolean excludeGroupConstraints) { >+ return new IObservableValue[] { >+ (requiredIf != null ? requiredIf.anyPresent() : null), >+ >+ (notAllowedIf != null && !excludeGroupConstraints ? notAllowedIf >+ .anyPresent() >+ : null) }; >+ } >+ >+ /** >+ * Utility method to join two arrays. >+ * >+ * @param original Original array or null. >+ * @param addition Second array or null. >+ * @return One array which is the original array joined with the second array, or null if neither array was provided. >+ */ >+ protected static IObservableValue[] joinArrays(IObservableValue[] original, >+ IObservableValue[] addition) { >+ if (addition == null) >+ return original; >+ if (original == null) >+ return addition; >+ IObservableValue[] out = new IObservableValue[original.length >+ + addition.length]; >+ System.arraycopy(original, 0, out, 0, original.length); >+ System.arraycopy(addition, 0, out, original.length, addition.length); >+ return out; >+ } >+ >+ /** >+ * Sets the binding associated with this constraint validator. >+ * >+ * @param binding >+ * @param excludeFromGroupConstraints >+ */ >+ public void setBinding(Binding binding, boolean excludeFromGroupConstraints) { >+ this.binding = binding; >+ this.excludedFromGroupConstraints = excludeFromGroupConstraints; >+ } >+ >+ void applyRelationalConstraints() { >+ applyRelationalConstraints(binding, excludedFromGroupConstraints); >+ } >+ >+ // For debugging >+ // protected static final Logger logger = >+ // Logger.getLogger(ConstraintValidator.class.getName()); >+ >+} >Index: src/org/eclipse/core/databinding/constraints/ValidationDependency.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/constraints/ValidationDependency.java >diff -N src/org/eclipse/core/databinding/constraints/ValidationDependency.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/constraints/ValidationDependency.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,108 @@ >+/******************************************************************************* >+ * Copyright (c) 2007 Matt Carter. >+ * 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: >+ * Matt Carter - initial API and implementation >+ *******************************************************************************/ >+package org.eclipse.core.databinding.constraints; >+ >+import java.util.logging.Logger; >+ >+import org.eclipse.core.databinding.Binding; >+import org.eclipse.core.databinding.observable.ChangeEvent; >+import org.eclipse.core.databinding.observable.IChangeListener; >+import org.eclipse.core.databinding.observable.value.IObservableValue; >+ >+/** >+ * Creates a dependency between the validation state of the binding, >+ * and the given observable(s). >+ * >+ * Whenever the observable changes, revalidation of target to model and/or >+ * model to target occurs. >+ * >+ * @author Matt Carter >+ */ >+public class ValidationDependency implements IChangeListener { >+ >+ final Binding binding; >+ final boolean targetToModel; >+ final boolean modelToTarget; >+ >+ final IObservableValue[] observableValues; // debugging >+ >+ /** >+ * Create a validation dependency for the binding to the given observable values. >+ * >+ * If all observableValues in the array are null, creates nothing and returns null. >+ * >+ * @param binding Binding whose validation status is dependent on the given observables. >+ * @param observableValues Observable values whose change affects validation status. Values in the array can be null and if so are ignored. >+ * @param targetToModel Whether to validate target to model on change. >+ * @param modelToTarget Whether to validate model to target on change. >+ * @return ValidationDependency object or null if no observable values were passed. >+ */ >+ public static ValidationDependency create(Binding binding, IObservableValue[] observableValues, >+ boolean targetToModel, boolean modelToTarget) { >+ boolean allNull = true; >+ for(int i=0; i < observableValues.length; i++) { >+ if(observableValues[i] != null) { >+ allNull = false; break; >+ } >+ } >+ return allNull? null : new ValidationDependency(binding, observableValues, targetToModel, modelToTarget); >+ } >+ >+ /** >+ * Create a validation dependency for the binding to the given observable value. >+ * >+ * @param binding Binding whose validation status is dependent on the given observable. >+ * @param observableValue Observable value whose change affects validation status. Value can be null, in which case this object is redundant. >+ * @param targetToModel Whether to validate target to model on change. >+ * @param modelToTarget Whether to validate model to target on change. >+ */ >+ public ValidationDependency(Binding binding, IObservableValue observableValue, >+ boolean targetToModel, boolean modelToTarget) { >+ this(binding, new IObservableValue[] {observableValue}, targetToModel, modelToTarget); >+ } >+ >+ /** >+ * Create a validation dependency for the binding to the given observable values. >+ * >+ * @param binding Binding whose validation status is dependent on the given observables. >+ * @param observableValues Observable values whose change affects validation status. Values in the array can be null and if so are ignored. >+ * @param targetToModel Whether to validate target to model on change. >+ * @param modelToTarget Whether to validate model to target on change. >+ */ >+ public ValidationDependency(Binding binding, IObservableValue[] observableValues, >+ boolean targetToModel, boolean modelToTarget) { >+ >+ this.binding = binding; >+ this.targetToModel = targetToModel; >+ this.modelToTarget = modelToTarget; >+ >+ this.observableValues = observableValues; >+ >+ // Add value change listeners to all observers that cause >+ // revalidation of the binding when the state changes. >+ for(int i = 0; i < observableValues.length; i++) { >+ if(observableValues[i] != null) observableValues[i].addChangeListener(this); >+ } >+ } >+ >+ /** >+ * When the observable changes, revalidate the binding which depends upon it. >+ * >+ * @param event >+ */ >+ public void handleChange(ChangeEvent event) { >+ if(targetToModel) binding.validateTargetToModel(); >+ if(modelToTarget) binding.validateModelToTarget(); >+ } >+ >+ protected static final Logger logger = Logger.getLogger(ValidationDependency.class.getName()); >+ >+} >Index: src/org/eclipse/core/databinding/constraints/INamedObservableProvider.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/constraints/INamedObservableProvider.java >diff -N src/org/eclipse/core/databinding/constraints/INamedObservableProvider.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/constraints/INamedObservableProvider.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,61 @@ >+/******************************************************************************* >+ * Copyright (c) 2007 Matt Carter. >+ * 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: >+ * Matt Carter - initial API and implementation >+ *******************************************************************************/ >+package org.eclipse.core.databinding.constraints; >+ >+import org.eclipse.core.databinding.observable.IObservable; >+ >+/** >+ * Provider of observables by name. >+ */ >+public interface INamedObservableProvider { >+ >+ /** >+ * Returns the observable with the given name. >+ * >+ * Throws an exception if the observable is not found. >+ * >+ * @param observableName >+ * @throws IllegalArgumentException >+ * If the named observable is not found. >+ * @return IObservable >+ */ >+ IObservable getObservable(String observableName) >+ throws IllegalArgumentException; >+ >+ /** >+ * Returns a user-friendly description of the named observable. >+ * >+ * Throws an exception if the observable is not found. >+ * >+ * @param observableName >+ * @throws IllegalArgumentException >+ * If the named observable is not found. >+ * @return String >+ */ >+ String getObservableDescription(String observableName) >+ throws IllegalArgumentException; >+ >+ /** >+ * Returns the observable with the given name together with a user-friendly >+ * description. >+ * >+ * Throws an exception if the observable is not found. >+ * >+ * @param observableName >+ * @throws IllegalArgumentException >+ * If the named observable is not found. >+ * @return Object containing observable (type IObservable) and description >+ * (type String) >+ */ >+ ObservableStringPair getObservableAndDescription(String observableName) >+ throws IllegalArgumentException; >+ >+} >Index: src/org/eclipse/core/databinding/constraints/ObservableGroup.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/constraints/ObservableGroup.java >diff -N src/org/eclipse/core/databinding/constraints/ObservableGroup.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/constraints/ObservableGroup.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,350 @@ >+/******************************************************************************* >+ * Copyright (c) 2007 Matt Carter. >+ * 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: >+ * Matt Carter - initial API and implementation >+ ******************************************************************************/ >+package org.eclipse.core.databinding.constraints; >+ >+import java.util.ArrayList; >+import java.util.Collection; >+import java.util.Iterator; >+ >+import org.eclipse.core.databinding.observable.IObservable; >+import org.eclipse.core.databinding.observable.Realm; >+import org.eclipse.core.databinding.observable.list.WritableList; >+import org.eclipse.core.databinding.observable.value.ComputedValue; >+import org.eclipse.core.databinding.observable.value.IObservableValue; >+ >+/** >+ * A group of related observables. The observables may have associated >+ * descriptive names (optional). The group can be queried for the presence >+ * of any, all or no members. >+ */ >+public class ObservableGroup { >+ >+ /** >+ * The list of observables. >+ */ >+ private final WritableList observables; >+ >+ /** >+ * An optional descriptive name for the observable group. >+ */ >+ private final String name; >+ >+ /** >+ * Realm. >+ */ >+ protected final Realm realm; >+ >+ /** >+ * Create an empty observable group. >+ * @param realm Realm. >+ */ >+ public ObservableGroup(Realm realm) { >+ this(realm, null); >+ } >+ >+ /** >+ * Create an empty observable group with an descriptive name. >+ * >+ * @param realm >+ * Data binding realm in which the observable group exists. >+ * @param name >+ * Optional descriptive name for the observable group, or null. >+ */ >+ public ObservableGroup(Realm realm, String name) { >+ observables = new WritableList(realm, >+ new ArrayList(), IObservableValue.class); >+ this.realm = realm; >+ this.name = name; >+ } >+ >+ /** >+ * An observable value reporting true only if all fields in the group are present. >+ */ >+ ComputedValue allPresent = null; >+ >+ /** >+ * An observable value reporting true if all fields in the group are present >+ * or if none are present. >+ */ >+ ComputedValue allOrNonePresent = null; >+ >+ /** >+ * An observable value reporting true if any fields in the group are >+ * present. >+ */ >+ ComputedValue anyPresent = null; >+ >+ /** >+ * An observable value reporting true if no fields in the group are >+ * present. >+ */ >+ ComputedValue nonePresent = null; >+ >+ /** >+ * If set to true, a false boolean value (e.g. empty check box) is >+ * considered to be null for presence detection purposes. >+ */ >+ boolean booleanFalseIsNull = false; >+ >+ /** >+ * Adds an observable to the group with no description. >+ * >+ * @param observable >+ */ >+ public void addObservable(IObservable observable) { >+ observables.add(new ObservableStringPair(observable, null)); >+ } >+ >+ /** >+ * Adds a collection of observables to the group with no descriptions. >+ * >+ * @param observables >+ */ >+ public void addAll(Collection observables) { >+ Iterator iter = observables.iterator(); >+ while(iter.hasNext()) >+ this.observables.add(new ObservableStringPair((IObservable) iter.next(), null)); >+ } >+ >+ /** >+ * Adds an observable to the group with a user-friendly name / description. >+ * >+ * @param observable >+ * @param description >+ */ >+ public void addObservable(IObservable observable, String description) { >+ observables.add(new ObservableStringPair(observable, description)); >+ } >+ >+ /** >+ * Adds an observable to the group with a user-friendly name / description >+ * provided as an ObservableStringPair object. >+ * >+ * @param pair >+ */ >+ public void addObservable(ObservableStringPair pair) { >+ observables.add(pair); >+ } >+ >+ /** >+ * Returns an observable of the group's "all fields present" status, boolean >+ * true or false. >+ * >+ * @return Observable value of the observable group's "all fields present" state. >+ */ >+ public ComputedValue allPresent() { >+ if (allPresent == null) { >+ allPresent = new ComputedValue(realm, boolean.class) { >+ public Object calculate() { >+ return new Boolean(evaluateCondition(false, false, true)); >+ } >+ }; >+ >+ } >+ return allPresent; >+ } >+ >+ /** >+ * Returns an observable of the group's "all or no fields present" status, boolean >+ * true or false. >+ * >+ * @return Observable value of the observable group's "all or no fields present" state. >+ */ >+ public ComputedValue allOrNonePresent() { >+ if (allOrNonePresent == null) { >+ allOrNonePresent = new ComputedValue(realm, boolean.class) { >+ public Object calculate() { >+ return new Boolean(evaluateCondition(true, false, true)); >+ } >+ }; >+ >+ } >+ return allOrNonePresent; >+ } >+ >+ /** >+ * Returns an observable of the group's "Any fields present" status, boolean >+ * true or false. >+ * >+ * @return Observable value of the observable group's "any fields present" state. >+ */ >+ public ComputedValue anyPresent() { >+ if (anyPresent == null) { >+ anyPresent = new ComputedValue(realm, boolean.class) { >+ public Object calculate() { >+ return new Boolean(evaluateCondition(false, true, true)); >+ } >+ }; >+ >+ } >+ return anyPresent; >+ } >+ >+ /** >+ * Returns an observable of the group's "All fields present" status, boolean >+ * true or false. >+ * >+ * @return Observable value of the observable group's "no fields present" state. >+ */ >+ public ComputedValue nonePresent() { >+ if (nonePresent == null) { >+ nonePresent = new ComputedValue(realm, boolean.class) { >+ public Object calculate() { >+ return new Boolean(evaluateCondition(true, false, false)); >+ } >+ }; >+ >+ } >+ return nonePresent; >+ } >+ >+ private boolean evaluateCondition(boolean nonePresent, boolean anyPresent, >+ boolean allPresent) { >+ int numPresent = 0; >+ Iterator iter = observables.iterator(); >+ while(iter.hasNext()) { >+ IObservable o = ((ObservableStringPair) iter.next()).observable; >+ if (o instanceof IObservableValue) { >+ IObservableValue observable = (IObservableValue) o; >+ // Check value is null or empty string, if so, then count as >+ // present >+ Object value = observable.getValue(); >+ boolean present = false; >+ if (value != null) { >+ if (value instanceof String) { >+ // Empty strings considered null >+ if (!"".equals(value)) //$NON-NLS-1$ >+ present = true; >+ } else if (value instanceof Boolean) { >+ // Booleans with false value are considered null, only >+ // if that mode is set >+ if (booleanFalseIsNull >+ && ((Boolean) value).booleanValue() == false) >+ present = false; >+ else >+ present = true; >+ } else { >+ // Any other type with non-null value >+ present = true; >+ } >+ } >+ if (present) >+ numPresent++; >+ // logger.debug("Observable of type ["+((Class) >+ // observable.getValueType()).getSimpleName()+"], >+ // present=["+present+"]"); >+ } else { >+ throw new IllegalArgumentException("Observable type [" //$NON-NLS-1$ >+ + o.getClass().getName() >+ + "] not yet supported in groups."); //$NON-NLS-1$ >+ // TODO: Support ObservableList too. >+ } >+ } >+ boolean result = ((numPresent == 0 && nonePresent) >+ || (numPresent == observables.size() && allPresent) || (numPresent > 0 && anyPresent)); >+ // logger.debug("Checked ["+observables.size()+"] observables to see if >+ // allPresent. Result=["+result+"]. >+ // numPresent=["+numPresent+"/"+observables.size()+"]"); >+ // All present valid either when no controls are complete, or when all >+ // controls are complete. >+ return result; >+ } >+ >+ /** >+ * Returns the descriptions of all the observables in this group in a comma >+ * separated list. >+ * >+ * This method requires descriptions for all the observables. If any >+ * observable has no description an IllegalArgumentException will be thrown. >+ * >+ * @return Comma separated list of the description strings of the >+ * observables in this group. >+ * @throws IllegalArgumentException >+ * If any of the observables have no description set. >+ */ >+ public String asText() throws IllegalArgumentException { >+ StringBuilder sb = new StringBuilder(""); //$NON-NLS-1$ >+ boolean appending = false; >+ Iterator iter = observables.iterator(); >+ while(iter.hasNext()) { >+ ObservableStringPair o = (ObservableStringPair) iter.next(); >+ if (appending) >+ sb.append(","); //$NON-NLS-1$ >+ appending = true; >+ String s = o.string; >+ if (s == null || "".equals(s)) //$NON-NLS-1$ >+ throw new IllegalArgumentException( >+ "Observable missing description which is required"); //$NON-NLS-1$ >+ sb.append(s); >+ } >+ return sb.toString(); >+ } >+ >+ public String toString() { >+ String s; >+ try { >+ s = asText(); >+ } catch (IllegalArgumentException e) { >+ s = Integer.toString(observables.size()) + " unnamed items"; //$NON-NLS-1$ >+ } >+ return "Observable Group {" + s + "}"; //$NON-NLS-1$ //$NON-NLS-2$ >+ } >+ >+ /** >+ * Returns true if boolean false is treated as "no value present" >+ * on the group set side of conditions, just the same as NULL and empty strings. >+ * @return True booleanFalseIsNull flag for this set. >+ */ >+ public boolean isBooleanFalseNull() { >+ return booleanFalseIsNull; >+ } >+ >+ /** >+ * Sets the booleanFalseIsNull flag on this observable group. >+ * Determines whether boolean false is treated as "no value >+ * present" on the group set side of conditions, just the same as >+ * NULL and empty strings. >+ * @param booleanFalseIsNull >+ */ >+ public void setBooleanFalseIsNull(boolean booleanFalseIsNull) { >+ this.booleanFalseIsNull = booleanFalseIsNull; >+ } >+ >+ /** >+ * Returns the name of the observable group, or null if it has no name >+ * assigned. >+ * >+ * @return Name given to this observable group, or null. >+ */ >+ public String getName() { >+ return name; >+ } >+ >+ // Debug >+ void makeDirty() { >+ // if(allOrNonePresent != null) allOrNonePresent.makeDirty(); >+ // if(anyPresent != null) anyPresent.makeDirty(); >+ } >+ >+ /** >+ * Returns the number of observables in this observable group. >+ * @return Size of observable group >+ */ >+ public int size() { >+ if (observables == null) >+ return 0; >+ return observables.size(); >+ } >+ >+ /* protected static final Logger logger = Logger >+ .getLogger(ObservableGroup.class.getName()); */ >+ >+} >Index: src/org/eclipse/core/databinding/constraints/ConstraintBindingContext.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/constraints/ConstraintBindingContext.java >diff -N src/org/eclipse/core/databinding/constraints/ConstraintBindingContext.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/constraints/ConstraintBindingContext.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,210 @@ >+/******************************************************************************* >+ * Copyright (c) 2007 Matt Carter. >+ * 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: >+ * Matt Carter - initial API and implementation >+ ******************************************************************************/ >+package org.eclipse.core.databinding.constraints; >+ >+import java.util.ArrayList; >+import java.util.Iterator; >+import java.util.List; >+ >+import org.eclipse.core.databinding.Binding; >+import org.eclipse.core.databinding.DataBindingContext; >+import org.eclipse.core.databinding.UpdateListStrategy; >+import org.eclipse.core.databinding.UpdateValueStrategy; >+import org.eclipse.core.databinding.observable.Realm; >+import org.eclipse.core.databinding.observable.list.IObservableList; >+import org.eclipse.core.databinding.observable.value.IObservableValue; >+ >+/** >+ * A specialized form of data binding context for binding observables with a set >+ * of potentially mutually dependent constraint validators. >+ * >+ */ >+public class ConstraintBindingContext { >+ >+ /** >+ * List of constraint validators created in this context. >+ */ >+ private List/*<ConstraintValidator>*/ constraintValidators = new ArrayList(); >+ >+ private List/*<ConstrainedObservableGroup>*/ groups = new ArrayList(); >+ >+ /** >+ * Resolve all references and activate referential constraint validation. >+ * Consumers must call this after a constraint binding session. >+ */ >+ public void activateDependentConstraints() { >+ // Resolve references on all group constraints >+ Iterator iter = groups.iterator(); >+ while(iter.hasNext()) { >+ ((ConstrainedObservableGroup)iter.next()).resolveReferences(); >+ } >+ // Resolve references on all constraint validators >+ iter = constraintValidators.iterator(); >+ while(iter.hasNext()) { >+ ((ConstraintValidator) iter.next()).resolveReferences(); >+ } >+ // Apply all relational constraints >+ iter = constraintValidators.iterator(); >+ while(iter.hasNext()) { >+ ((ConstraintValidator) iter.next()).applyRelationalConstraints(); >+ } >+ } >+ >+ /** >+ * Add a constraint validator to this context. >+ * @param validator >+ */ >+ public void add(ConstraintValidator validator) { >+ constraintValidators.add(validator); >+ } >+ >+ >+ /** >+ * Add a constrained observable group to this context. >+ * @see #createObservableGroup >+ * @param group Group to register >+ */ >+ public void add(ConstrainedObservableGroup group) { >+ groups.add(group); >+ } >+ >+ /** >+ * Creates a {@link Binding} to synchronize the values of two >+ * {@link IObservableValue observable values}. During synchronization >+ * validation and conversion can be employed to customize the process. For >+ * specifics on the customization of the process see >+ * {@link UpdateValueStrategy}. This specialized method binds the >+ * observables and retains the associated ConstraintValidator. The >+ * ConstraintValidator must also have been applied to the update strategy >+ * prior to this call, with e.g. targetToModel.setAfterConvertValidator(). >+ * >+ * @param dbc >+ * DataBindingContext to hold the binding >+ * @param targetObservableValue >+ * target value, commonly a UI widget >+ * @param modelObservableValue >+ * model value >+ * @param targetToModel >+ * strategy to employ when the target is the source of the change >+ * and the model is the destination >+ * @param modelToTarget >+ * strategy to employ when the model is the source of the change >+ * and the target is the destination >+ * @param constraintValidator >+ * constraint validator created for this binding >+ * @param excludeFromGroupConstraints >+ * allows individual bindings to be excluded from group >+ * constraints >+ * @return created binding >+ * >+ * @see UpdateValueStrategy >+ */ >+ public Binding bindValue(DataBindingContext dbc, >+ IObservableValue targetObservableValue, >+ IObservableValue modelObservableValue, >+ UpdateValueStrategy targetToModel, >+ UpdateValueStrategy modelToTarget, >+ ConstraintValidator constraintValidator, >+ boolean excludeFromGroupConstraints) { >+ constraintValidators.add(constraintValidator); >+ Binding b = dbc.bindValue(targetObservableValue, modelObservableValue, >+ targetToModel, modelToTarget); >+ constraintValidator.setBinding(b, excludeFromGroupConstraints); >+ return b; >+ } >+ >+ /** >+ * Creates a {@link Binding} to synchronize the values of two >+ * {@link IObservableList observable lists}. During synchronization >+ * validation and conversion can be employed to customize the process. For >+ * specifics on the customization of the process see >+ * {@link UpdateListStrategy}. This specialized method binds the >+ * observables and retains the associated ConstraintValidator. The >+ * ConstraintValidator must also have been applied to the update strategy >+ * prior to this call, with e.g. targetToModel.setAfterConvertValidator(). >+ * >+ * @param dbc >+ * DataBindingContext to hold the binding >+ * @param targetObservableList >+ * target list, commonly a list representing a list in the UI >+ * @param modelObservableList >+ * model list >+ * @param targetToModel >+ * strategy to employ when the target is the source of the change >+ * and the model is the destination >+ * @param modelToTarget >+ * strategy to employ when the model is the source of the change >+ * and the target is the destination >+ * @param constraintValidator >+ * constraint validator created for this binding >+ * @param excludeFromGroupConstraints >+ * allows individual bindings to be excluded from group >+ * constraints * >+ * @return created binding >+ * >+ * @see UpdateListStrategy >+ */ >+ public Binding bindList(DataBindingContext dbc, >+ IObservableList targetObservableList, >+ IObservableList modelObservableList, >+ UpdateListStrategy targetToModel, UpdateListStrategy modelToTarget, >+ ConstraintValidator constraintValidator, >+ boolean excludeFromGroupConstraints) { >+ constraintValidators.add(constraintValidator); >+ Binding b = dbc.bindList(targetObservableList, modelObservableList, >+ targetToModel, modelToTarget); >+ constraintValidator.setBinding(b, excludeFromGroupConstraints); >+ return b; >+ } >+ >+ >+ /** >+ * Create and register a constrained observable group with which to apply groupwide constraints >+ * to a group of observables. >+ * @param realm >+ * @param namedObservableProvider >+ * @return observable group on which group constraints can be set >+ */ >+ public ConstrainedObservableGroup createObservableGroup(Realm realm, INamedObservableProvider namedObservableProvider) { >+ ConstrainedObservableGroup group = new ConstrainedObservableGroup(realm, namedObservableProvider); >+ groups.add(group); >+ return group; >+ } >+ >+ /** >+ * Returns a list of the observable groups known to this constraint binding context. >+ * @return list of groups added to this context >+ */ >+ public ConstrainedObservableGroup[] getObservableGroups() { >+ return (ConstrainedObservableGroup[]) groups.toArray(new ConstrainedObservableGroup[groups.size()]); >+ } >+ >+ /** >+ * Needed to work around a very strange validation status bug. >+ * (Non-API). >+ * @param dbc >+ * @param realm >+ */ >+ public static void revalidateAllBindings(DataBindingContext dbc, Realm realm) { >+ // Revalidate all bindings >+ Iterator iter = dbc.getBindings().iterator(); >+ while(iter.hasNext()) { >+ final Binding binding = (Binding) iter.next(); >+ realm.asyncExec(new Runnable() { >+ public void run() { >+ binding.validateTargetToModel(); >+ } >+ }); >+ >+ } >+ } >+ >+} >Index: src/org/eclipse/core/databinding/constraints/ConstrainedObservableGroup.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/constraints/ConstrainedObservableGroup.java >diff -N src/org/eclipse/core/databinding/constraints/ConstrainedObservableGroup.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/constraints/ConstrainedObservableGroup.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,304 @@ >+/******************************************************************************* >+ * Copyright (c) 2007 Matt Carter. >+ * 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: >+ * Matt Carter - initial API and implementation >+ ******************************************************************************/ >+package org.eclipse.core.databinding.constraints; >+ >+import org.eclipse.core.databinding.observable.Realm; >+import org.eclipse.core.databinding.observable.value.IObservableValue; >+import org.eclipse.core.databinding.validation.ValidationStatus; >+import org.eclipse.core.runtime.IStatus; >+import org.eclipse.core.runtime.Status; >+ >+/** >+ * A group of observables with groupwide constraints. >+ */ >+public class ConstrainedObservableGroup extends ObservableGroup { >+ >+ protected final Realm realm; >+ protected final INamedObservableProvider context; >+ >+ /* Group Constraints */ >+ >+ private boolean allRequiredIfAny = false; >+ >+ private boolean oneOrMoreRequired = false; >+ >+ private String[] allRequiredIfTargetNames; >+ private boolean allRequiredIfIsNegative = false; >+ private ObservableGroup allRequiredIf = null; >+ >+ private String[] oneOrMoreRequiredIfTargetNames; >+ private boolean oneOrMoreRequiredIfIsNegative = false; >+ private ObservableGroup oneOrMoreRequiredIf = null; >+ >+ private String[] noneAllowedIfTargetNames; >+ private boolean noneAllowedIfIsNegative = false; >+ private ObservableGroup noneAllowedIf = null; >+ >+ /** >+ * Create an empty observable group. >+ * @param realm Data binding realm in which the observable group exists. >+ * @param context Interface allowing this class to look up peer observables by name. >+ */ >+ public ConstrainedObservableGroup(Realm realm, >+ INamedObservableProvider context) { >+ super(realm); >+ this.realm = realm; >+ this.context = context; >+ } >+ >+ /** >+ * Create an empty observable group with an descriptive name. >+ * >+ * @param realm >+ * Data binding realm in which the observable group exists. >+ * @param context >+ * Interface allowing this class to look up peer observables by name. >+ * @param name >+ * Optional descriptive name for the observable group, or null. >+ */ >+ public ConstrainedObservableGroup(Realm realm, >+ INamedObservableProvider context, String name) { >+ super(realm, name); >+ this.realm = realm; >+ this.context = context; >+ } >+ >+ /** >+ * Sets an "All Required If Any Present" constraint on this observable group. >+ * All observables require a value if one or more observables in the same group have a value set. >+ * @param allRequiredIfAny >+ */ >+ public void setAllRequiredIfAny(boolean allRequiredIfAny) { >+ this.allRequiredIfAny = allRequiredIfAny; >+ } >+ >+ /** >+ * Sets an "All Required If Any Present" constraint on this observable group. >+ * One or more observables in the group must have a value set. >+ * @param oneOrMoreRequired >+ */ >+ public void setOneOrMoreRequired(boolean oneOrMoreRequired) { >+ this.oneOrMoreRequired = oneOrMoreRequired; >+ } >+ >+ /** >+ * Sets an "All Required If" constraint on this observable group. >+ * All observables require a value if any of the named target >+ * observables (targetNames) have a value set. >+ * >+ * @param targetNames >+ * @param negative If true, changes the condition to: >+ * All observables require a value if any of the named >+ * target observables (targetNames) do not have a value set. >+ */ >+ public void setAllRequiredIf(String[] targetNames, boolean negative) { >+ this.allRequiredIfTargetNames = targetNames; >+ this.allRequiredIfIsNegative = negative; >+ } >+ >+ /** >+ * Sets a "One Or More Required If" constraint on this observable group. >+ * One or more observables in the group must have a value set if >+ * any of the named target observables (targetNames) have a value set. >+ * @param targetNames >+ * @param negative If true, changes the condition to: >+ * One or more observables in the group must have a value set >+ * if any of the named target observables (targetNames) >+ * do not have a value set. >+ */ >+ public void setOneOrMoreRequiredIf(String[] targetNames, boolean negative) { >+ oneOrMoreRequiredIfTargetNames = targetNames; >+ oneOrMoreRequiredIfIsNegative = negative; >+ } >+ >+ /** >+ * Sets a "None Allowed If" constraint on this observable group. >+ * No observables in the group may have a value set if >+ * any of the named target observables (targetNames) have a value set. >+ * @param targetNames >+ * @param negative If true, changes the condition to: >+ * No observables in the group may have a value set if >+ * any of the named target observables (targetNames) >+ * do not have a value set. >+ */ >+ public void setNoneAllowedIf(String[] targetNames, boolean negative) { >+ noneAllowedIfTargetNames = targetNames; >+ noneAllowedIfIsNegative = negative; >+ } >+ >+ /** >+ * Resolve all observable references now. >+ */ >+ protected void resolveReferences() { >+ >+ // All required if >+ if (allRequiredIfTargetNames != null >+ && allRequiredIfTargetNames.length > 0) { >+ allRequiredIf = new ObservableGroup(realm); >+ if (isBooleanFalseNull()) >+ allRequiredIf.setBooleanFalseIsNull(true); >+ for (int i=0; i < allRequiredIfTargetNames.length; i++) { >+ allRequiredIf.addObservable(context >+ .getObservableAndDescription(allRequiredIfTargetNames[i])); >+ } >+ } else { >+ allRequiredIf = null; >+ } >+ >+ // One or more required if >+ if (oneOrMoreRequiredIfTargetNames != null >+ && oneOrMoreRequiredIfTargetNames.length > 0) { >+ oneOrMoreRequiredIf = new ObservableGroup(realm); >+ if (isBooleanFalseNull()) >+ oneOrMoreRequiredIf.setBooleanFalseIsNull(true); >+ for (int i=0; i < oneOrMoreRequiredIfTargetNames.length; i++) { >+ oneOrMoreRequiredIf.addObservable(context >+ .getObservableAndDescription(oneOrMoreRequiredIfTargetNames[i])); >+ } >+ } else { >+ oneOrMoreRequiredIf = null; >+ } >+ >+ // None allowed if >+ if (noneAllowedIfTargetNames != null >+ && noneAllowedIfTargetNames.length > 0) { >+ noneAllowedIf = new ObservableGroup(realm); >+ if (isBooleanFalseNull()) >+ noneAllowedIf.setBooleanFalseIsNull(true); >+ for (int i=0; i < noneAllowedIfTargetNames.length; i++) { >+ noneAllowedIf.addObservable(context >+ .getObservableAndDescription(noneAllowedIfTargetNames[i])); >+ } >+ } else { >+ noneAllowedIf = null; >+ } >+ } >+ >+ /** >+ * Internal method. Validates the given value against >+ * any effective group constraints. >+ * @see ConstraintValidator#validate >+ * @param value Value to validate >+ * @return Validation status >+ */ >+ IStatus validate(Object value) { >+ // Group constraint "None allowed if" >+ if (noneAllowedIf != null) { >+ boolean anyReferredPresent = ((Boolean) noneAllowedIf.anyPresent() >+ .getValue()).booleanValue(); >+ if (anyReferredPresent != noneAllowedIfIsNegative) { >+ boolean nonePresent = ((Boolean) nonePresent().getValue()).booleanValue(); >+ if (!nonePresent) { >+ if (noneAllowedIfIsNegative) { >+ // All group fields are not allowed if specified fields >+ // are not set >+ return ValidationStatus >+ .error("No fields in this group can be set until the field(s) " //$NON-NLS-1$ >+ + noneAllowedIf.asText() + " are set ."); //$NON-NLS-1$ >+ } else { >+ // All group fields are not allowed if any one specified >+ // field is set >+ return ValidationStatus >+ .error("No fields in this group can be set with the field(s) " //$NON-NLS-1$ >+ + noneAllowedIf.asText() + " set."); //$NON-NLS-1$ >+ } >+ } >+ } >+ } >+ // Group constraint "All required if any" >+ if (allRequiredIfAny) { >+ // All fields are required if any one field is set. >+ // logger.debug("Validating group Constraint >+ // allRequiredIfAny=["+allRequiredIfAny+"]"); >+ boolean allPresent = ((Boolean) allOrNonePresent().getValue()).booleanValue(); >+ // logger.debug("allPresent=["+allPresent+"]"); >+ if (!allPresent) >+ return ValidationStatus >+ .error("All fields in this group are required, or none."); //$NON-NLS-1$ >+ } >+ // Group constraint "One or more required" >+ if (oneOrMoreRequired) { >+ // All fields are required if any one field is set. >+ boolean anyPresent = ((Boolean) anyPresent().getValue()).booleanValue(); >+ if (!anyPresent) >+ return ValidationStatus >+ .error("One or more fields in this group are required."); //$NON-NLS-1$ >+ } >+ // Group constraint "All required if" >+ if (allRequiredIf != null) { >+ boolean anyReferredPresent = ((Boolean) allRequiredIf.anyPresent() >+ .getValue()).booleanValue(); >+ if (anyReferredPresent != allRequiredIfIsNegative) { >+ boolean allPresent = ((Boolean) allPresent().getValue()).booleanValue(); >+ if (!allPresent) { >+ if (allRequiredIfIsNegative) { >+ // All group fields are required if specified fields are >+ // not set >+ return ValidationStatus >+ .error("All fields in this group must be set when the field(s) " //$NON-NLS-1$ >+ + allRequiredIf.asText() >+ + " are not set."); //$NON-NLS-1$ >+ } else { >+ // All group fields are required if any one specified >+ // field is set >+ return ValidationStatus >+ .error("All fields in this group must be set with the field(s) " //$NON-NLS-1$ >+ + allRequiredIf.asText() + " set."); //$NON-NLS-1$ >+ } >+ } >+ } >+ } >+ // Group constraint "One or more required if" >+ if (oneOrMoreRequiredIf != null) { >+ // One or more group fields are required if any one field is set >+ // logger.debug("Validating oneOrMoreRequiredIf"); >+ boolean anyReferredPresent = ((Boolean) oneOrMoreRequiredIf >+ .anyPresent().getValue()).booleanValue(); >+ if (anyReferredPresent != oneOrMoreRequiredIfIsNegative) { >+ boolean anyPresent = ((Boolean) anyPresent().getValue()).booleanValue(); >+ if (!anyPresent) { >+ if (oneOrMoreRequiredIfIsNegative) { >+ return ValidationStatus >+ .error("One or more fields in this group must be set when the field(s) " //$NON-NLS-1$ >+ + oneOrMoreRequiredIf.asText() >+ + " are not set."); //$NON-NLS-1$ >+ } else { >+ return ValidationStatus >+ .error("One or more fields in this group must be set when the field(s) " //$NON-NLS-1$ >+ + oneOrMoreRequiredIf.asText() >+ + " are set."); //$NON-NLS-1$ >+ } >+ } >+ } >+ } >+ >+ return Status.OK_STATUS; >+ } >+ >+ /** >+ * Return the observable states on which the constraint set >+ * is dependent. >+ * @return Array of observable values on whose change >+ * this group has a dependency. >+ */ >+ public IObservableValue[] getValidationDependencies() { >+ return new IObservableValue[] { >+ (allRequiredIfAny ? allOrNonePresent() : null), >+ (oneOrMoreRequired ? anyPresent() : null), >+ (allRequiredIf != null ? allRequiredIf.anyPresent() : null), >+ (allRequiredIf != null ? allPresent() : null), >+ (oneOrMoreRequiredIf != null ? oneOrMoreRequiredIf.anyPresent() >+ : null), >+ (oneOrMoreRequiredIf != null ? anyPresent() : null), >+ (noneAllowedIf != null ? noneAllowedIf.anyPresent() : null), >+ (noneAllowedIf != null ? nonePresent() : null) }; >+ } >+} >Index: src/org/eclipse/core/databinding/constraints/ObservableStringPair.java >=================================================================== >RCS file: src/org/eclipse/core/databinding/constraints/ObservableStringPair.java >diff -N src/org/eclipse/core/databinding/constraints/ObservableStringPair.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/core/databinding/constraints/ObservableStringPair.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,36 @@ >+/******************************************************************************* >+ * Copyright (c) 2007 Matt Carter. >+ * 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: >+ * Matt Carter - initial API and implementation >+ ******************************************************************************/ >+package org.eclipse.core.databinding.constraints; >+ >+import org.eclipse.core.databinding.observable.IObservable; >+ >+/** >+ * Pair of IObservable and String. >+ */ >+public class ObservableStringPair { >+ /** >+ * Observable. >+ */ >+ final public IObservable observable; >+ /** >+ * Associated string. >+ */ >+ final public String string; >+ /** >+ * Construct an observable and string pair. >+ * @param observable >+ * @param string >+ */ >+ public ObservableStringPair(IObservable observable, String string) { >+ this.observable = observable; >+ this.string = string; >+ } >+}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 197807
: 82783