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 }