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