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