001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry.enhance; 016 017 import java.lang.reflect.Method; 018 import java.lang.reflect.Modifier; 019 import java.util.HashMap; 020 import java.util.HashSet; 021 import java.util.Iterator; 022 import java.util.List; 023 import java.util.Map; 024 import java.util.Set; 025 026 import org.apache.hivemind.ErrorLog; 027 import org.apache.hivemind.Location; 028 import org.apache.tapestry.spec.IComponentSpecification; 029 030 /** 031 * Validates that an enhanced class is correct; checks that all inherited 032 * abstract methods are, in fact, implemented in the class. 033 * 034 * @author Howard M. Lewis Ship 035 * @since 4.0 036 */ 037 public class EnhancedClassValidatorImpl implements EnhancedClassValidator 038 { 039 040 private ErrorLog _errorLog; 041 042 private ClassInspector _inspector; 043 044 public void validate(Class baseClass, Class enhancedClass, IComponentSpecification specification) 045 { 046 // Set of MethodSignatures for methods that have a non-abstract 047 // implementation 048 // The Set is built working from the deepest subclass up to (and 049 // including) java.lang.Object 050 051 Set implementedMethods = new HashSet(); 052 // Key is MethodSignature, value is Method 053 // Tracks which methods come from interfaces 054 Map interfaceMethods = new HashMap(); 055 056 Location location = specification.getLocation(); 057 058 Class current = enhancedClass; 059 060 while(true) 061 { 062 addInterfaceMethods(current, interfaceMethods); 063 064 // Inside Eclipse, for abstract classes, getDeclaredMethods() does 065 // NOT report methods 066 // inherited from interfaces. For Sun JDK and abstract classes, 067 // getDeclaredMethods() 068 // DOES report interface methods 069 // (as if they were declared by the class itself). This code is 070 // needlessly complex so 071 // that the checks work in both 072 // situations. Basically, I think Eclipse is right and Sun JDK is 073 // wrong and we're using 074 // the interfaceMethods map as a filter to ignore methods that Sun 075 // JDK is attributing 076 // to the class. 077 078 Method[] methods = current.getDeclaredMethods(); 079 080 for(int i = 0; i < methods.length; i++) 081 { 082 Method m = methods[i]; 083 084 MethodSignature s = _inspector.getMethodSignature(current, m); 085 086 boolean isAbstract = Modifier.isAbstract(m.getModifiers()); 087 088 if (isAbstract) 089 { 090 if (interfaceMethods.containsKey(s)) 091 continue; 092 093 // If a superclass defines an abstract method that a 094 // subclass implements, then 095 // all's OK. 096 097 if (implementedMethods.contains(s)) 098 continue; 099 100 _errorLog.error(EnhanceMessages.noImplForAbstractMethod(m, current, baseClass, enhancedClass), location, null); 101 } 102 103 implementedMethods.add(s); 104 } 105 106 current = current.getSuperclass(); 107 108 // No need to check Object.class; it is concrete and doesn't 109 // implement any interfaces, 110 // or provide any methods 111 // that might be declared in an interface. 112 113 if (current == null || current == Object.class) 114 break; 115 } 116 117 Iterator i = interfaceMethods.entrySet().iterator(); 118 while(i.hasNext()) 119 { 120 Map.Entry entry = (Map.Entry) i.next(); 121 122 MethodSignature sig = (MethodSignature) entry.getKey(); 123 124 if (implementedMethods.contains(sig)) 125 continue; 126 127 Method method = (Method) entry.getValue(); 128 129 _errorLog.error(EnhanceMessages.unimplementedInterfaceMethod(method, baseClass, enhancedClass), location, null); 130 } 131 132 } 133 134 private void addInterfaceMethods(Class current, Map interfaceMethods) 135 { 136 Class[] interfaces = current.getInterfaces(); 137 138 for(int i = 0; i < interfaces.length; i++) 139 addMethodsFromInterface(interfaces[i], interfaceMethods); 140 } 141 142 private void addMethodsFromInterface(Class interfaceClass, Map interfaceMethods) 143 { 144 Method[] methods = interfaceClass.getMethods(); 145 146 for(int i = 0; i < methods.length; i++) 147 { 148 MethodSignature sig = _inspector.getMethodSignature(interfaceClass, methods[i]); 149 150 if (interfaceMethods.containsKey(sig)) 151 continue; 152 153 interfaceMethods.put(sig, methods[i]); 154 } 155 } 156 157 public void setErrorLog(ErrorLog errorLog) 158 { 159 _errorLog = errorLog; 160 } 161 162 public void setClassInspector(ClassInspector inspector) 163 { 164 _inspector = inspector; 165 } 166 167 public void setClassInspectors(List inspectors) 168 { 169 _inspector = (ClassInspector)inspectors.get(0); 170 } 171 }