KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > databinding > UpdateValueStrategy


1 /*******************************************************************************
2  * Copyright (c) 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  ******************************************************************************/

11
12 package org.eclipse.core.databinding;
13
14 import java.util.Date JavaDoc;
15 import java.util.HashMap JavaDoc;
16
17 import org.eclipse.core.databinding.conversion.IConverter;
18 import org.eclipse.core.databinding.observable.value.IObservableValue;
19 import org.eclipse.core.databinding.validation.IValidator;
20 import org.eclipse.core.databinding.validation.ValidationStatus;
21 import org.eclipse.core.internal.databinding.BindingMessages;
22 import org.eclipse.core.internal.databinding.Pair;
23 import org.eclipse.core.internal.databinding.conversion.NumberToBigDecimalConverter;
24 import org.eclipse.core.internal.databinding.conversion.NumberToBigIntegerConverter;
25 import org.eclipse.core.internal.databinding.conversion.NumberToByteConverter;
26 import org.eclipse.core.internal.databinding.conversion.NumberToDoubleConverter;
27 import org.eclipse.core.internal.databinding.conversion.NumberToFloatConverter;
28 import org.eclipse.core.internal.databinding.conversion.NumberToIntegerConverter;
29 import org.eclipse.core.internal.databinding.conversion.NumberToLongConverter;
30 import org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter;
31 import org.eclipse.core.internal.databinding.conversion.NumberToShortConverter;
32 import org.eclipse.core.internal.databinding.conversion.StringToDateConverter;
33 import org.eclipse.core.internal.databinding.validation.NumberFormatConverter;
34 import org.eclipse.core.internal.databinding.validation.NumberToByteValidator;
35 import org.eclipse.core.internal.databinding.validation.NumberToDoubleValidator;
36 import org.eclipse.core.internal.databinding.validation.NumberToFloatValidator;
37 import org.eclipse.core.internal.databinding.validation.NumberToIntegerValidator;
38 import org.eclipse.core.internal.databinding.validation.NumberToLongValidator;
39 import org.eclipse.core.internal.databinding.validation.NumberToShortValidator;
40 import org.eclipse.core.internal.databinding.validation.NumberToUnboundedNumberValidator;
41 import org.eclipse.core.internal.databinding.validation.ObjectToPrimitiveValidator;
42 import org.eclipse.core.internal.databinding.validation.StringToByteValidator;
43 import org.eclipse.core.internal.databinding.validation.StringToDateValidator;
44 import org.eclipse.core.internal.databinding.validation.StringToDoubleValidator;
45 import org.eclipse.core.internal.databinding.validation.StringToFloatValidator;
46 import org.eclipse.core.internal.databinding.validation.StringToIntegerValidator;
47 import org.eclipse.core.internal.databinding.validation.StringToLongValidator;
48 import org.eclipse.core.internal.databinding.validation.StringToShortValidator;
49 import org.eclipse.core.runtime.IStatus;
50 import org.eclipse.core.runtime.Status;
51
52 /**
53  * Customizes a {@link Binding} between two
54  * {@link IObservableValue observable values}. The following behaviors can be
55  * customized via the strategy:
56  * <ul>
57  * <li>Validation</li>
58  * <li>Conversion</li>
59  * <li>Automatic processing</li>
60  * </ul>
61  * <p>
62  * The update phases are:
63  * <ol>
64  * <li>Validate after get - {@link #validateAfterGet(Object)}</li>
65  * <li>Conversion - {@link #convert(Object)}</li>
66  * <li>Validate after conversion - {@link #validateAfterConvert(Object)}</li>
67  * <li>Validate before set - {@link #validateBeforeSet(Object)}</li>
68  * <li>Value set - {@link #doSet(IObservableValue, Object)}</li>
69  * </ol>
70  * </p>
71  * <p>
72  * Validation:<br/> {@link IValidator Validators} validate the value at
73  * multiple phases in the update process. Statuses returned from validators are
74  * aggregated into a <code>MultiStatus</code> until a status of
75  * <code>ERROR</code> or <code>CANCEL</code> is encountered. Either of these
76  * statuses will abort the update process. These statuses are available as the
77  * {@link Binding#getValidationStatus() binding validation status}.
78  * </p>
79  * <p>
80  * Conversion:<br/> A {@link IConverter converter} will convert the value from
81  * the type of the source observable into the type of the destination. The
82  * strategy has the ability to default converters for common scenarios.
83  * </p>
84  * <p>
85  * Automatic processing:<br/> The processing to perform when the source
86  * observable changes. This behavior is configured via policies provided on
87  * construction of the strategy (e.g. {@link #POLICY_NEVER},
88  * {@link #POLICY_CONVERT}, {@link #POLICY_ON_REQUEST}, {@link #POLICY_UPDATE}).
89  * </p>
90  *
91  * @see DataBindingContext#bindValue(IObservableValue, IObservableValue,
92  * UpdateValueStrategy, UpdateValueStrategy)
93  * @see Binding#getValidationStatus()
94  * @see IValidator
95  * @see IConverter
96  * @since 1.0
97  */

98 public class UpdateValueStrategy extends UpdateStrategy {
99
100     /**
101      * Policy constant denoting that the source observable's state should not be
102      * tracked and that the destination observable's value should never be
103      * updated.
104      */

105     public static int POLICY_NEVER = notInlined(1);
106
107     /**
108      * Policy constant denoting that the source observable's state should not be
109      * tracked, but that validation, conversion and updating the destination
110      * observable's value should be performed when explicitly requested.
111      */

112     public static int POLICY_ON_REQUEST = notInlined(2);
113
114     /**
115      * Policy constant denoting that the source observable's state should be
116      * tracked, including validating changes except for
117      * {@link #validateBeforeSet(Object)}, but that the destination
118      * observable's value should only be updated on request.
119      */

120     public static int POLICY_CONVERT = notInlined(4);
121
122     /**
123      * Policy constant denoting that the source observable's state should be
124      * tracked, and that validation, conversion and updating the destination
125      * observable's value should be performed automaticlly on every change of
126      * the source observable value.
127      */

128     public static int POLICY_UPDATE = notInlined(8);
129
130     /**
131      * Helper method allowing API evolution of the above constant values. The
132      * compiler will not inline constant values into client code if values are
133      * "computed" using this helper.
134      *
135      * @param i
136      * an integer
137      * @return the same integer
138      */

139     private static int notInlined(int i) {
140         return i;
141     }
142
143     protected IValidator afterGetValidator;
144     protected IValidator afterConvertValidator;
145     protected IValidator beforeSetValidator;
146     protected IConverter converter;
147
148     private int updatePolicy;
149
150     private static ValidatorRegistry validatorRegistry = new ValidatorRegistry();
151     private static HashMap JavaDoc validatorsByConverter = new HashMap JavaDoc();
152
153     protected boolean provideDefaults;
154
155     /**
156      * <code>true</code> if we defaulted the converter
157      */

158     private boolean defaultedConverter = false;
159
160     /**
161      * Creates a new update value strategy for automatically updating the
162      * destination observable value whenever the source observable value
163      * changes. Default validators and a default converter will be provided. The
164      * defaults can be changed by calling one of the setter methods.
165      */

166     public UpdateValueStrategy() {
167         this(true, POLICY_UPDATE);
168     }
169
170     /**
171      * Creates a new update value strategy with a configurable update policy.
172      * Default validators and a default converter will be provided. The defaults
173      * can be changed by calling one of the setter methods.
174      *
175      * @param updatePolicy
176      * one of {@link #POLICY_NEVER}, {@link #POLICY_ON_REQUEST},
177      * {@link #POLICY_CONVERT}, or {@link #POLICY_UPDATE}
178      */

179     public UpdateValueStrategy(int updatePolicy) {
180         this(true, updatePolicy);
181     }
182
183     /**
184      * Creates a new update value strategy with a configurable update policy.
185      * Default validators and a default converter will be provided if
186      * <code>provideDefaults</code> is <code>true</code>. The defaults can
187      * be changed by calling one of the setter methods.
188      *
189      * @param provideDefaults
190      * if <code>true</code>, default validators and a default
191      * converter will be provided based on the observable value's
192      * type.
193      * @param updatePolicy
194      * one of {@link #POLICY_NEVER}, {@link #POLICY_ON_REQUEST},
195      * {@link #POLICY_CONVERT}, or {@link #POLICY_UPDATE}
196      */

197     public UpdateValueStrategy(boolean provideDefaults, int updatePolicy) {
198         this.provideDefaults = provideDefaults;
199         this.updatePolicy = updatePolicy;
200     }
201
202     /**
203      * Converts the value from the source type to the destination type.
204      * <p>
205      * Default implementation will use the
206      * {@link #setConverter(IConverter) converter} if one exists. If no
207      * converter exists no conversion occurs.
208      * </p>
209      *
210      * @param value
211      * @return the converted value
212      */

213     public Object JavaDoc convert(Object JavaDoc value) {
214         return converter == null ? value : converter.convert(value);
215     }
216
217     /**
218      * Tries to create a validator that can validate values of type fromType.
219      * Returns <code>null</code> if no validator could be created. Either
220      * toType or modelDescription can be <code>null</code>, but not both.
221      *
222      * @param fromType
223      * @param toType
224      * @return an IValidator, or <code>null</code> if unsuccessful
225      */

226     protected IValidator createValidator(Object JavaDoc fromType, Object JavaDoc toType) {
227         if (fromType == null || toType == null) {
228             return new IValidator() {
229
230                 public IStatus validate(Object JavaDoc value) {
231                     return Status.OK_STATUS;
232                 }
233             };
234         }
235
236         return findValidator(fromType, toType);
237     }
238
239     /**
240      * Fills out default values based upon the provided <code>source</code>
241      * and <code>destination</code>. If the strategy is to default values it
242      * will attempt to default a converter. If the converter can be defaulted an
243      * attempt is made to default the
244      * {@link #validateAfterGet(Object) after get validator}. If a validator
245      * cannot be defaulted it will be <code>null</code>.
246      *
247      * @param source
248      * @param destination
249      */

250     protected void fillDefaults(IObservableValue source,
251             IObservableValue destination) {
252         Object JavaDoc sourceType = source.getValueType();
253         Object JavaDoc destinationType = destination.getValueType();
254         if (provideDefaults && sourceType != null && destinationType != null) {
255             if (converter == null) {
256                 IConverter converter = createConverter(sourceType,
257                         destinationType);
258                 defaultedConverter = (converter != null);
259                 setConverter(converter);
260             }
261
262             if (afterGetValidator == null) {
263                 afterGetValidator = createValidator(sourceType, destinationType);
264             }
265         }
266         if (converter != null) {
267             if (sourceType != null) {
268                 checkAssignable(converter.getFromType(), sourceType,
269                         "converter does not convert from type " + sourceType); //$NON-NLS-1$
270
}
271             if (destinationType != null) {
272                 checkAssignable(converter.getToType(), destinationType,
273                         "converter does not convert to type " + destinationType); //$NON-NLS-1$
274
}
275         }
276     }
277
278     private IValidator findValidator(Object JavaDoc fromType, Object JavaDoc toType) {
279         IValidator result = null;
280
281         // We only default the validator if we defaulted the converter since the
282
// two are tightly coupled.
283
if (defaultedConverter) {
284             if (String JavaDoc.class.equals(fromType)) {
285                 result = (IValidator) validatorsByConverter.get(converter);
286
287                 if (result == null) {
288                     // TODO sring based lookup
289
if (Integer JavaDoc.class.equals(toType)
290                             || Integer.TYPE.equals(toType)) {
291                         result = new StringToIntegerValidator((NumberFormatConverter) converter);
292                     } else if (Long JavaDoc.class.equals(toType)
293                             || Long.TYPE.equals(toType)) {
294                         result = new StringToLongValidator((NumberFormatConverter) converter);
295                     } else if (Float JavaDoc.class.equals(toType)
296                             || Float.TYPE.equals(toType)) {
297                         result = new StringToFloatValidator((NumberFormatConverter) converter);
298                     } else if (Double JavaDoc.class.equals(toType)
299                             || Double.TYPE.equals(toType)) {
300                         result = new StringToDoubleValidator((NumberFormatConverter) converter);
301                     } else if (Byte JavaDoc.class.equals(toType)
302                             || Byte.TYPE.equals(toType)) {
303                         result = new StringToByteValidator((NumberFormatConverter) converter);
304                     } else if (Short JavaDoc.class.equals(toType)
305                             || Short.TYPE.equals(toType)) {
306                         result = new StringToShortValidator((NumberFormatConverter) converter);
307                     } else if (Date JavaDoc.class.equals(toType)
308                             && converter instanceof StringToDateConverter) {
309                         result = new StringToDateValidator(
310                                 (StringToDateConverter) converter);
311                     }
312
313                     if (result != null) {
314                         validatorsByConverter.put(converter, result);
315                     }
316                 }
317             } else if (converter instanceof NumberToNumberConverter) {
318                 result = (IValidator) validatorsByConverter.get(converter);
319                 
320                 if (result == null) {
321                     if (converter instanceof NumberToByteConverter) {
322                         result = new NumberToByteValidator((NumberToByteConverter) converter);
323                     } else if (converter instanceof NumberToShortConverter) {
324                         result = new NumberToShortValidator((NumberToShortConverter) converter);
325                     } else if (converter instanceof NumberToIntegerConverter) {
326                         result = new NumberToIntegerValidator((NumberToIntegerConverter) converter);
327                     } else if (converter instanceof NumberToLongConverter) {
328                         result = new NumberToLongValidator((NumberToLongConverter) converter);
329                     } else if (converter instanceof NumberToFloatConverter) {
330                         result = new NumberToFloatValidator((NumberToFloatConverter) converter);
331                     } else if (converter instanceof NumberToDoubleConverter) {
332                         result = new NumberToDoubleValidator((NumberToDoubleConverter) converter);
333                     } else if (converter instanceof NumberToBigIntegerConverter || converter instanceof NumberToBigDecimalConverter) {
334                         result = new NumberToUnboundedNumberValidator((NumberToNumberConverter) converter);
335                     }
336                 }
337             }
338
339             if (result == null) {
340                 // TODO string based lookup
341
result = validatorRegistry.get(fromType, toType);
342             }
343         }
344
345         return result;
346     }
347
348     /**
349      * @return the update policy
350      */

351     public int getUpdatePolicy() {
352         return updatePolicy;
353     }
354
355     /**
356      * Sets the validator to be invoked after the source value is converted to
357      * the type of the destination observable.
358      *
359      * @param validator
360      * @return the receiver, to enable method call chaining
361      */

362     public UpdateValueStrategy setAfterConvertValidator(IValidator validator) {
363         this.afterConvertValidator = validator;
364         return this;
365     }
366
367     /**
368      * Sets the validator to be invoked after the source value is retrieved at
369      * the beginning of the synchronization process.
370      *
371      * @param validator
372      * @return the receiver, to enable method call chaining
373      */

374     public UpdateValueStrategy setAfterGetValidator(IValidator validator) {
375         this.afterGetValidator = validator;
376         return this;
377     }
378
379     /**
380      * Sets the validator to be invoked before the value is to be set on the
381      * destination at the end of the synchronization process.
382      *
383      * @param validator
384      * @return the receiver, to enable method call chaining
385      */

386     public UpdateValueStrategy setBeforeSetValidator(IValidator validator) {
387         this.beforeSetValidator = validator;
388         return this;
389     }
390
391     /**
392      * Sets the converter to be invoked when converting from the source type to
393      * the destination type.
394      *
395      * @param converter
396      * @return the receiver, to enable method call chaining
397      */

398     public UpdateValueStrategy setConverter(IConverter converter) {
399         this.converter = converter;
400         return this;
401     }
402
403     /**
404      * Validates the value after it is converted.
405      * <p>
406      * Default implementation will use the
407      * {@link #setAfterConvertValidator(IValidator) validator} if one exists. If
408      * one does not exist no validation will occur.
409      * </p>
410      *
411      * @param value
412      * @return an ok status
413      */

414     public IStatus validateAfterConvert(Object JavaDoc value) {
415         return afterConvertValidator == null ? Status.OK_STATUS
416                 : afterConvertValidator.validate(value);
417     }
418
419     /**
420      * Validates the value after it is retrieved from the source.
421      * <p>
422      * Default implementation will use the
423      * {@link #setAfterGetValidator(IValidator) validator} if one exists. If one
424      * does not exist no validation will occur.
425      * </p>
426      *
427      * @param value
428      * @return an ok status
429      */

430     public IStatus validateAfterGet(Object JavaDoc value) {
431         return afterGetValidator == null ? Status.OK_STATUS : afterGetValidator
432                 .validate(value);
433     }
434
435     /**
436      * Validates the value before it is set on the destination.
437      * <p>
438      * Default implementation will use the
439      * {@link #setBeforeSetValidator(IValidator) validator} if one exists. If
440      * one does not exist no validation will occur.
441      * </p>
442      *
443      * @param value
444      * @return an ok status
445      */

446     public IStatus validateBeforeSet(Object JavaDoc value) {
447         return beforeSetValidator == null ? Status.OK_STATUS
448                 : beforeSetValidator.validate(value);
449     }
450     
451     /**
452      * Sets the current value of the given observable to the given value.
453      * Clients may extend but must call the super implementation.
454      *
455      * @param observableValue
456      * @param value
457      * @return status
458      */

459     protected IStatus doSet(IObservableValue observableValue, Object JavaDoc value) {
460         try {
461             observableValue.setValue(value);
462         } catch (Exception JavaDoc ex) {
463             return ValidationStatus.error(BindingMessages
464                     .getString("ValueBinding_ErrorWhileSettingValue"), //$NON-NLS-1$
465
ex);
466         }
467         return Status.OK_STATUS;
468     }
469
470     private static class ValidatorRegistry {
471
472         private HashMap JavaDoc validators = new HashMap JavaDoc();
473
474         /**
475          * Adds the system-provided validators to the current validator
476          * registry. This is done automatically for the validator registry
477          * singleton.
478          */

479         private ValidatorRegistry() {
480             // Standalone validators here...
481
associate(Integer JavaDoc.class, Integer.TYPE,
482                     new ObjectToPrimitiveValidator(Integer.TYPE));
483             associate(Byte JavaDoc.class, Byte.TYPE, new ObjectToPrimitiveValidator(
484                     Byte.TYPE));
485             associate(Short JavaDoc.class, Short.TYPE, new ObjectToPrimitiveValidator(
486                     Short.TYPE));
487             associate(Long JavaDoc.class, Long.TYPE, new ObjectToPrimitiveValidator(
488                     Long.TYPE));
489             associate(Float JavaDoc.class, Float.TYPE, new ObjectToPrimitiveValidator(
490                     Float.TYPE));
491             associate(Double JavaDoc.class, Double.TYPE,
492                     new ObjectToPrimitiveValidator(Double.TYPE));
493             associate(Boolean JavaDoc.class, Boolean.TYPE,
494                     new ObjectToPrimitiveValidator(Boolean.TYPE));
495
496             associate(Object JavaDoc.class, Integer.TYPE,
497                     new ObjectToPrimitiveValidator(Integer.TYPE));
498             associate(Object JavaDoc.class, Byte.TYPE, new ObjectToPrimitiveValidator(
499                     Byte.TYPE));
500             associate(Object JavaDoc.class, Short.TYPE, new ObjectToPrimitiveValidator(
501                     Short.TYPE));
502             associate(Object JavaDoc.class, Long.TYPE, new ObjectToPrimitiveValidator(
503                     Long.TYPE));
504             associate(Object JavaDoc.class, Float.TYPE, new ObjectToPrimitiveValidator(
505                     Float.TYPE));
506             associate(Object JavaDoc.class, Double.TYPE,
507                     new ObjectToPrimitiveValidator(Double.TYPE));
508             associate(Object JavaDoc.class, Boolean.TYPE,
509                     new ObjectToPrimitiveValidator(Boolean.TYPE));
510         }
511
512         /**
513          * Associate a particular validator that can validate the conversion
514          * (fromClass, toClass)
515          *
516          * @param fromClass
517          * The Class to convert from
518          * @param toClass
519          * The Class to convert to
520          * @param validator
521          * The IValidator
522          */

523         private void associate(Object JavaDoc fromClass, Object JavaDoc toClass,
524                 IValidator validator) {
525             validators.put(new Pair(fromClass, toClass), validator);
526         }
527
528         /**
529          * Return an IValidator for a specific fromClass and toClass.
530          *
531          * @param fromClass
532          * The Class to convert from
533          * @param toClass
534          * The Class to convert to
535          * @return An appropriate IValidator
536          */

537         private IValidator get(Object JavaDoc fromClass, Object JavaDoc toClass) {
538             IValidator result = (IValidator) validators.get(new Pair(fromClass,
539                     toClass));
540             if (result != null)
541                 return result;
542             if (fromClass != null && toClass != null && fromClass == toClass) {
543                 return new IValidator() {
544                     public IStatus validate(Object JavaDoc value) {
545                         return Status.OK_STATUS;
546                     }
547                 };
548             }
549             return new IValidator() {
550                 public IStatus validate(Object JavaDoc value) {
551                     return Status.OK_STATUS;
552                 }
553             };
554         }
555     }
556
557 }
558
Popular Tags