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