View Javadoc

    1   /********************************************************************************
    2    * Copyright (c) 2004 IBM Corporation and others.
    3    * All rights reserved.   This program and the accompanying materials
    4    * are made available under the terms of the Common Public License v1.0
    5    * which accompanies this distribution, and is available at
    6    * http://www.eclipse.org/legal/cpl-v10.html
    7    *
    8    * Contributors:
    9    * IBM - Initial API and implementation
    10   ******************************************************************************/
    11
    12
    13  package org.codehaus.groovy.classgen;
    14
    15  import java.lang.reflect.Modifier;
    16  import java.util.Iterator;
    17  import java.util.List;
    18
    19  import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
    20  import org.codehaus.groovy.ast.ClassNode;
    21  import org.codehaus.groovy.ast.FieldNode;
    22  import org.codehaus.groovy.ast.MethodNode;
    23  import org.codehaus.groovy.ast.Parameter;
    24  import org.codehaus.groovy.ast.expr.BinaryExpression;
    25  import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
    26  import org.codehaus.groovy.ast.expr.MapEntryExpression;
    27  import org.codehaus.groovy.control.SourceUnit;
    28  import org.objectweb.asm.Opcodes;
    29  import org.codehaus.groovy.syntax.Types;
    30
    31
    32  /***
    33   * ClassCompletionVerifier
    34   *
    35   */
    36  public class ClassCompletionVerifier extends ClassCodeVisitorSupport {
    37
    38      private ClassNode currentClass;
    39      private SourceUnit source;
    40
    41      public ClassCompletionVerifier(SourceUnit source) {
    42          this.source = source;
    43      }
    44
    45      public ClassNode getClassNode() {
    46          return currentClass;
    47      }
    48
    49      public void visitClass(ClassNode node) {
    50          ClassNode oldClass = currentClass;
    51          currentClass = node;
    52
    53          checkImplementsAndExtends(node);
    54          if (source!=null && !source.getErrorCollector().hasErrors()) {
    55              checkClassForOverwritingFinal(node);
    56              checkMethodsForOverwritingFinal(node);
    57              checkNoAbstractMethodsNonabstractClass(node);
    58          }
    59
    60          super.visitClass(node);
    61
    62          currentClass = oldClass;
    63      }
    64
    65      private void checkNoAbstractMethodsNonabstractClass(ClassNode node) {
    66          if (Modifier.isAbstract(node.getModifiers())) return;
    67          List abstractMethods = node.getAbstractMethods();
    68          if (abstractMethods==null) return;
    69          for (Iterator iter = abstractMethods.iterator(); iter.hasNext();) {
    70              MethodNode method = (MethodNode) iter.next();
    71              String methodName = method.getTypeDescriptor();
    72              addError("Can't have an abstract method in a non abstract class."+
    73                       " The class '"+node.getName()+"' must be declared abstract or"+
    74                       " the method '"+methodName+"' must be implemented.",node);
    75          }
    76      }
    77
    78      private void checkAbstractDeclaration(MethodNode methodNode) {
    79          if (!Modifier.isAbstract(methodNode.getModifiers())) return;
    80          if (Modifier.isAbstract(currentClass.getModifiers())) return;
    81          addError("Can't have an abstract method in a non abstract class." +
    82                   " The class '" + currentClass.getName() +  "' must be declared abstract or the method '" +
    83                   methodNode.getTypeDescriptor() + "' must not be abstract.",methodNode);
    84      }
    85
    86      private void checkClassForOverwritingFinal(ClassNode cn) {
    87          ClassNode superCN = cn.getSuperClass();
    88          if (superCN==null) return;
    89          if (!Modifier.isFinal(superCN.getModifiers())) return;
    90          StringBuffer msg = new StringBuffer();
    91          msg.append("you are not allowed to overwrite the final class ");
    92          msg.append(superCN.getName());
    93          msg.append(".");
    94          addError(msg.toString(),cn);
    95      }
    96
    97      private void checkImplementsAndExtends(ClassNode node) {
    98          ClassNode cn = node.getSuperClass();
    99          if (cn.isInterface() && !node.isInterface()) addError("you are not allowed to extend the Interface "+cn.getName()+", use implements instead", node);
    100         ClassNode[] interfaces = node.getInterfaces();
    101         for (int i = 0; i < interfaces.length; i++) {
    102             cn = interfaces[i];
    103             if (!cn.isInterface()) addError ("you are not allowed to implement the Class "+cn.getName()+", use extends instead", node);
    104         }
    105     }
    106
    107     private void checkMethodsForOverwritingFinal(ClassNode cn) {
    108         List l = cn.getMethods();
    109         for (Iterator cnIter = l.iterator(); cnIter.hasNext();) {
    110             MethodNode method =(MethodNode) cnIter.next();
    111             Parameter[] parameters = method.getParameters();
    112             for (ClassNode superCN = cn.getSuperClass(); superCN!=null; superCN=superCN.getSuperClass()){
    113                 List methods = superCN.getMethods(method.getName());
    114                 for (Iterator iter = methods.iterator(); iter.hasNext();) {
    115                     MethodNode m = (MethodNode) iter.next();
    116                     Parameter[] np = m.getParameters();
    117                     if (!hasEqualParameterTypes(parameters,np)) continue;
    118                     if (!Modifier.isFinal(m.getModifiers())) return;
    119
    120                     StringBuffer msg = new StringBuffer();
    121                     msg.append("you are not allowed to overwrite the final method ").append(method.getName());
    122                     msg.append("(");
    123                     boolean semi = false;
    124                     for (int i=0; i<parameters.length;i++) {
    125                         if (semi) {
    126                             msg.append(",");
    127                         } else {
    128                             semi = true;
    129                         }
    130                         msg.append(parameters[i].getType());
    131                     }
    132                     msg.append(")");
    133                     msg.append(" from class ").append(superCN.getName());
    134                     msg.append(".");
    135                     addError(msg.toString(),method);
    136                     return;
    137                 }
    138             }
    139         }
    140     }
    141
    142     private boolean hasEqualParameterTypes(Parameter[] first, Parameter[] second) {
    143         if (first.length!=second.length) return false;
    144         for (int i=0; i<first.length; i++) {
    145             String ft = first[i].getType().getName();
    146             String st = second[i].getType().getName();
    147             if (ft.equals(st)) continue;
    148             return false;
    149         }
    150         return true;
    151     }
    152
    153     protected SourceUnit getSourceUnit() {
    154         return source;
    155     }
    156
    157     public void visitConstructorCallExpression(ConstructorCallExpression call) {
    158         ClassNode type = call.getType();
    159         if (Modifier.isAbstract(type.getModifiers())) {
    160             addError("cannot create an instance from the abstract class "+type.getName(),call);
    161         }
    162         super.visitConstructorCallExpression(call);
    163     }
    164
    165     public void visitMethod(MethodNode node) {
    166         checkAbstractDeclaration(node);
    167         checkRepetitiveMethod(node);
    168         super.visitMethod(node);
    169     }
    170
    171     private void checkRepetitiveMethod(MethodNode node) {
    172         if (node.getName().equals("<clinit>")) return;
    173         List methods = currentClass.getMethods(node.getName());
    174         for (Iterator iter = methods.iterator(); iter.hasNext();) {
    175             MethodNode element = (MethodNode) iter.next();
    176             if (element==node) continue;
    177             if (!element.getDeclaringClass().equals(node.getDeclaringClass())) continue;
    178             Parameter[] p1 = node.getParameters();
    179             Parameter[] p2 = element.getParameters();
    180             if (p1.length!=p2.length) continue;
    181             boolean isEqual=true;
    182             for (int i = 0; i < p2.length; i++) {
    183                 isEqual &= p1[i].equals(p2[i]);
    184             }
    185             isEqual &= node.getReturnType().equals(element.getReturnType());
    186             if (isEqual) {
    187                 addError("Repetitive method name/signature in class "+currentClass.getName(),node);
    188             }
    189         }
    190     }
    191
    192     public void visitField(FieldNode node) {
    193     	if (currentClass.getField(node.getName())!=node) {
    194     		addError("The field "+node.getName()+" is declared multiple times.", node);
    195     	}
    196     	checkInterfaceFieldModifiers(node);
    197     	super.visitField(node);
    198     }
    199
    200     private void checkInterfaceFieldModifiers(FieldNode node) {
    201     	if (!currentClass.isInterface()) return;
    202     	if ((node.getModifiers() & (Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL))==0) {
    203     		addError("The field "+node.getName()+" is not 'public final static' but part of the interface "+currentClass.getName()+".", node);
    204     	}
    205     }
    206
    207     public void visitBinaryExpression(BinaryExpression expression) {
    208         if (expression.getOperation().getType()==Types.LEFT_SQUARE_BRACKET &&
    209             expression.getRightExpression() instanceof MapEntryExpression){
    210             addError("You tried to use a map entry for an index operation, this is not "+
    211                      "allowed. Maybe something should be set in parentheses or a comma is missing?",
    212                      expression.getRightExpression());
    213         }
    214         super.visitBinaryExpression(expression);
    215     }
    216
    217 }