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 static net.sourceforge.pmd.PropertyDescriptorFields.LEGAL_PACKAGES;
7   
8   import java.util.HashSet;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import net.sourceforge.pmd.lang.rule.properties.factories.BasicPropertyDescriptorFactory;
13  
14  /**
15   * Concrete subclasses manage items that reside within namespaces per the design
16   * of the Java language. Rule developers can limit the range of permissible
17   * items by specifying portions of their package names in the constructor. If
18   * the legalPackageNames value is set to null then no restrictions are made.
19   * 
20   * @author Brian Remedios
21   * @param <T>
22   */
23  public abstract class AbstractPackagedProperty<T> extends AbstractProperty<T> {
24  
25      private String[] legalPackageNames;
26  
27      private static final char PACKAGE_NAME_DELIMITER = ' ';
28  
29      protected static final Map<String, Boolean> PACKAGED_FIELD_TYPES_BY_KEY = BasicPropertyDescriptorFactory
30              .expectedFieldTypesWith(new String[] { LEGAL_PACKAGES }, new Boolean[] { Boolean.FALSE });
31  
32      protected static String[] packageNamesIn(Map<String, String> params) {
33          // TODO
34          return null;
35      }
36  
37      /**
38       * 
39       * @param theName
40       * @param theDescription
41       * @param theDefault
42       * @param theLegalPackageNames
43       * @param theUIOrder
44       * @throws IllegalArgumentException
45       */
46      protected AbstractPackagedProperty(String theName, String theDescription, T theDefault,
47              String[] theLegalPackageNames, float theUIOrder) {
48          super(theName, theDescription, theDefault, theUIOrder);
49  
50          checkValidPackages(theDefault, theLegalPackageNames);
51  
52          legalPackageNames = theLegalPackageNames;
53      }
54  
55      /**
56       * @param attributes Map<String,String>
57       */
58      protected void addAttributesTo(Map<String, String> attributes) {
59          super.addAttributesTo(attributes);
60  
61          attributes.put(LEGAL_PACKAGES, delimitedPackageNames());
62      }
63  
64      /**
65       * @return String
66       */
67      private final String delimitedPackageNames() {
68  
69          if (legalPackageNames == null || legalPackageNames.length == 0) {
70              return "";
71          }
72          if (legalPackageNames.length == 1) {
73              return legalPackageNames[0];
74          }
75  
76          StringBuilder sb = new StringBuilder();
77          sb.append(legalPackageNames[0]);
78          for (int i = 1; i < legalPackageNames.length; i++) {
79              sb.append(PACKAGE_NAME_DELIMITER).append(legalPackageNames[i]);
80          }
81          return sb.toString();
82      }
83  
84      /**
85       * Evaluates the names of the items against the allowable name prefixes. If
86       * one or more do not have valid prefixes then an exception will be thrown.
87       * 
88       * @param item
89       * @param legalNamePrefixes
90       * @throws IllegalArgumentException
91       */
92      private void checkValidPackages(Object item, String[] legalNamePrefixes) {
93          Object[] items = new Object[0];
94  
95          if (item != null) {
96              if (item.getClass().isArray()) {
97                  items = (Object[]) item;
98              } else {
99                  items = new Object[] { item };
100             }
101         }
102 
103         String[] names = new String[items.length];
104         Set<String> nameSet = new HashSet<>(items.length);
105         String name = null;
106 
107         for (int i = 0; i < items.length; i++) {
108             name = packageNameOf(items[i]);
109             names[i] = name;
110             nameSet.add(name);
111         }
112 
113         for (int i = 0; i < names.length; i++) {
114             for (int l = 0; l < legalNamePrefixes.length; l++) {
115                 if (names[i].startsWith(legalNamePrefixes[l])) {
116                     nameSet.remove(names[i]);
117                     break;
118                 }
119             }
120         }
121         if (nameSet.isEmpty()) {
122             return;
123         }
124 
125         throw new IllegalArgumentException("Invalid items: " + nameSet);
126     }
127 
128     /**
129      * Method itemTypeName.
130      * 
131      * @return String
132      */
133     abstract protected String itemTypeName();
134 
135     /**
136      *
137      * @param value Object
138      * @return String
139      */
140     protected String valueErrorFor(Object value) {
141 
142         if (value == null) {
143             String err = super.valueErrorFor(null);
144             if (err != null) {
145                 return err;
146             }
147         }
148 
149         if (legalPackageNames == null) {
150             return null; // no restriction
151         }
152 
153         String name = packageNameOf(value);
154 
155         for (int i = 0; i < legalPackageNames.length; i++) {
156             if (name.startsWith(legalPackageNames[i])) {
157                 return null;
158             }
159         }
160 
161         return "Disallowed " + itemTypeName() + ": " + name;
162     }
163 
164     /**
165      *
166      * @param item Object
167      * @return String
168      */
169     abstract protected String packageNameOf(Object item);
170 
171     /**
172      *
173      * @return String[]
174      */
175     public String[] legalPackageNames() {
176         return legalPackageNames;
177     }
178 
179 }