Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[FELIX-3757] Processed comments of fmeschbe:

- applied correct code formatting;
- when min/max values were defined for types other than integer, the validation
  failed (possibly with a class cast exception);
- some other small issues fixed regarding the validation of the different types;
- updated the changelog to include the set of changes.



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1410279 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information...
commit ec46d903b768bf40da69df4141d3f905ebf6b545 1 parent da9fbe8
authored November 16, 2012
8  metatype/changelog.txt
... ...
@@ -1,3 +1,11 @@
  1
+Changes from 1.0.6 to 1.0.8
  2
+---------------------------
  3
+
  4
+** Bug
  5
+    * [FELIX-3756] - Optional attributes validated invalid data as correct;
  6
+    * [FELIX-3757] - If an AttributeDefinition did not specify a minimum, maximum or option values, it did not detect missing values;
  7
+    * [FELIX-3758] - AttributeDefinition.validate() did not take non-zero cardinality into consideration.
  8
+
1 9
 Changes from 1.0.4 to 1.0.6
2 10
 ---------------------------
3 11
 
262  metatype/src/main/java/org/apache/felix/metatype/AD.java
@@ -18,7 +18,6 @@
18 18
  */
19 19
 package org.apache.felix.metatype;
20 20
 
21  
-
22 21
 import java.util.ArrayList;
23 22
 import java.util.Iterator;
24 23
 import java.util.List;
@@ -28,7 +27,6 @@
28 27
 import org.osgi.service.log.LogService;
29 28
 import org.osgi.service.metatype.AttributeDefinition;
30 29
 
31  
-
32 30
 /**
33 31
  * The <code>AD</code> class represents the <code>AD</code> element of the
34 32
  * meta type descriptor.
@@ -84,255 +82,221 @@
84 82
     private String max;
85 83
     private boolean isRequired = true;
86 84
 
87  
-
88 85
     public String getID()
89 86
     {
90 87
         return id;
91 88
     }
92 89
 
93  
-
94 90
     public String getName()
95 91
     {
96 92
         return name;
97 93
     }
98 94
 
99  
-
100 95
     public String getDescription()
101 96
     {
102 97
         return description;
103 98
     }
104 99
 
105  
-
106 100
     public int getType()
107 101
     {
108 102
         return type;
109 103
     }
110 104
 
111  
-
112 105
     public int getCardinality()
113 106
     {
114 107
         return cardinality;
115 108
     }
116 109
 
117  
-
118 110
     public String[] getOptionLabels()
119 111
     {
120 112
         return optionLabels;
121 113
     }
122 114
 
123  
-
124 115
     public String[] getOptionValues()
125 116
     {
126 117
         return optionValues;
127 118
     }
128 119
 
129  
-
130 120
     public String[] getDefaultValue()
131 121
     {
132 122
         return defaultValue;
133 123
     }
134 124
 
135  
-
136 125
     public String getMin()
137 126
     {
138 127
         return min;
139 128
     }
140 129
 
141  
-
142 130
     public String getMax()
143 131
     {
144 132
         return max;
145 133
     }
146 134
 
147  
-
148 135
     public boolean isRequired()
149 136
     {
150 137
         return isRequired;
151 138
     }
152 139
 
153  
-
154 140
     /**
155 141
      * Implements validation of the <code>valueString</code> and returns an
156  
-     * indication of the success:
157  
-     * <dl>
158  
-     * <dt><code>null</code>
159  
-     * <dd>If neither a {@link #getMin() minimal value} nor a
160  
-     *      {@link #getMax() maximal value} nor any
161  
-     *      {@link #getOptionValues() optional values} are defined in this
162  
-     *      instance, validation cannot be performed.
163  
-     * <dt>Empty String
164  
-     * <dd>If validation succeeds. This value is also returned if the
165  
-     *      <code>valueString</code> is empty or <code>null</code> or cannot be
166  
-     *      converted into a numeric type.
167  
-     * <dt><b>%</b>message
168  
-     * <dd>If the value falls below the minimum, higher than the maximum or is
169  
-     *      not any of the option values, an explanatory message, which may be
170  
-     *      localized is returned. If any of the minimum, maximum or option
171  
-     *      values is <code>null</code>, the respective value is not checked.
172  
-     * </dl>
  142
+     * indication of the validation result.
173 143
      *
174  
-     * @param valueString The string representation of the value to validate.
  144
+     * @param valueString The string representation of the value to validate,
  145
+     *        can be <code>null</code>.
175 146
      *
176  
-     * @return As explained above.
  147
+     * @return <code>null</code> if no validation is performed, <tt>""</tt> if
  148
+     *         the value is accepted as valid, or a non-empty string 
  149
+     *         indicating a validation problem was found.
177 150
      *
  151
+     * @see ADValidator#validate(AD, String)
178 152
      * @see #VALIDATE_GREATER_THAN_MAXIMUM
179  
-     * @see #VALIDATE_LESS_THAN_MINIMUM
180 153
      * @see #VALIDATE_NOT_A_VALID_OPTION
  154
+     * @see #VALIDATE_LESS_THAN_MINIMUM
  155
+     * @see #VALIDATE_INVALID_VALUE
  156
+     * @see #VALIDATE_MISSING
181 157
      */
182  
-    public String validate( String valueString )
  158
+    public String validate(String valueString)
183 159
     {
184  
-    	return ADValidator.validate(this, valueString);
  160
+        return ADValidator.validate(this, valueString);
185 161
     }
186 162
 
187  
-
188 163
     //--------- Setters for setting up this instance --------------------------
189 164
 
190 165
     /**
191 166
      * @param id the id to set
192 167
      */
193  
-    public void setID( String id )
  168
+    public void setID(String id)
194 169
     {
195 170
         this.id = id;
196 171
     }
197 172
 
198  
-
199 173
     /**
200 174
      * @param name the name to set
201 175
      */
202  
-    public void setName( String name )
  176
+    public void setName(String name)
203 177
     {
204 178
         this.name = name;
205 179
     }
206 180
 
207  
-
208 181
     /**
209 182
      * @param description the description to set
210 183
      */
211  
-    public void setDescription( String description )
  184
+    public void setDescription(String description)
212 185
     {
213 186
         this.description = description;
214 187
     }
215 188
 
216  
-
217 189
     /**
218 190
      * @param typeString the type to set
219 191
      */
220  
-    public void setType( String typeString )
  192
+    public void setType(String typeString)
221 193
     {
222  
-        this.type = toType( typeString );
  194
+        this.type = toType(typeString);
223 195
     }
224 196
 
225  
-
226 197
     /**
227 198
      * @param cardinality the cardinality to set
228 199
      */
229  
-    public void setCardinality( int cardinality )
  200
+    public void setCardinality(int cardinality)
230 201
     {
231 202
         this.cardinality = cardinality;
232 203
     }
233 204
 
234  
-
235 205
     /**
236 206
      * @param options the options to set
237 207
      */
238  
-    public void setOptions( Map options )
  208
+    public void setOptions(Map options)
239 209
     {
240 210
         optionLabels = new String[options.size()];
241 211
         optionValues = new String[options.size()];
242 212
         int i = 0;
243  
-        for ( Iterator oi = options.entrySet().iterator(); oi.hasNext(); i++ )
  213
+        for (Iterator oi = options.entrySet().iterator(); oi.hasNext(); i++)
244 214
         {
245  
-            Map.Entry entry = ( Map.Entry ) oi.next();
246  
-            optionValues[i] = String.valueOf( entry.getKey() );
247  
-            optionLabels[i] = String.valueOf( entry.getValue() );
  215
+            Map.Entry entry = (Map.Entry) oi.next();
  216
+            optionValues[i] = String.valueOf(entry.getKey());
  217
+            optionLabels[i] = String.valueOf(entry.getValue());
248 218
         }
249 219
     }
250 220
 
251  
-
252 221
     /**
253 222
      * @param defaultValue the defaultValue to set
254 223
      */
255  
-    public void setDefaultValue( String defaultValue )
  224
+    public void setDefaultValue(String defaultValue)
256 225
     {
257  
-        this.defaultValue = splitList( defaultValue );
  226
+        this.defaultValue = splitList(defaultValue);
258 227
     }
259 228
 
260  
-
261 229
     /**
262 230
      * @param min the min to set
263 231
      */
264  
-    public void setMin( String min )
  232
+    public void setMin(String min)
265 233
     {
266 234
         this.min = min;
267 235
     }
268 236
 
269  
-
270 237
     /**
271 238
      * @param max the max to set
272 239
      */
273  
-    public void setMax( String max )
  240
+    public void setMax(String max)
274 241
     {
275 242
         this.max = max;
276 243
     }
277 244
 
278  
-
279 245
     /**
280 246
      * @param defaultValue the defaultValue to set
281 247
      */
282  
-    public void setDefaultValue( String[] defaultValue )
  248
+    public void setDefaultValue(String[] defaultValue)
283 249
     {
284  
-        this.defaultValue = ( String[] ) defaultValue.clone();
  250
+        this.defaultValue = (String[]) defaultValue.clone();
285 251
     }
286 252
 
287  
-
288 253
     /**
289 254
      * @param isRequired the isRequired to set
290 255
      */
291  
-    public void setRequired( boolean isRequired )
  256
+    public void setRequired(boolean isRequired)
292 257
     {
293 258
         this.isRequired = isRequired;
294 259
     }
295 260
 
296  
-
297  
-    public static int toType( String typeString )
  261
+    public static int toType(String typeString)
298 262
     {
299  
-        if ( "String".equals( typeString ) )
  263
+        if ("String".equals(typeString))
300 264
         {
301 265
             return AttributeDefinition.STRING;
302 266
         }
303  
-        else if ( "Long".equals( typeString ) )
  267
+        else if ("Long".equals(typeString))
304 268
         {
305 269
             return AttributeDefinition.LONG;
306 270
         }
307  
-        else if ( "Double".equals( typeString ) )
  271
+        else if ("Double".equals(typeString))
308 272
         {
309 273
             return AttributeDefinition.DOUBLE;
310 274
         }
311  
-        else if ( "Float".equals( typeString ) )
  275
+        else if ("Float".equals(typeString))
312 276
         {
313 277
             return AttributeDefinition.FLOAT;
314 278
         }
315  
-        else if ( "Integer".equals( typeString ) )
  279
+        else if ("Integer".equals(typeString))
316 280
         {
317 281
             return AttributeDefinition.INTEGER;
318 282
         }
319  
-        else if ( "Byte".equals( typeString ) )
  283
+        else if ("Byte".equals(typeString))
320 284
         {
321 285
             return AttributeDefinition.BYTE;
322 286
         }
323  
-        else if ( "Char".equals( typeString ) )
  287
+        else if ("Char".equals(typeString))
324 288
         {
325 289
             return AttributeDefinition.CHARACTER;
326 290
         }
327  
-        else if ( "Boolean".equals( typeString ) )
  291
+        else if ("Boolean".equals(typeString))
328 292
         {
329 293
             return AttributeDefinition.BOOLEAN;
330 294
         }
331  
-        else if ( "Short".equals( typeString ) )
  295
+        else if ("Short".equals(typeString))
332 296
         {
333 297
             return AttributeDefinition.SHORT;
334 298
         }
335  
-        else if ( "Password".equals( typeString ) )
  299
+        else if ("Password".equals(typeString))
336 300
         {
337 301
             return AttributeDefinition.PASSWORD;
338 302
         }
@@ -341,90 +305,102 @@ else if ( "Password".equals( typeString ) )
341 305
         return AttributeDefinition.STRING;
342 306
     }
343 307
 
344  
-
345  
-    public static String[] splitList( String listString )
  308
+    public static String[] splitList(String listString)
346 309
     {
347  
-		if (listString == null) {
348  
-			return null;
349  
-		} else if (listString.length() == 0) {
350  
-			return new String[] { "" };
351  
-		}
352  
-
353  
-		List strings = new ArrayList();
354  
-		StringBuffer sb = new StringBuffer();
355  
-
356  
-		int length = listString.length();
357  
-		boolean escaped = false;
358  
-		
359  
-		for (int i = 0; i < length; i++) {
360  
-			char ch = listString.charAt(i);
361  
-			if (ch == '\\') {
362  
-				if (!escaped) {
363  
-					escaped = true;
364  
-					continue;
365  
-				}
366  
-			} else if (ch == ',') {
367  
-				if (!escaped) {
368  
-					// unescaped comma, this is a string delimiter...
369  
-					strings.add(sb.toString());
370  
-					sb.setLength(0);
371  
-					continue;
372  
-				}
373  
-			} else if (ch == ' ') {
374  
-				// we should ignore spaces normally, unless they are escaped...
375  
-				if (!escaped) {
376  
-					continue;
377  
-				}
378  
-			} else if (Character.isWhitespace(ch)) {
379  
-				// Other whitespaces are ignored...
380  
-				continue;
381  
-			}
382  
-
383  
-			sb.append(ch);
384  
-			escaped = false;
385  
-		}
386  
-
387  
-		// Always add the last string, as it contains everything after the last comma...
388  
-		strings.add(sb.toString());
389  
-
390  
-		return (String[]) strings.toArray(new String[strings.size()]);
391  
-    }
  310
+        if (listString == null)
  311
+        {
  312
+            return null;
  313
+        }
  314
+        else if (listString.length() == 0)
  315
+        {
  316
+            return new String[] { "" };
  317
+        }
  318
+
  319
+        List strings = new ArrayList();
  320
+        StringBuffer sb = new StringBuffer();
392 321
 
  322
+        int length = listString.length();
  323
+        boolean escaped = false;
393 324
 
394  
-    protected Comparable convertToType( final String value )
  325
+        for (int i = 0; i < length; i++)
  326
+        {
  327
+            char ch = listString.charAt(i);
  328
+            if (ch == '\\')
  329
+            {
  330
+                if (!escaped)
  331
+                {
  332
+                    escaped = true;
  333
+                    continue;
  334
+                }
  335
+            }
  336
+            else if (ch == ',')
  337
+            {
  338
+                if (!escaped)
  339
+                {
  340
+                    // unescaped comma, this is a string delimiter...
  341
+                    strings.add(sb.toString());
  342
+                    sb.setLength(0);
  343
+                    continue;
  344
+                }
  345
+            }
  346
+            else if (ch == ' ')
  347
+            {
  348
+                // we should ignore spaces normally, unless they are escaped...
  349
+                if (!escaped)
  350
+                {
  351
+                    continue;
  352
+                }
  353
+            }
  354
+            else if (Character.isWhitespace(ch))
  355
+            {
  356
+                // Other whitespaces are ignored...
  357
+                continue;
  358
+            }
  359
+
  360
+            sb.append(ch);
  361
+            escaped = false;
  362
+        }
  363
+
  364
+        // Always add the last string, as it contains everything after the last comma...
  365
+        strings.add(sb.toString());
  366
+
  367
+        return (String[]) strings.toArray(new String[strings.size()]);
  368
+    }
  369
+
  370
+    protected Comparable convertToType(final String value)
395 371
     {
396  
-        if ( value != null && value.length() > 0 )
  372
+        if (value != null && value.length() > 0)
397 373
         {
398 374
             try
399 375
             {
400  
-                switch ( getType() )
  376
+                switch (getType())
401 377
                 {
402 378
                     case AttributeDefinition.BOOLEAN:
403 379
                         // Boolean is only Comparable starting with Java 5
404  
-                        return new ComparableBoolean( value );
  380
+                        return new ComparableBoolean(value);
405 381
                     case AttributeDefinition.CHARACTER:
406  
-                        return new Character( value.charAt( 0 ) );
  382
+                        return new Character(value.charAt(0));
407 383
                     case AttributeDefinition.BYTE:
408  
-                        return Byte.valueOf( value );
  384
+                        return Byte.valueOf(value);
409 385
                     case AttributeDefinition.SHORT:
410  
-                        return Short.valueOf( value );
  386
+                        return Short.valueOf(value);
411 387
                     case AttributeDefinition.INTEGER:
412  
-                        return Integer.valueOf( value );
  388
+                        return Integer.valueOf(value);
413 389
                     case AttributeDefinition.LONG:
414  
-                        return Long.valueOf( value );
  390
+                        return Long.valueOf(value);
415 391
                     case AttributeDefinition.FLOAT:
416  
-                        return Float.valueOf( value );
  392
+                        return Float.valueOf(value);
417 393
                     case AttributeDefinition.DOUBLE:
418  
-                        return Double.valueOf( value );
  394
+                        return Double.valueOf(value);
419 395
                     case AttributeDefinition.STRING:
420 396
                     case AttributeDefinition.PASSWORD:
421 397
                     default:
422 398
                         return value;
423 399
                 }
424 400
             }
425  
-            catch ( NumberFormatException nfe )
  401
+            catch (NumberFormatException nfe)
426 402
             {
427  
-                Activator.log( LogService.LOG_INFO, "Cannot convert value '" + value + "'", nfe );
  403
+                Activator.log(LogService.LOG_INFO, "Cannot convert value '" + value + "'", nfe);
428 404
             }
429 405
         }
430 406
 
@@ -435,17 +411,15 @@ protected Comparable convertToType( final String value )
435 411
     {
436 412
         private boolean value;
437 413
 
438  
-
439  
-        ComparableBoolean( String boolValue )
  414
+        ComparableBoolean(String boolValue)
440 415
         {
441  
-            value = Boolean.valueOf( boolValue ).booleanValue();
  416
+            value = Boolean.valueOf(boolValue).booleanValue();
442 417
         }
443 418
 
444  
-
445  
-        public int compareTo( Object obj )
  419
+        public int compareTo(Object obj)
446 420
         {
447  
-            ComparableBoolean cb = ( ComparableBoolean ) obj;
448  
-            return ( cb.value == value ? 0 : ( value ? 1 : -1 ) );
  421
+            ComparableBoolean cb = (ComparableBoolean) obj;
  422
+            return (cb.value == value ? 0 : (value ? 1 : -1));
449 423
         }
450 424
     }
451 425
 }
718  metatype/src/main/java/org/apache/felix/metatype/ADValidator.java
@@ -30,319 +30,407 @@
30 30
  * 
31 31
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
32 32
  */
33  
-final class ADValidator {
34  
-
35  
-	/**
36  
-	 * Validates a given input string according to the type specified by the
37  
-	 * given attribute definition.
38  
-	 * 
39  
-	 * @param ad
40  
-	 *            the attribute definition to use in the validation;
41  
-	 * @param rawInput
42  
-	 *            the raw input value to validate.
43  
-	 * @return <code>null</code> if no validation is available, <tt>""</tt> if
44  
-	 *         validation was successful, or any other non-empty string in case
45  
-	 *         validation fails.
46  
-	 */
47  
-	public static String validate(AD ad, String rawInput) {
48  
-		// Handle the case in which the given input is not defined...
49  
-		if (rawInput == null) {
50  
-			if (ad.isRequired()) {
51  
-				return AD.VALIDATE_MISSING;
52  
-			}
53  
-
54  
-			return ""; // accept null value...
55  
-		}
56  
-
57  
-		// Raw input is defined, validate it further
58  
-		String[] input;
59  
-
60  
-		if (ad.getCardinality() == 0) {
61  
-			input = new String[] { rawInput.trim() };
62  
-		} else {
63  
-			input = AD.splitList(rawInput);
64  
-		}
65  
-
66  
-		int type = ad.getType();
67  
-		switch (type) {
68  
-		case AttributeDefinition.BOOLEAN:
69  
-			return validateBooleanValue(ad, input);
70  
-
71  
-		case AttributeDefinition.CHARACTER:
72  
-			return validateCharacterValue(ad, input);
73  
-
74  
-		case AttributeDefinition.BIGDECIMAL:
75  
-		case AttributeDefinition.BIGINTEGER:
76  
-		case AttributeDefinition.BYTE:
77  
-		case AttributeDefinition.DOUBLE:
78  
-		case AttributeDefinition.FLOAT:
79  
-		case AttributeDefinition.INTEGER:
80  
-		case AttributeDefinition.LONG:
81  
-		case AttributeDefinition.SHORT:
82  
-			return validateNumericValue(ad, input);
83  
-
84  
-		case AttributeDefinition.PASSWORD:
85  
-		case AttributeDefinition.STRING:
86  
-			return validateString(ad, input);
87  
-
88  
-		default:
89  
-			return null; // no validation present...
90  
-		}
91  
-	}
92  
-
93  
-	/**
94  
-	 * Searches for a given search value in a given array of options.
95  
-	 * 
96  
-	 * @param searchValue
97  
-	 *            the value to search for;
98  
-	 * @param optionValues
99  
-	 *            the values to search in.
100  
-	 * @return <code>null</code> if the given search value is not found in the
101  
-	 *         given options, the searched value if found, or <tt>""</tt> if no
102  
-	 *         search value or options were given.
103  
-	 */
104  
-	private static String findOptionValue(String searchValue, String[] optionValues) {
105  
-		if ((searchValue == null) || (optionValues == null) || (optionValues.length == 0)) {
106  
-			// indicates that we've not searched
107  
-			return "";
108  
-		}
109  
-
110  
-		for (int i = 0; i < optionValues.length; i++) {
111  
-			if (optionValues[i].equals(searchValue)) {
112  
-				return optionValues[i];
113  
-			}
114  
-		}
115  
-
116  
-		return null;
117  
-	}
118  
-
119  
-	/**
120  
-	 * Parses a given string value into a numeric type.
121  
-	 * 
122  
-	 * @param type
123  
-	 *            the type to parse;
124  
-	 * @param value
125  
-	 *            the value to parse.
126  
-	 * @return a {@link Number} representation of the given value, or
127  
-	 *         <code>null</code> if the input was <code>null</code>, empty, or
128  
-	 *         not a numeric type.
129  
-	 * @throws NumberFormatException
130  
-	 *             in case the given value cannot be parsed as numeric value.
131  
-	 */
132  
-	private static Comparable parseNumber(int type, String value) throws NumberFormatException {
133  
-		if ((value != null) && (value.length() > 0)) {
134  
-			switch (type) {
135  
-			case AttributeDefinition.BIGDECIMAL:
136  
-				return new BigDecimal(value);
137  
-			case AttributeDefinition.BIGINTEGER:
138  
-				return new BigInteger(value);
139  
-			case AttributeDefinition.BYTE:
140  
-				return Byte.valueOf(value);
141  
-			case AttributeDefinition.SHORT:
142  
-				return Short.valueOf(value);
143  
-			case AttributeDefinition.INTEGER:
144  
-				return Integer.valueOf(value);
145  
-			case AttributeDefinition.LONG:
146  
-				return Long.valueOf(value);
147  
-			case AttributeDefinition.FLOAT:
148  
-				return Float.valueOf(value);
149  
-			case AttributeDefinition.DOUBLE:
150  
-				return Double.valueOf(value);
151  
-			default:
152  
-				return null;
153  
-			}
154  
-		}
155  
-		return null;
156  
-	}
157  
-
158  
-	/**
159  
-	 * Parses a given string value as character, allowing <code>null</code>
160  
-	 * -values and empty values to be given as input.
161  
-	 * 
162  
-	 * @param value
163  
-	 *            the value to parse as character, can be <code>null</code> or
164  
-	 *            an empty value.
165  
-	 * @return the character value if, and only if, the given input was non-
166  
-	 *         <code>null</code> and a non-empty string.
167  
-	 */
168  
-	private static Character parseOptionalChar(String value) {
169  
-		if ((value != null) && (value.length() > 0)) {
170  
-			return Character.valueOf(value.charAt(0));
171  
-		}
172  
-		return null;
173  
-	}
174  
-
175  
-	/**
176  
-	 * Parses a given string value as integer, allowing <code>null</code>-values
177  
-	 * and invalid numeric values to be given as input.
178  
-	 * 
179  
-	 * @param value
180  
-	 *            the value to parse as integer, can be <code>null</code> or a
181  
-	 *            non-numeric value.
182  
-	 * @return the integer value if, and only if, the given input was non-
183  
-	 *         <code>null</code> and a valid integer representation.
184  
-	 */
185  
-	private static Integer parseOptionalInt(String value) {
186  
-		if (value != null) {
187  
-			try {
188  
-				return Integer.valueOf(value);
189  
-			} catch (NumberFormatException e) {
190  
-				// Ignore; invalid value...
191  
-			}
192  
-		}
193  
-		return null;
194  
-	}
195  
-
196  
-	/**
197  
-	 * Validates a given input string as boolean value.
198  
-	 * 
199  
-	 * @param ad
200  
-	 *            the attribute definition to use in the validation;
201  
-	 * @param input
202  
-	 *            the array with input values to validate.
203  
-	 * @return <code>null</code> if no validation is available, <tt>""</tt> if
204  
-	 *         validation was successful, or any other non-empty string in case
205  
-	 *         validation fails.
206  
-	 */
207  
-	private static String validateBooleanValue(AD ad, String[] input) {
208  
-		for (int i = 0; i < input.length; i++) {
209  
-			int length = input[i].length();
210  
-			if ((length == 0) && ad.isRequired()) {
211  
-				return AD.VALIDATE_MISSING;
212  
-			} else if (length > 0 && !"true".equalsIgnoreCase(input[i]) && !"false".equalsIgnoreCase(input[i])) {
213  
-				return AD.VALIDATE_INVALID_VALUE;
214  
-			}
215  
-		}
216  
-
217  
-		String[] optionValues = ad.getOptionValues();
218  
-		if (optionValues != null && optionValues.length > 0) {
219  
-			return null; // no validation possible for this type...
220  
-		}
221  
-
222  
-		return ""; // accept given value...
223  
-	}
224  
-
225  
-	/**
226  
-	 * Validates a given input string as character value.
227  
-	 * 
228  
-	 * @param ad
229  
-	 *            the attribute definition to use in the validation;
230  
-	 * @param input
231  
-	 *            the array with input values to validate.
232  
-	 * @return <code>null</code> if no validation is available, <tt>""</tt> if
233  
-	 *         validation was successful, or any other non-empty string in case
234  
-	 *         validation fails.
235  
-	 */
236  
-	private static String validateCharacterValue(AD ad, String[] input) {
237  
-		Character min = parseOptionalChar(ad.getMin());
238  
-		Character max = parseOptionalChar(ad.getMax());
239  
-		String[] optionValues = ad.getOptionValues();
240  
-
241  
-		for (int i = 0; i < input.length; i++) {
242  
-			Character ch = null;
243  
-			int length = input[i].length();
244  
-			if (length > 1) {
245  
-				return AD.VALIDATE_GREATER_THAN_MAXIMUM;
246  
-			} else if ((length == 0) && ad.isRequired()) {
247  
-				return AD.VALIDATE_MISSING;
248  
-			} else if (length == 1) {
249  
-				ch = Character.valueOf(input[i].charAt(0));
250  
-				// Check whether the minimum value is adhered for all values...
251  
-				if ((min != null) && (ch.compareTo(min) < 0)) {
252  
-					return AD.VALIDATE_LESS_THAN_MINIMUM;
253  
-				}
254  
-				// Check whether the maximum value is adhered for all values...
255  
-				if ((max != null) && (ch.compareTo(max) > 0)) {
256  
-					return AD.VALIDATE_GREATER_THAN_MAXIMUM;
257  
-				}
258  
-			}
259  
-
260  
-			if (findOptionValue(input[i], optionValues) == null) {
261  
-				return AD.VALIDATE_NOT_A_VALID_OPTION;
262  
-			}
263  
-		}
264  
-
265  
-		return ""; // accept given value...
266  
-	}
267  
-
268  
-	/**
269  
-	 * Validates a given input string as numeric value.
270  
-	 * 
271  
-	 * @param ad
272  
-	 *            the attribute definition to use in the validation;
273  
-	 * @param input
274  
-	 *            the array with input values to validate.
275  
-	 * @return <code>null</code> if no validation is available, <tt>""</tt> if
276  
-	 *         validation was successful, or any other non-empty string in case
277  
-	 *         validation fails.
278  
-	 */
279  
-	private static String validateNumericValue(AD ad, String[] input) {
280  
-		Integer min = parseOptionalInt(ad.getMin());
281  
-		Integer max = parseOptionalInt(ad.getMax());
282  
-		String[] optionValues = ad.getOptionValues();
283  
-
284  
-		for (int i = 0; i < input.length; i++) {
285  
-			Comparable value = null;
286  
-			try {
287  
-				value = parseNumber(ad.getType(), input[i]);
288  
-			} catch (NumberFormatException e) {
289  
-				return AD.VALIDATE_INVALID_VALUE;
290  
-			}
291  
-
292  
-			if ((value == null) && ad.isRequired()) {
293  
-				// Possible if the cardinality != 0 and input was something like
294  
-				// "0,,1"...
295  
-				return AD.VALIDATE_MISSING;
296  
-			}
297  
-			// Check whether the minimum value is adhered for all values...
298  
-			if ((min != null) && (value != null) && (value.compareTo(min) < 0)) {
299  
-				return AD.VALIDATE_LESS_THAN_MINIMUM;
300  
-			}
301  
-			// Check whether the maximum value is adhered for all values...
302  
-			if ((max != null) && (value != null) && (value.compareTo(max) > 0)) {
303  
-				return AD.VALIDATE_GREATER_THAN_MAXIMUM;
304  
-			}
305  
-
306  
-			if (findOptionValue(input[i], optionValues) == null) {
307  
-				return AD.VALIDATE_NOT_A_VALID_OPTION;
308  
-			}
309  
-		}
310  
-
311  
-		return ""; // accept given value...
312  
-	}
313  
-
314  
-	/**
315  
-	 * Validates a given input string as string (or password).
316  
-	 * 
317  
-	 * @param ad
318  
-	 *            the attribute definition to use in the validation;
319  
-	 * @param input
320  
-	 *            the array with input values to validate.
321  
-	 * @return <code>null</code> if no validation is available, <tt>""</tt> if
322  
-	 *         validation was successful, or any other non-empty string in case
323  
-	 *         validation fails.
324  
-	 */
325  
-	private static String validateString(AD ad, String[] input) {
326  
-		Integer min = parseOptionalInt(ad.getMin());
327  
-		Integer max = parseOptionalInt(ad.getMax());
328  
-		String[] optionValues = ad.getOptionValues();
329  
-
330  
-		for (int i = 0; i < input.length; i++) {
331  
-			int length = input[i].length();
332  
-			// Check whether the minimum length is adhered for all values...
333  
-			if ((min != null) && (length < min.intValue())) {
334  
-				return AD.VALIDATE_LESS_THAN_MINIMUM;
335  
-			}
336  
-			// Check whether the maximum length is adhered for all values...
337  
-			if ((max != null) && (length > max.intValue())) {
338  
-				return AD.VALIDATE_GREATER_THAN_MAXIMUM;
339  
-			}
340  
-
341  
-			if (findOptionValue(input[i], optionValues) == null) {
342  
-				return AD.VALIDATE_NOT_A_VALID_OPTION;
343  
-			}
344  
-		}
345  
-
346  
-		return ""; // accept given value...
347  
-	}
  33
+final class ADValidator
  34
+{
  35
+    /**
  36
+     * Validates a given input string according to the type specified by the given attribute 
  37
+     * definition.
  38
+     * <p>
  39
+     * The validation is done in the following way:
  40
+     * </p>
  41
+     * <ul>
  42
+     * <li>If the input is undefined (ie. <code>null</code>), and the attribute is mandatory, the 
  43
+     * validation fails due to a missing value. If the attribute is optional, the input is 
  44
+     * accepted;</li>
  45
+     * <li>If the input represents a <em>boolean</em> value, it is tested whether it is defined (in
  46
+     * case of non-zero cardinality) and represents either <tt>"true"</tt> or <tt>"false"</tt>. The
  47
+     * minimum and maximum parameters are <b>not</b> used in this validation;</li>
  48
+     * <li>If the input represents a <em>character</em> value, it is tested whether it is defined 
  49
+     * (in case of non-zero cardinality). The character value must be defined within the character 
  50
+     * range specified by the minimum and maximum parameters (if defined);</li>
  51
+     * <li>If the input represents a <em>numeric</em> value, it is tested whether it is defined (in
  52
+     * case of non-zero cardinality). The numeric value must be defined within the numeric range 
  53
+     * specified by the minimum and maximum parameters (if defined);</li>
  54
+     * <li>If the input represents a <em>string</em> or <em>password</em>, it is tested whether it
  55
+     * is defined (in case of non-zero cardinality). The length of the string value must be in the
  56
+     * range specified by the minimum and maximum parameters (if defined).</li>
  57
+     * </ul>
  58
+     * <p>
  59
+     * For all types of attributes, if it defines option values, the input should be present as one
  60
+     * of the defined option values. 
  61
+     * </p>
  62
+     * 
  63
+     * @param ad
  64
+     *            the attribute definition to use in the validation;
  65
+     * @param rawInput
  66
+     *            the raw input value to validate.
  67
+     * @return <code>null</code> if no validation is available, <tt>""</tt> if
  68
+     *         validation was successful, or any other non-empty string in case
  69
+     *         validation fails.
  70
+     */
  71
+    public static String validate(AD ad, String rawInput)
  72
+    {
  73
+        // Handle the case in which the given input is not defined...
  74
+        if (rawInput == null)
  75
+        {
  76
+            if (ad.isRequired())
  77
+            {
  78
+                return AD.VALIDATE_MISSING;
  79
+            }
  80
+
  81
+            return ""; // accept null value...
  82
+        }
  83
+
  84
+        // Raw input is defined, validate it further
  85
+        String[] input;
  86
+        if (ad.getCardinality() == 0)
  87
+        {
  88
+            input = new String[] { rawInput.trim() };
  89
+        }
  90
+        else
  91
+        {
  92
+            input = AD.splitList(rawInput);
  93
+        }
  94
+
  95
+        int type = ad.getType();
  96
+        switch (type)
  97
+        {
  98
+            case AttributeDefinition.BOOLEAN:
  99
+                return validateBooleanValue(ad, input);
  100
+
  101
+            case AttributeDefinition.CHARACTER:
  102
+                return validateCharacterValue(ad, input);
  103
+
  104
+            case AttributeDefinition.BIGDECIMAL:
  105
+            case AttributeDefinition.BIGINTEGER:
  106
+            case AttributeDefinition.BYTE:
  107
+            case AttributeDefinition.DOUBLE:
  108
+            case AttributeDefinition.FLOAT:
  109
+            case AttributeDefinition.INTEGER:
  110
+            case AttributeDefinition.LONG:
  111
+            case AttributeDefinition.SHORT:
  112
+                return validateNumericValue(ad, input);
  113
+
  114
+            case AttributeDefinition.PASSWORD:
  115
+            case AttributeDefinition.STRING:
  116
+                return validateString(ad, input);
  117
+
  118
+            default:
  119
+                return null; // no validation present...
  120
+        }
  121
+    }
  122
+
  123
+    /**
  124
+     * Searches for a given search value in a given array of options.
  125
+     * 
  126
+     * @param searchValue
  127
+     *            the value to search for;
  128
+     * @param optionValues
  129
+     *            the values to search in.
  130
+     * @return <code>null</code> if the given search value is not found in the
  131
+     *         given options, the searched value if found, or <tt>""</tt> if no
  132
+     *         search value or options were given.
  133
+     */
  134
+    private static String findOptionValue(String searchValue, String[] optionValues)
  135
+    {
  136
+        if ((searchValue == null) || (optionValues == null) || (optionValues.length == 0))
  137
+        {
  138
+            // indicates that we've not searched...
  139
+            return "";
  140
+        }
  141
+
  142
+        for (int i = 0; i < optionValues.length; i++)
  143
+        {
  144
+            if (optionValues[i].equals(searchValue))
  145
+            {
  146
+                return optionValues[i];
  147
+            }
  148
+        }
  149
+
  150
+        return null;
  151
+    }
  152
+
  153
+    /**
  154
+     * Parses a given string value into a numeric type.
  155
+     * 
  156
+     * @param type
  157
+     *            the type to parse;
  158
+     * @param value
  159
+     *            the value to parse.
  160
+     * @return a {@link Number} representation of the given value, or
  161
+     *         <code>null</code> if the input was <code>null</code>, empty, or
  162
+     *         not a numeric type.
  163
+     * @throws NumberFormatException
  164
+     *             in case the given value cannot be parsed as numeric value.
  165
+     */
  166
+    private static Comparable parseNumber(int type, String value) throws NumberFormatException
  167
+    {
  168
+        if ((value != null) && (value.length() > 0))
  169
+        {
  170
+            switch (type)
  171
+            {
  172
+                case AttributeDefinition.BIGDECIMAL:
  173
+                    return new BigDecimal(value);
  174
+                case AttributeDefinition.BIGINTEGER:
  175
+                    return new BigInteger(value);
  176
+                case AttributeDefinition.BYTE:
  177
+                    return Byte.valueOf(value);
  178
+                case AttributeDefinition.SHORT:
  179
+                    return Short.valueOf(value);
  180
+                case AttributeDefinition.INTEGER:
  181
+                    return Integer.valueOf(value);
  182
+                case AttributeDefinition.LONG:
  183
+                    return Long.valueOf(value);
  184
+                case AttributeDefinition.FLOAT:
  185
+                    return Float.valueOf(value);
  186
+                case AttributeDefinition.DOUBLE:
  187
+                    return Double.valueOf(value);
  188
+                default:
  189
+                    return null;
  190
+            }
  191
+        }
  192
+        return null;
  193
+    }
  194
+
  195
+    /**
  196
+     * Parses a given string value as character, allowing <code>null</code>
  197
+     * -values and empty values to be given as input.
  198
+     * 
  199
+     * @param value
  200
+     *            the value to parse as character, can be <code>null</code> or
  201
+     *            an empty value.
  202
+     * @return the character value if, and only if, the given input was non-
  203
+     *         <code>null</code> and a non-empty string.
  204
+     */
  205
+    private static Character parseOptionalChar(String value)
  206
+    {
  207
+        if ((value != null) && (value.length() > 0))
  208
+        {
  209
+            return Character.valueOf(value.charAt(0));
  210
+        }
  211
+        return null;
  212
+    }
  213
+
  214
+    /**
  215
+     * Parses a given string value as numeric value, allowing 
  216
+     * <code>null</code>-values and invalid numeric values to be given as 
  217
+     * input.
  218
+     * 
  219
+     * @param type the type of number, should only be a numeric type;
  220
+     * @param value
  221
+     *            the value to parse as integer, can be <code>null</code> or a
  222
+     *            non-numeric value.
  223
+     * @return the integer value if, and only if, the given input was non-
  224
+     *         <code>null</code> and a valid integer representation.
  225
+     */
  226
+    private static Comparable parseOptionalNumber(int type, String value)
  227
+    {
  228
+        if (value != null)
  229
+        {
  230
+            try
  231
+            {
  232
+                return parseNumber(type, value);
  233
+            }
  234
+            catch (NumberFormatException e)
  235
+            {
  236
+                // Ignore; invalid value...
  237
+