View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    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   * @author Brian Remedios
    16   * @param <T>
    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  	 * Constructor for AbstractPMDProperty.
    30  	 * @param theName String
    31  	 * @param theDescription String
    32  	 * @param theDefault Object
    33  	 * @param theUIOrder float
    34  	 * @throws IllegalArgumentException
    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;	// TODO - do we need this?
    41  		uiOrder = checkPositive(theUIOrder, "UI order");
    42  	}
    43
    44  	/**
    45  	 * @param arg String
    46  	 * @param argId String
    47  	 * @return String
    48  	 * @throws IllegalArgumentException
    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  	 * @param arg float
    61  	 * @param argId String
    62  	 * @return float
    63  	 * @throws IllegalArgumentException
    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           * {@inheritDoc}
    74  	 */
    75  	public char multiValueDelimiter() {
    76  		return DELIMITER;
    77  	}
    78
    79  	/**
    80           * {@inheritDoc}
    81  	 */
    82  	public String name() {
    83  		return name;
    84  	}
    85
    86  	/**
    87           * {@inheritDoc}
    88  	 */
    89  	public String description() {
    90  		return description;
    91  	}
    92
    93  	/**
    94           * {@inheritDoc}
    95  	 */
    96  	public T defaultValue() {
    97  		return defaultValue;
    98  	}
    99
    100 	/**
    101 	 * Method defaultHasNullValue.
    102 	 * @return boolean
    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          * {@inheritDoc}
    122 	 */
    123 	public boolean isMultiValue() {
    124 		return false;
    125 	}
    126
    127 	/**
    128          * {@inheritDoc}
    129 	 */
    130 	public boolean isRequired() {
    131 		return isRequired;
    132 	}
    133
    134 	/**
    135          * {@inheritDoc}
    136 	 */
    137 	public float uiOrder() {
    138 		return uiOrder;
    139 	}
    140
    141 	/**
    142 	 * Return the value as a string that can be easily recognized and parsed
    143 	 * when we see it again.
    144 	 *
    145 	 * @param value Object
    146 	 * @return String
    147 	 */
    148 	protected String asString(Object value) {
    149 		return value == null ? "" : value.toString();
    150 	}
    151
    152 	/**
    153          * {@inheritDoc}
    154 	 */
    155 	public String asDelimitedString(T values) {
    156 	    return asDelimitedString(values, multiValueDelimiter());
    157 	}
    158
    159 	/**
    160 	 * Return the specified values as a single string using the delimiter.
    161 	 * @param values Object
    162 	 * @param delimiter char
    163 	 * @return String
    164 	 * @see net.sourceforge.pmd.PropertyDescriptor#asDelimitedString(Object)
    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          * {@inheritDoc}
    194 	 */
    195 	public int compareTo(PropertyDescriptor<?> otherProperty) {
    196 		float otherOrder = otherProperty.uiOrder();
    197 		return (int) (otherOrder - uiOrder);
    198 	}
    199
    200 	/**
    201          * {@inheritDoc}
    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 	 * @param value Object
    216 	 * @return String
    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 	 * @param value Object
    231 	 * @return String
    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 	 * @param value Object
    252 	 * @return boolean
    253 	 */
    254 	protected static boolean isArray(Object value) {
    255 		return value != null && value.getClass().getComponentType() != null;
    256 	}
    257
    258 	/**
    259 	 * @param value Object
    260 	 * @return String
    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          * {@inheritDoc}
    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          * {@inheritDoc}
    300 	 */
    301 	public Object[][] choices() {
    302 		return null;
    303 	}
    304
    305 	/**
    306          * {@inheritDoc}
    307 	 */
    308 	public int preferredRowCount() {
    309 		return 1;
    310 	}
    311
    312 	/**
    313          * {@inheritDoc}
    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          * {@inheritDoc}
    331 	 */
    332 	@Override
    333 	public int hashCode() {
    334 	    return name.hashCode();
    335 	}
    336
    337 	/**
    338          * {@inheritDoc}
    339 	 */
    340 	@Override
    341 	public String toString() {
    342 	    return "[PropertyDescriptor: name=" + name() + ", type=" + type() + ", value=" + defaultValue() + "]";
    343 	}
    344
    345 	/**
    346 	 * @return String
    347 	 */
    348 	protected abstract String defaultAsString();
    349
    350 	/**
    351 	 * @param value Object
    352 	 * @param otherValue Object
    353 	 * @return boolean
    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 	 * @return Map<String,String>
    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 	 * @param attributes Map<String,String>
    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 }