AbstractPackagedProperty.java
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 }