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 }