001: package net.sourceforge.pmd;
002:
003: import java.util.ArrayList;
004: import java.util.Collections;
005: import java.util.HashMap;
006: import java.util.List;
007: import java.util.Map;
008: import java.util.Properties;
009:
010: /**
011: * Basic abstract implementation of all parser-independent methods of the Rule
012: * interface.
013: *
014: * @author pieter_van_raemdonck - Application Engineers NV/SA - www.ae.be
015: */
016: // FUTURE Rename to AbstractRule when cleaning up @deprecated
017: // FUTURE Move PropertyDescriptor APIs up to Rule interface
018: // FUTURE Implement Cloneable and clone()
019: public abstract class CommonAbstractRule implements Rule {
020:
021: // TODO Remove - Temporary flag during conversion.
022: private static final boolean IN_OLD_PROPERTY_MODE = true;
023:
024: private String name = getClass().getName();
025: private String since;
026: private String ruleClass = getClass().getName();
027: private String ruleSetName;
028: private String message;
029: private String description;
030: private List<String> examples = new ArrayList<String>();
031: private String externalInfoUrl;
032: private int priority = LOWEST_PRIORITY;
033: // FUTURE Remove when cleaning up @deprecated
034: private boolean include;
035: private Properties properties = new Properties();
036: private boolean usesDFA;
037: private boolean usesTypeResolution;
038: private List<String> ruleChainVisits = new ArrayList<String>();
039:
040: public String getName() {
041: return name;
042: }
043:
044: public void setName(String name) {
045: this .name = name;
046: }
047:
048: public String getSince() {
049: return since;
050: }
051:
052: public void setSince(String since) {
053: this .since = since;
054: }
055:
056: public String getRuleClass() {
057: return ruleClass;
058: }
059:
060: public void setRuleClass(String ruleClass) {
061: this .ruleClass = ruleClass;
062: }
063:
064: public String getRuleSetName() {
065: return ruleSetName;
066: }
067:
068: public void setRuleSetName(String ruleSetName) {
069: this .ruleSetName = ruleSetName;
070: }
071:
072: public String getMessage() {
073: return message;
074: }
075:
076: public void setMessage(String message) {
077: this .message = message;
078: }
079:
080: public String getDescription() {
081: return description;
082: }
083:
084: public void setDescription(String description) {
085: this .description = description;
086: }
087:
088: public List<String> getExamples() {
089: // TODO Needs to be externally immutable
090: return examples;
091: }
092:
093: // FUTURE Remove when cleaning up @deprecated
094: public String getExample() {
095: if (examples.isEmpty()) {
096: return null;
097: } else {
098: // We return the last example, so the override still works
099: 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()
259: .getProperty(descriptor.name());
260: return rawValue == null || rawValue.length() == 0 ? descriptor
261: .defaultValue() : descriptor.valueFrom(rawValue);
262: }
263:
264: public void setProperty(PropertyDescriptor descriptor, Object value) {
265: if (descriptor.maxValueCount() > 1) {
266: propertySetError(descriptor, true);
267: }
268: getProperties().setProperty(descriptor.name(),
269: descriptor.asDelimitedString(value));
270: }
271:
272: private Object[] getProperties(PropertyDescriptor descriptor) {
273: if (descriptor.maxValueCount() == 1) {
274: propertyGetError(descriptor, false);
275: }
276: String rawValue = getProperties()
277: .getProperty(descriptor.name());
278: return rawValue == null || rawValue.length() == 0 ? (Object[]) descriptor
279: .defaultValue()
280: : (Object[]) descriptor.valueFrom(rawValue);
281: }
282:
283: public void setProperties(PropertyDescriptor descriptor,
284: Object[] values) {
285: if (descriptor.maxValueCount() == 1) {
286: propertySetError(descriptor, false);
287: }
288: getProperties().setProperty(descriptor.name(),
289: descriptor.asDelimitedString(values));
290: }
291:
292: /**
293: * Return all the relevant properties for the receiver by overriding in
294: * subclasses as necessary.
295: *
296: * @return Map
297: */
298: protected Map<String, PropertyDescriptor> propertiesByName() {
299: return Collections.emptyMap();
300: }
301:
302: public PropertyDescriptor propertyDescriptorFor(String name) {
303: PropertyDescriptor descriptor = propertiesByName().get(name);
304: if (descriptor == null) {
305: throw new IllegalArgumentException("Unknown property: "
306: + name);
307: }
308: return descriptor;
309: }
310:
311: private void propertyGetError(PropertyDescriptor descriptor,
312: boolean requestedSingleValue) {
313:
314: if (requestedSingleValue) {
315: throw new RuntimeException(
316: "Cannot retrieve a single value from a multi-value property field");
317: }
318: throw new RuntimeException(
319: "Cannot retrieve multiple values from a single-value property field");
320: }
321:
322: private void propertySetError(PropertyDescriptor descriptor,
323: boolean setSingleValue) {
324:
325: if (setSingleValue) {
326: throw new RuntimeException(
327: "Cannot set a single value within a multi-value property field");
328: }
329: throw new RuntimeException(
330: "Cannot set multiple values within a single-value property field");
331: }
332:
333: public void setUsesDFA() {
334: this .usesDFA = true;
335: }
336:
337: public boolean usesDFA() {
338: return this .usesDFA;
339: }
340:
341: public void setUsesTypeResolution() {
342: this .usesTypeResolution = true;
343: }
344:
345: public boolean usesTypeResolution() {
346: return this .usesTypeResolution;
347: }
348:
349: public boolean usesRuleChain() {
350: return !getRuleChainVisits().isEmpty();
351: }
352:
353: public List<String> getRuleChainVisits() {
354: return ruleChainVisits;
355: }
356:
357: public void addRuleChainVisit(String astNodeName) {
358: if (!ruleChainVisits.contains(astNodeName)) {
359: ruleChainVisits.add(astNodeName);
360: }
361: }
362:
363: public void start(RuleContext ctx) {
364: // Override as needed
365: }
366:
367: public void end(RuleContext ctx) {
368: // Override as needed
369: }
370:
371: /**
372: * Rules are equal if:
373: * <ol>
374: * <li>They have the same implementation class.</li>
375: * <li>They have the same name.</li>
376: * <li>They have the same priority.</li>
377: * <li>They share the same properties.</li>
378: * </ol>
379: */
380: @Override
381: public boolean equals(Object o) {
382: if (o == null) {
383: return false; // trivial
384: }
385:
386: if (this == o) {
387: return true; // trivial
388: }
389:
390: boolean equality = this .getClass().getName().equals(
391: o.getClass().getName());
392:
393: if (equality) {
394: Rule that = (Rule) o;
395: equality = this .getName().equals(that.getName())
396: && this .getPriority() == that.getPriority()
397: && this .getProperties()
398: .equals(that.getProperties());
399: }
400:
401: return equality;
402: }
403:
404: /**
405: * @see #equals(Object)
406: */
407: @Override
408: public int hashCode() {
409: return this .getClass().getName().hashCode()
410: + (this .getName() != null ? this .getName().hashCode()
411: : 0)
412: + this .getPriority()
413: + (this .getProperties() != null ? this .getProperties()
414: .hashCode() : 0);
415: }
416:
417: protected static Map<String, PropertyDescriptor> asFixedMap(
418: PropertyDescriptor[] descriptors) {
419: Map<String, PropertyDescriptor> descriptorsByName = new HashMap<String, PropertyDescriptor>(
420: descriptors.length);
421: for (PropertyDescriptor descriptor : descriptors) {
422: descriptorsByName.put(descriptor.name(), descriptor);
423: }
424: return Collections.unmodifiableMap(descriptorsByName);
425: }
426:
427: protected static Map<String, PropertyDescriptor> asFixedMap(
428: PropertyDescriptor descriptor) {
429: return asFixedMap(new PropertyDescriptor[] { descriptor });
430: }
431: }
|