/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.rule.properties; import static net.sourceforge.pmd.PropertyDescriptorFields.LEGAL_PACKAGES; import java.util.HashSet; import java.util.Map; import java.util.Set; import net.sourceforge.pmd.lang.rule.properties.factories.BasicPropertyDescriptorFactory; /** * Concrete subclasses manage items that reside within namespaces per the design * of the Java language. Rule developers can limit the range of permissible * items by specifying portions of their package names in the constructor. If * the legalPackageNames value is set to null then no restrictions are made. * * @author Brian Remedios * @param <T> */ public abstract class AbstractPackagedProperty<T> extends AbstractProperty<T> { private String[] legalPackageNames; private static final char PACKAGE_NAME_DELIMITER = ' '; protected static final Map<String, Boolean> PACKAGED_FIELD_TYPES_BY_KEY = BasicPropertyDescriptorFactory .expectedFieldTypesWith(new String[] { LEGAL_PACKAGES }, new Boolean[] { Boolean.FALSE }); /** * * @param theName * @param theDescription * @param theDefault * @param theLegalPackageNames * @param theUIOrder * @throws IllegalArgumentException */ protected AbstractPackagedProperty(String theName, String theDescription, T theDefault, String[] theLegalPackageNames, float theUIOrder) { super(theName, theDescription, theDefault, theUIOrder); checkValidPackages(theDefault, theLegalPackageNames); legalPackageNames = theLegalPackageNames; } protected static String[] packageNamesIn(Map<String, String> params) { // TODO return null; } @Override protected void addAttributesTo(Map<String, String> attributes) { super.addAttributesTo(attributes); attributes.put(LEGAL_PACKAGES, delimitedPackageNames()); } /** * @return String */ private String delimitedPackageNames() { if (legalPackageNames == null || legalPackageNames.length == 0) { return ""; } if (legalPackageNames.length == 1) { return legalPackageNames[0]; } StringBuilder sb = new StringBuilder(); sb.append(legalPackageNames[0]); for (int i = 1; i < legalPackageNames.length; i++) { sb.append(PACKAGE_NAME_DELIMITER).append(legalPackageNames[i]); } return sb.toString(); } /** * Evaluates the names of the items against the allowable name prefixes. If * one or more do not have valid prefixes then an exception will be thrown. * * @param item * @param legalNamePrefixes * @throws IllegalArgumentException */ private void checkValidPackages(Object item, String[] legalNamePrefixes) { Object[] items = new Object[0]; if (item != null) { if (item.getClass().isArray()) { items = (Object[]) item; } else { items = new Object[] { item }; } } String[] names = new String[items.length]; Set<String> nameSet = new HashSet<>(items.length); String name = null; for (int i = 0; i < items.length; i++) { name = packageNameOf(items[i]); names[i] = name; nameSet.add(name); } for (int i = 0; i < names.length; i++) { for (int l = 0; l < legalNamePrefixes.length; l++) { if (names[i].startsWith(legalNamePrefixes[l])) { nameSet.remove(names[i]); break; } } } if (nameSet.isEmpty()) { return; } throw new IllegalArgumentException("Invalid items: " + nameSet); } /** * Method itemTypeName. * * @return String */ protected abstract String itemTypeName(); /** * * @param value * Object * @return String */ @Override protected String valueErrorFor(Object value) { if (value == null) { String err = super.valueErrorFor(null); if (err != null) { return err; } } if (legalPackageNames == null) { return null; // no restriction } String name = packageNameOf(value); for (int i = 0; i < legalPackageNames.length; i++) { if (name.startsWith(legalPackageNames[i])) { return null; } } return "Disallowed " + itemTypeName() + ": " + name; } /** * * @param item * Object * @return String */ protected abstract String packageNameOf(Object item); /** * * @return String[] */ public String[] legalPackageNames() { return legalPackageNames; } }