1
2
3
4 package net.sourceforge.pmd.lang.rule.properties;
5
6 import static net.sourceforge.pmd.PropertyDescriptorFields.DEFAULT_VALUE;
7 import static net.sourceforge.pmd.PropertyDescriptorFields.DESCRIPTION;
8 import static net.sourceforge.pmd.PropertyDescriptorFields.NAME;
9
10 import java.util.HashMap;
11 import java.util.Map;
12
13 import net.sourceforge.pmd.PropertyDescriptor;
14 import net.sourceforge.pmd.PropertyDescriptorFields;
15 import net.sourceforge.pmd.Rule;
16 import net.sourceforge.pmd.util.StringUtil;
17
18
19
20
21
22
23 public abstract class AbstractProperty<T> implements PropertyDescriptor<T> {
24
25 private final String name;
26 private final String description;
27 private final T defaultValue;
28 private final boolean isRequired;
29 private final float uiOrder;
30
31
32
33
34
35 public static final char DEFAULT_DELIMITER = '|';
36
37
38
39 public static final char DEFAULT_NUMERIC_DELIMITER = ',';
40
41 private char multiValueDelimiter = DEFAULT_DELIMITER;
42
43 protected AbstractProperty(String theName, String theDescription, T theDefault, float theUIOrder) {
44 this(theName, theDescription, theDefault, theUIOrder, DEFAULT_DELIMITER);
45 }
46
47
48
49
50
51
52
53
54
55
56 protected AbstractProperty(String theName, String theDescription, T theDefault, float theUIOrder, char delimiter) {
57 name = checkNotEmpty(theName, NAME);
58 description = checkNotEmpty(theDescription, DESCRIPTION);
59 defaultValue = theDefault;
60 isRequired = false;
61 uiOrder = checkPositive(theUIOrder, "UI order");
62 multiValueDelimiter = delimiter;
63 }
64
65
66
67
68
69
70
71 private static String checkNotEmpty(String arg, String argId) {
72
73 if (StringUtil.isEmpty(arg)) {
74 throw new IllegalArgumentException("Property attribute '" + argId + "' cannot be null or blank");
75 }
76
77 return arg;
78 }
79
80
81
82
83
84
85
86 private static float checkPositive(float arg, String argId) {
87 if (arg < 0) {
88 throw new IllegalArgumentException("Property attribute " + argId + "' must be zero or positive");
89 }
90 return arg;
91 }
92
93
94
95
96 public char multiValueDelimiter() {
97 return multiValueDelimiter;
98 }
99
100
101
102
103 public String name() {
104 return name;
105 }
106
107
108
109
110 public String description() {
111 return description;
112 }
113
114
115
116
117 public T defaultValue() {
118 return defaultValue;
119 }
120
121
122
123
124
125
126 protected boolean defaultHasNullValue() {
127
128 if (defaultValue == null) {
129 return true;
130 }
131
132 if (isMultiValue() && isArray(defaultValue)) {
133 Object[] defaults = (Object[]) defaultValue;
134 for (Object default1 : defaults) {
135 if (default1 == null) {
136 return true;
137 }
138 }
139 }
140
141 return false;
142 }
143
144
145
146
147 public boolean isMultiValue() {
148 return false;
149 }
150
151
152
153
154 public boolean isRequired() {
155 return isRequired;
156 }
157
158
159
160
161 public float uiOrder() {
162 return uiOrder;
163 }
164
165
166
167
168
169
170
171
172 protected String asString(Object value) {
173 return value == null ? "" : value.toString();
174 }
175
176
177
178
179 public String asDelimitedString(T values) {
180 return asDelimitedString(values, multiValueDelimiter());
181 }
182
183
184
185
186
187
188
189
190
191 public String asDelimitedString(T values, char delimiter) {
192 if (values == null) {
193 return "";
194 }
195
196 if (values instanceof Object[]) {
197 Object[] valueSet = (Object[]) values;
198 if (valueSet.length == 0) {
199 return "";
200 }
201 if (valueSet.length == 1) {
202 return asString(valueSet[0]);
203 }
204
205 StringBuilder sb = new StringBuilder();
206 sb.append(asString(valueSet[0]));
207 for (int i = 1; i < valueSet.length; i++) {
208 sb.append(delimiter);
209 sb.append(asString(valueSet[i]));
210 }
211 return sb.toString();
212 }
213
214 return asString(values);
215 }
216
217
218
219
220 public int compareTo(PropertyDescriptor<?> otherProperty) {
221 float otherOrder = otherProperty.uiOrder();
222 return (int) (otherOrder - uiOrder);
223 }
224
225
226
227
228 public String errorFor(Object value) {
229
230 String typeError = typeErrorFor(value);
231 if (typeError != null) {
232 return typeError;
233 }
234 return isMultiValue() ? valuesErrorFor(value) : valueErrorFor(value);
235 }
236
237
238
239
240
241 protected String valueErrorFor(Object value) {
242
243 if (value == null) {
244 if (defaultHasNullValue()) {
245 return null;
246 }
247 return "missing value";
248 }
249 return null;
250 }
251
252
253
254
255
256 protected String valuesErrorFor(Object value) {
257
258 if (!isArray(value)) {
259 return "multiple values expected";
260 }
261
262 Object[] values = (Object[]) value;
263
264 String err = null;
265 for (Object value2 : values) {
266 err = valueErrorFor(value2);
267 if (err != null) {
268 return err;
269 }
270 }
271
272 return null;
273 }
274
275
276
277
278
279 protected static boolean isArray(Object value) {
280 return value != null && value.getClass().getComponentType() != null;
281 }
282
283
284
285
286
287 protected String typeErrorFor(Object value) {
288
289 if (value == null && !isRequired) {
290 return null;
291 }
292
293 if (isMultiValue()) {
294 if (!isArray(value)) {
295 return "Value is not an array of type: " + type();
296 }
297
298 Class<?> arrayType = value.getClass().getComponentType();
299 if (arrayType == null || !arrayType.isAssignableFrom(type().getComponentType())) {
300 return "Value is not an array of type: " + type();
301 }
302 return null;
303 }
304
305 if (!type().isAssignableFrom(value.getClass())) {
306 return value + " is not an instance of " + type();
307 }
308
309 return null;
310 }
311
312
313
314
315 public String propertyErrorFor(Rule rule) {
316 Object realValue = rule.getProperty(this);
317 if (realValue == null && !isRequired()) {
318 return null;
319 }
320 return errorFor(realValue);
321 }
322
323
324
325
326 public Object[][] choices() {
327 return null;
328 }
329
330
331
332
333 public int preferredRowCount() {
334 return 1;
335 }
336
337
338
339
340 @Override
341 public boolean equals(Object obj) {
342 if (this == obj) {
343 return true;
344 }
345 if (obj == null) {
346 return false;
347 }
348 if (obj instanceof PropertyDescriptor) {
349 return name.equals(((PropertyDescriptor<?>) obj).name());
350 }
351 return false;
352 }
353
354
355
356
357 @Override
358 public int hashCode() {
359 return name.hashCode();
360 }
361
362
363
364
365 @Override
366 public String toString() {
367 return "[PropertyDescriptor: name=" + name() + ", type=" + type() + ", value=" + defaultValue() + "]";
368 }
369
370
371
372
373 protected String defaultAsString() {
374 if (isMultiValue()) {
375 return asDelimitedString(defaultValue(), multiValueDelimiter());
376 } else {
377 return defaultValue().toString();
378 }
379 }
380
381
382
383
384
385
386 @SuppressWarnings("PMD.CompareObjectsWithEquals")
387 public static final boolean areEqual(Object value, Object otherValue) {
388 if (value == otherValue) {
389 return true;
390 }
391 if (value == null) {
392 return false;
393 }
394 if (otherValue == null) {
395 return false;
396 }
397
398 return value.equals(otherValue);
399 }
400
401
402
403
404 public Map<String, String> attributeValuesById() {
405
406 Map<String, String> values = new HashMap<String, String>();
407 addAttributesTo(values);
408 return values;
409 }
410
411
412
413
414 protected void addAttributesTo(Map<String, String> attributes) {
415 attributes.put(NAME, name);
416 attributes.put(DESCRIPTION, description);
417 attributes.put(DEFAULT_VALUE, defaultAsString());
418 if (isMultiValue()) {
419 attributes.put(PropertyDescriptorFields.DELIMITER, Character.toString(multiValueDelimiter()));
420 }
421 }
422
423 }