001 package net.sourceforge.pmd.lang.rule.properties;
002
003 import java.util.HashSet;
004 import java.util.Map;
005 import java.util.Set;
006
007 /**
008 * Concrete subclasses manage items that reside within namespaces per the design of the Java language.
009 * Rule developers can limit the range of permissible items by specifying portions of their package
010 * names in the constructor. If the legalPackageNames value is set to null then no restrictions are
011 * made.
012 *
013 * @author Brian Remedios
014 */
015 public abstract class AbstractPackagedProperty<T> extends AbstractProperty<T> {
016
017 private String[] legalPackageNames;
018
019 private static final char PACKAGE_NAME_DELIMITER = ' ';
020
021 /**
022 *
023 * @param theName
024 * @param theDescription
025 * @param theDefault
026 * @param theLegalPackageNames
027 * @param theUIOrder
028 * @throws IllegalArgumentException
029 */
030 protected AbstractPackagedProperty(String theName, String theDescription, T theDefault, String[] theLegalPackageNames, float theUIOrder) {
031 super(theName, theDescription, theDefault, theUIOrder);
032
033 checkValidPackages(theDefault, theLegalPackageNames);
034
035 legalPackageNames = theLegalPackageNames;
036 }
037
038 /**
039 * @param attributes Map<String,String>
040 */
041 protected void addAttributesTo(Map<String, String> attributes) {
042 super.addAttributesTo(attributes);
043
044 attributes.put("legalPackageNames", delimitedPackageNames());
045 }
046
047 /**
048 * @return String
049 */
050 private final String delimitedPackageNames() {
051
052 if (legalPackageNames == null || legalPackageNames.length == 0) { return ""; }
053 if (legalPackageNames.length == 1) { return legalPackageNames[0]; }
054
055 StringBuilder sb = new StringBuilder();
056 sb.append(legalPackageNames[0]);
057 for (int i=1; i<legalPackageNames.length; i++) {
058 sb.append(PACKAGE_NAME_DELIMITER).append(legalPackageNames[i]);
059 }
060 return sb.toString();
061 }
062
063 /**
064 * Evaluates the names of the items against the allowable name prefixes. If one or more
065 * do not have valid prefixes then an exception will be thrown.
066 *
067 * @param item
068 * @param legalNamePrefixes
069 * @throws IllegalArgumentException
070 */
071 private void checkValidPackages(Object item, String[] legalNamePrefixes) {
072 Object[] items;
073 if (item.getClass().isArray()) {
074 items = (Object[])item;
075 } else{
076 items = new Object[]{item};
077 }
078
079 String[] names = new String[items.length];
080 Set<String> nameSet = new HashSet<String>(items.length);
081 String name = null;
082
083 for (int i=0; i<items.length; i++) {
084 name = packageNameOf(items[i]);
085 names[i] = name;
086 nameSet.add(name);
087 }
088
089 for (int i=0; i<names.length; i++) {
090 for (int l=0; l<legalNamePrefixes.length; l++) {
091 if (names[i].startsWith(legalNamePrefixes[l])) {
092 nameSet.remove(names[i]);
093 break;
094 }
095 }
096 }
097 if (nameSet.isEmpty()) { return; }
098
099 throw new IllegalArgumentException("Invalid items: " + nameSet);
100 }
101
102 /**
103 * Method itemTypeName.
104 * @return String
105 */
106 abstract protected String itemTypeName();
107
108 /**
109 *
110 * @param value Object
111 * @return String
112 */
113 protected String valueErrorFor(Object value) {
114
115 if (value == null) {
116 String err = super.valueErrorFor(null);
117 if (err != null) { return err; }
118 }
119
120 if (legalPackageNames == null) {
121 return null; // no restriction
122 }
123
124 String name = packageNameOf(value);
125
126 for (int i=0; i<legalPackageNames.length; i++) {
127 if (name.startsWith(legalPackageNames[i])) {
128 return null;
129 }
130 }
131
132 return "Disallowed " + itemTypeName() + ": " + name;
133 }
134
135 /**
136 *
137 * @param item Object
138 * @return String
139 */
140 abstract protected String packageNameOf(Object item);
141
142 /**
143 *
144 * @return String[]
145 */
146 public String[] legalPackageNames() {
147 return legalPackageNames;
148 }
149
150 }
|