View Javadoc

    1   /***
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.rules;
    5
    6   import net.sourceforge.pmd.AbstractRule;
    7   import net.sourceforge.pmd.ast.ASTAllocationExpression;
    8   import net.sourceforge.pmd.ast.ASTArguments;
    9   import net.sourceforge.pmd.ast.ASTArrayDimsAndInits;
    10  import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
    11  import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
    12  import net.sourceforge.pmd.ast.ASTCompilationUnit;
    13  import net.sourceforge.pmd.ast.ASTConstructorDeclaration;
    14  import net.sourceforge.pmd.ast.ASTEnumDeclaration;
    15
    16  import java.util.ArrayList;
    17  import java.util.Iterator;
    18  import java.util.List;
    19  import java.util.ListIterator;
    20
    21  /***
    22   * 1. Note all private constructors.
    23   * 2. Note all instantiations from outside of the class by way of the private
    24   * constructor.
    25   * 3. Flag instantiations.
    26   * <p/>
    27   * <p/>
    28   * Parameter types can not be matched because they can come as exposed members
    29   * of classes.  In this case we have no way to know what the type is.  We can
    30   * make a best effort though which can filter some?
    31   *
    32   * @author CL Gilbert (dnoyeb@users.sourceforge.net)
    33   * @author David Konecny (david.konecny@)
    34   */
    35  public class AccessorClassGeneration extends AbstractRule {
    36
    37      private List classDataList = new ArrayList();
    38      private int classID = -1;
    39      privateong> String packageName;
    40
    41      public Object visit(ASTEnumDeclaration node, Object data) {
    42          return data;  // just skip Enums
    43      }
    44
    45      public Object visit(ASTCompilationUnit node, Object data) {
    46          classDataList.clear();
    47          packageName = node.getScope().getEnclosingSourceFileScope().getPackageName();
    48          return super.visit(node, data);
    49      }
    50
    51      private static class ClassData {
    52          private String m_ClassName;
    53          private List m_PrivateConstructors;
    54          private List m_Instantiations;
    55          /***
    56           * List of outer class names that exist above this class
    57           */
    58          private List m_ClassQualifyingNames;
    59
    60          public ClassData(String className) {
    61              m_ClassName = className;
    62              m_PrivateConstructors = new ArrayList();
    63              m_Instantiations = new ArrayList();
    64              m_ClassQualifyingNames = new ArrayList();
    65          }
    66
    67          public void addInstantiation(AllocData ad) {
    68              m_Instantiations.add(ad);
    69          }
    70
    71          public Iterator getInstantiationIterator() {
    72              return m_Instantiations.iterator();
    73          }
    74
    75          public void addConstructor(ASTConstructorDeclaration cd) {
    76              m_PrivateConstructors.add(cd);
    77          }
    78
    79          public Iterator getPrivateConstructorIterator() {
    80              return m_PrivateConstructors.iterator();
    81          }
    82
    83          public String getClassName() {
    84              return m_ClassName;
    85          }
    86
    87          public void addClassQualifyingName(String name) {
    88              m_ClassQualifyingNames.add(name);
    89          }
    90
    91          public List getClassQualifyingNamesList() {
    92              return m_ClassQualifyingNames;
    93          }
    94      }
    95
    96      private static class AllocData {
    97          private String m_Name;
    98          private int m_ArgumentCount;
    99          private ASTAllocationExpression m_ASTAllocationExpression;
    100         private boolean isArray;
    101
    102         public AllocData(ASTAllocationExpression node, String aPackageName, List classQualifyingNames) {
    103             if (node.jjtGetChild(1) instanceof ASTArguments) {
    104                 ASTArguments aa = (ASTArguments) node.jjtGetChild(1);
    105                 m_ArgumentCount = aa.getArgumentCount();
    106                 //Get name and strip off all superfluous data
    107                 //strip off package name if it is current package
    108                 if (!(node.jjtGetChild(0) instanceof ASTClassOrInterfaceType)) {
    109                     throw new RuntimeException("BUG: Expected a ASTClassOrInterfaceType, got a " + node.jjtGetChild(0).getClass());
    110                 }
    111                 ASTClassOrInterfaceType an = (ASTClassOrInterfaceType) node.jjtGetChild(0);
    112                 m_Name = stripString(aPackageName + ".", an.getImage());
    113
    114                 //strip off outer class names
    115                 //try OuterClass, then try OuterClass.InnerClass, then try OuterClass.InnerClass.InnerClass2, etc...
    116                 String findName = "";
    117                 for (ListIterator li = classQualifyingNames.listIterator(classQualifyingNames.size()); li.hasPrevious();) {
    118                     String aName = (String) li.previous();
    119                     findName = aName + "." + findName;
    120                     if (m_Name.startsWith(findName)) {
    121                         //strip off name and exit
    122                         m_Name = m_Name.substring(findName.length());
    123                         break;
    124                     }
    125                 }
    126             } else if (node.jjtGetChild(1) instanceof ASTArrayDimsAndInits) {
    127                 //this is incomplete because I dont need it.
    128                 //				child 0 could be primitive or object (ASTName or ASTPrimitiveType)
    129                 isArray = true;
    130             }
    131             m_ASTAllocationExpression = node;
    132         }
    133
    134         public String getName() {
    135             return m_Name;
    136         }
    137
    138         public int getArgumentCount() {
    139             return m_ArgumentCount;
    140         }
    141
    142         public ASTAllocationExpression getASTAllocationExpression() {
    143             return m_ASTAllocationExpression;
    144         }
    145
    146         public boolean isArray() {
    147             return isArray;
    148         }
    149     }
    150
    151     /***
    152      * Outer interface visitation
    153      */
    154     public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
    155         if (node.isInterface()) {
    156             if (!(node.jjtGetParent().jjtGetParent() instanceof ASTCompilationUnit)) {
    157                 // not a top level interface
    158                 String interfaceName = node.getImage();
    159                 int formerID = getClassID();
    160                 setClassID(classDataList.size());
    161                 ClassData newClassData = new ClassData(interfaceName);
    162                 //store the names of any outer classes of this class in the classQualifyingName List
    163                 ClassData formerClassData = (ClassData) classDataList.get(formerID);
    164                 newClassData.addClassQualifyingName(formerClassData.getClassName());
    165                 classDataList.add(getClassID(), newClassData);
    166                 Object o = super.visit(node, data);
    167                 setClassID(formerID);
    168                 return o;
    169             } else {
    170                 String interfaceName = node.getImage();
    171                 classDataList.clear();
    172                 setClassID(0);
    173                 classDataList.add(getClassID(), new ClassData(interfaceName));
    174                 Object o = super.visit(node, data);
    175                 if (o != null) {
    176                     processRule(o);
    177                 } else {
    178                     processRule(data);
    179                 }
    180                 setClassID(-1);
    181                 return o;
    182             }
    183         } else if (!(node.jjtGetParent().jjtGetParent() instanceof ASTCompilationUnit)) {
    184             // not a top level class
    185             String className = node.getImage();
    186             int formerID = getClassID();
    187             setClassID(classDataList.size());
    188             ClassData newClassData = new ClassData(className);
    189             // TODO
    190             // this is a hack to bail out here
    191             // but I'm not sure why this is happening
    192             // TODO
    193             if (formerID == -1 || formerID >= classDataList.size()) {
    194                 return null;
    195             }
    196             //store the names of any outer classes of this class in the classQualifyingName List
    197             ClassData formerClassData = (ClassData) classDataList.get(formerID);
    198             newClassData.addClassQualifyingName(formerClassData.getClassName());
    199             classDataList.add(getClassID(), newClassData);
    200             Object o = super.visit(node, data);
    201             setClassID(formerID);
    202             return o;
    203         }
    204         // outer classes
    205         String className = node.getImage();
    206         classDataList.clear();
    207         setClassID(0);//first class
    208         classDataList.add(getClassID(), new ClassData(className));
    209         Object o = super.visit(node, data);
    210         if (o != null) {
    211             processRule(o);
    212         } else {
    213             processRule(data);
    214         }
    215         setClassID(-1);
    216         return o;
    217     }
    218
    219     /***
    220      * Store all target constructors
    221      */
    222     public Object visit(ASTConstructorDeclaration node, Object data) {
    223         if (node.isPrivate()) {
    224             getCurrentClassData().addConstructor(node);
    225         }
    226         return super.visit(node, data);
    227     }
    228
    229     public Object visit(ASTAllocationExpression node, Object data) {
    230         // TODO
    231         // this is a hack to bail out here
    232         // but I'm not sure why this is happening
    233         // TODO
    234         if (classID == -1 || getCurrentClassData() == null) {
    235             return data;
    236         }
    237         AllocData ad = new AllocData(node, packageName, getCurrentClassData().getClassQualifyingNamesList());
    238         if (!ad.isArray()) {
    239             getCurrentClassData().addInstantiation(ad);
    240         }
    241         return super.visit(node, data);
    242     }
    243
    244     private void processRule(Object ctx) {
    245         //check constructors of outerIterator against allocations of innerIterator
    246         for (Iterator outerIterator = classDataList.iterator(); outerIterator.hasNext();) {
    247             ClassData outerDataSet = (ClassData) outerIterator.next();
    248             for (Iterator constructors = outerDataSet.getPrivateConstructorIterator(); constructors.hasNext();) {
    249                 ASTConstructorDeclaration cd = (ASTConstructorDeclaration) constructors.next();
    250
    251                 for (Iterator innerIterator = classDataList.iterator(); innerIterator.hasNext();) {
    252                     ClassData innerDataSet = (ClassData) innerIterator.next();
    253                     if (outerDataSet == innerDataSet) {
    254                         continue;
    255                     }
    256                     for (Iterator allocations = innerDataSet.getInstantiationIterator(); allocations.hasNext();) {
    257                         AllocData ad = (AllocData) allocations.next();
    258                         //if the constructor matches the instantiation
    259                         //flag the instantiation as a generator of an extra class
    260                         if (outerDataSet.getClassName().equals(ad.getName()) && (cd.getParameterCount() == ad.getArgumentCount())) {
    261                             addViolation(ctx, ad.getASTAllocationExpression());
    262                         }
    263                     }
    264                 }
    265             }
    266         }
    267     }
    268
    269     private ClassData getCurrentClassData() {
    270         // TODO
    271         // this is a hack to bail out here
    272         // but I'm not sure why this is happening
    273         // TODO
    274         if (classID >= classDataList.size()) {
    275             return null;
    276         }
    277         return (ClassData) classDataList.get(classID);
    278     }
    279
    280     private void setClassID(int ID) {
    281         classID = ID;
    282     }
    283
    284     private int getClassID() {
    285         return classID;
    286     }
    287
    288     //remove = Fire.
    289     //value = someFire.Fighter
    290     //        0123456789012345
    291     //index = 4
    292     //remove.size() = 5
    293     //value.substring(0,4) = some
    294     //value.substring(4 + remove.size()) = Fighter
    295     //return "someFighter"
    296     private static String stripString(String remove, String value) {
    297         String returnValue;
    298         int index = value.indexOf(remove);
    299         if (index != -1) {	//if the package name can start anywhere but 0 please inform the author because this will break
    300             returnValue = value.substring(0, index) + value.substring(index + remove.length());
    301         } else {
    302             returnValue = value;
    303         }
    304         return returnValue;
    305     }
    306
    307 }