View Javadoc

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  // FUTURE Rename to AbstractRule when cleaning up @deprecated
17  // FUTURE Move PropertyDescriptor APIs up to Rule interface
18  // FUTURE Implement Cloneable and clone()
19  public abstract class CommonAbstractRule implements Rule {
20  
21  	// TODO Remove - Temporary flag during conversion.
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  	// FUTURE Remove when cleaning up @deprecated
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  		// TODO Needs to be externally immutable
90  		return examples;
91  	}
92  
93  	// FUTURE Remove when cleaning up @deprecated
94  	public String getExample() {
95  		if (examples.isEmpty()) {
96  			return null;
97  		} else {
98  			// We return the last example, so the override still works
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 	// FUTURE Remove when cleaning up @deprecated
128 	public boolean include() {
129 		return include;
130 	}
131 
132 	// FUTURE Remove when cleaning up @deprecated
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 		// TODO Needs to be externally immutable
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 ? // TODO -remove
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 	// TODO
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 	// TODO
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 	// TODO
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 		// Override as needed
361 	}
362 
363 	public void end(RuleContext ctx) {
364 		// Override as needed
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; // trivial
380 		}
381 
382 		if (this == o) {
383 			return true; // trivial
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 }