1 package net.sourceforge.pmd;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Properties;
9
10 /**
11 * Basic abstract implementation of all parser-independent methods of the Rule
12 * interface.
13 *
14 * @author pieter_van_raemdonck - Application Engineers NV/SA - www.ae.be
15 */
16
17
18
19 public abstract class CommonAbstractRule implements Rule {
20
21
22 private static final boolean IN_OLD_PROPERTY_MODE = true;
23
24 private String name = getClass().getName();
25 private String since;
26 private String ruleClass = getClass().getName();
27 private String ruleSetName;
28 private String message;
29 private String description;
30 private List<String> examples = new ArrayList<String>();
31 private String externalInfoUrl;
32 private int priority = LOWEST_PRIORITY;
33
34 private boolean include;
35 private Properties properties = new Properties();
36 private boolean usesDFA;
37 private boolean usesTypeResolution;
38 private List<String> ruleChainVisits = new ArrayList<String>();
39
40 public String getName() {
41 return name;
42 }
43
44 public void setName(String name) {
45 this.name = name;
46 }
47
48 public String getSince() {
49 return since;
50 }
51
52 public void setSince(String since) {
53 this.since = since;
54 }
55
56 public String getRuleClass() {
57 return ruleClass;
58 }
59
60 public void setRuleClass(String ruleClass) {
61 this.ruleClass = ruleClass;
62 }
63
64 public String getRuleSetName() {
65 return ruleSetName;
66 }
67
68 public void setRuleSetName(String ruleSetName) {
69 this.ruleSetName = ruleSetName;
70 }
71
72 public String getMessage() {
73 return message;
74 }
75
76 public void setMessage(String message) {
77 this.message = message;
78 }
79
80 public String getDescription() {
81 return description;
82 }
83
84 public void setDescription(String description) {
85 this.description = description;
86 }
87
88 public List<String> getExamples() {
89
90 return examples;
91 }
92
93
94 public String getExample() {
95 if (examples.isEmpty()) {
96 return null;
97 } else {
98
99 return examples.get(examples.size() - 1);
100 }
101 }
102
103 public void addExample(String example) {
104 examples.add(example);
105 }
106
107 public String getExternalInfoUrl() {
108 return externalInfoUrl;
109 }
110
111 public void setExternalInfoUrl(String externalInfoUrl) {
112 this.externalInfoUrl = externalInfoUrl;
113 }
114
115 public int getPriority() {
116 return priority;
117 }
118
119 public void setPriority(int priority) {
120 this.priority = priority;
121 }
122
123 public String getPriorityName() {
124 return PRIORITIES[getPriority() - 1];
125 }
126
127
128 public boolean include() {
129 return include;
130 }
131
132
133 public void setInclude(boolean include) {
134 this.include = include;
135 }
136
137 /**
138 * @deprecated - retrieve by name using get<type>Property or get<type>Properties
139 */
140 public Properties getProperties() {
141
142 return properties;
143 }
144
145 /**
146 * @deprecated
147 */
148 public void addProperty(String name, String value) {
149 getProperties().setProperty(name, value);
150 }
151
152 /**
153 * @deprecated
154 */
155 public void addProperties(Properties properties) {
156 getProperties().putAll(properties);
157 }
158
159 /**
160 * @deprecated - property values will be guaranteed available via default
161 * values
162 */
163 public boolean hasProperty(String name) {
164 return IN_OLD_PROPERTY_MODE ?
165 getProperties().containsKey(name)
166 : propertiesByName().containsKey(name);
167 }
168
169 /**
170 * @deprecated - use getBooleanProperty(PropertyDescriptor) instead
171 */
172 public boolean getBooleanProperty(String name) {
173 return Boolean.parseBoolean(getProperties().getProperty(name));
174 }
175
176 public boolean getBooleanProperty(PropertyDescriptor descriptor) {
177
178 return ((Boolean)getProperty(descriptor)).booleanValue();
179 }
180
181
182 public boolean[] getBooleanProperties(PropertyDescriptor descriptor) {
183 Boolean[] values = (Boolean[])getProperties(descriptor);
184 boolean[] bools = new boolean[values.length];
185 for (int i = 0; i < bools.length; i++)
186 bools[i] = values[i].booleanValue();
187 return bools;
188 }
189
190 /**
191 * @deprecated - use getIntProperty(PropertyDescriptor) instead
192 */
193 public int getIntProperty(String name) {
194 return Integer.parseInt(getProperties().getProperty(name));
195 }
196
197 public int getIntProperty(PropertyDescriptor descriptor) {
198
199 return ((Number)getProperty(descriptor)).intValue();
200 }
201
202
203 public int[] getIntProperties(PropertyDescriptor descriptor) {
204 Number[] values = (Number[])getProperties(descriptor);
205 int[] ints = new int[values.length];
206 for (int i = 0; i < ints.length; i++)
207 ints[i] = values[i].intValue();
208 return ints;
209 }
210
211 /**
212 * @deprecated - use getDoubleProperty(PropertyDescriptor) instead
213 */
214 public double getDoubleProperty(String name) {
215 return Double.parseDouble(getProperties().getProperty(name));
216 }
217
218 public double getDoubleProperty(PropertyDescriptor descriptor) {
219 return ((Number)getProperty(descriptor)).doubleValue();
220 }
221
222
223 public double[] getDoubleProperties(PropertyDescriptor descriptor) {
224 Number[] values = (Number[])getProperties(descriptor);
225 double[] doubles = new double[values.length];
226 for (int i = 0; i < doubles.length; i++)
227 doubles[i] = values[i].doubleValue();
228 return doubles;
229 }
230
231 /**
232 * @deprecated - use getStringProperty(PropertyDescriptor) instead
233 */
234 public String getStringProperty(String name) {
235 return getProperties().getProperty(name);
236 }
237
238 public String getStringProperty(PropertyDescriptor descriptor) {
239 return (String)getProperty(descriptor);
240 }
241
242 public String[] getStringProperties(PropertyDescriptor descriptor) {
243 return (String[])getProperties(descriptor);
244 }
245
246 public Class[] getTypeProperties(PropertyDescriptor descriptor) {
247 return (Class[])getProperties(descriptor);
248 }
249
250 public Class getTypeProperty(PropertyDescriptor descriptor) {
251 return (Class)getProperty(descriptor);
252 }
253
254 private Object getProperty(PropertyDescriptor descriptor) {
255 if (descriptor.maxValueCount() > 1) {
256 propertyGetError(descriptor, true);
257 }
258 String rawValue = getProperties().getProperty(descriptor.name());
259 return rawValue == null || rawValue.length() == 0 ? descriptor
260 .defaultValue() : descriptor.valueFrom(rawValue);
261 }
262
263 public void setProperty(PropertyDescriptor descriptor, Object value) {
264 if (descriptor.maxValueCount() > 1) {
265 propertySetError(descriptor, true);
266 }
267 getProperties().setProperty(descriptor.name(),
268 descriptor.asDelimitedString(value));
269 }
270
271 private Object[] getProperties(PropertyDescriptor descriptor) {
272 if (descriptor.maxValueCount() == 1) {
273 propertyGetError(descriptor, false);
274 }
275 String rawValue = getProperties().getProperty(descriptor.name());
276 return rawValue == null || rawValue.length() == 0 ? (Object[])descriptor
277 .defaultValue()
278 : (Object[])descriptor.valueFrom(rawValue);
279 }
280
281 public void setProperties(PropertyDescriptor descriptor, Object[] values) {
282 if (descriptor.maxValueCount() == 1) {
283 propertySetError(descriptor, false);
284 }
285 getProperties().setProperty(descriptor.name(),
286 descriptor.asDelimitedString(values));
287 }
288
289 /**
290 * Return all the relevant properties for the receiver by overriding in
291 * subclasses as necessary.
292 *
293 * @return Map
294 */
295 protected Map<String, PropertyDescriptor> propertiesByName() {
296 return Collections.emptyMap();
297 }
298
299 public PropertyDescriptor propertyDescriptorFor(String name) {
300 PropertyDescriptor descriptor = propertiesByName().get(name);
301 if (descriptor == null) {
302 throw new IllegalArgumentException("Unknown property: " + name);
303 }
304 return descriptor;
305 }
306
307 private void propertyGetError(PropertyDescriptor descriptor,
308 boolean requestedSingleValue) {
309
310 if (requestedSingleValue) {
311 throw new RuntimeException(
312 "Cannot retrieve a single value from a multi-value property field");
313 }
314 throw new RuntimeException(
315 "Cannot retrieve multiple values from a single-value property field");
316 }
317
318 private void propertySetError(PropertyDescriptor descriptor,
319 boolean setSingleValue) {
320
321 if (setSingleValue) {
322 throw new RuntimeException(
323 "Cannot set a single value within a multi-value property field");
324 }
325 throw new RuntimeException(
326 "Cannot set multiple values within a single-value property field");
327 }
328
329 public void setUsesDFA() {
330 this.usesDFA = true;
331 }
332
333 public boolean usesDFA() {
334 return this.usesDFA;
335 }
336
337 public void setUsesTypeResolution() {
338 this.usesTypeResolution = true;
339 }
340
341 public boolean usesTypeResolution() {
342 return this.usesTypeResolution;
343 }
344
345 public boolean usesRuleChain() {
346 return !getRuleChainVisits().isEmpty();
347 }
348
349 public List<String> getRuleChainVisits() {
350 return ruleChainVisits;
351 }
352
353 public void addRuleChainVisit(String astNodeName) {
354 if (!ruleChainVisits.contains(astNodeName)) {
355 ruleChainVisits.add(astNodeName);
356 }
357 }
358
359 public void start(RuleContext ctx) {
360
361 }
362
363 public void end(RuleContext ctx) {
364
365 }
366
367 /**
368 * Rules are equal if:
369 * <ol>
370 * <li>They have the same implementation class.</li>
371 * <li>They have the same name.</li>
372 * <li>They have the same priority.</li>
373 * <li>They share the same properties.</li>
374 * </ol>
375 */
376 @Override
377 public boolean equals(Object o) {
378 if (o == null) {
379 return false;
380 }
381
382 if (this == o) {
383 return true;
384 }
385
386 boolean equality = this.getClass().getName().equals(
387 o.getClass().getName());
388
389 if (equality) {
390 Rule that = (Rule)o;
391 equality = this.getName().equals(that.getName())
392 && this.getPriority() == that.getPriority()
393 && this.getProperties().equals(that.getProperties());
394 }
395
396 return equality;
397 }
398
399 /**
400 * @see #equals(Object)
401 */
402 @Override
403 public int hashCode() {
404 return this.getClass().getName().hashCode()
405 + (this.getName() != null ? this.getName().hashCode() : 0)
406 + this.getPriority()
407 + (this.getProperties() != null ? this.getProperties()
408 .hashCode() : 0);
409 }
410
411 protected static Map<String, PropertyDescriptor> asFixedMap(
412 PropertyDescriptor[] descriptors) {
413 Map<String, PropertyDescriptor> descriptorsByName = new HashMap<String, PropertyDescriptor>(
414 descriptors.length);
415 for (PropertyDescriptor descriptor : descriptors) {
416 descriptorsByName.put(descriptor.name(), descriptor);
417 }
418 return Collections.unmodifiableMap(descriptorsByName);
419 }
420
421 protected static Map<String, PropertyDescriptor> asFixedMap(
422 PropertyDescriptor descriptor) {
423 return asFixedMap(new PropertyDescriptor[] { descriptor });
424 }
425 }