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.Map; 023 import java.util.Set; 024 025 import org.apache.hivemind.ErrorLog; 026 import org.apache.hivemind.Location; 027 import org.apache.hivemind.service.MethodSignature; 028 import org.apache.tapestry.spec.IComponentSpecification; 029 030 /** 031 * Validates that an enhanced class is correct; checks that all inherited abstract methods are, in 032 * 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 private ErrorLog _errorLog; 040 041 public void validate(Class baseClass, Class enhancedClass, IComponentSpecification specification) 042 { 043 // Set of MethodSignatures for methods that have a non-abstract implementation 044 // The Set is built working from the deepest subclass up to (and including) java.lang.Object 045 046 Set implementedMethods = new HashSet(); 047 // Key is MethodSignature, value is Method 048 // Tracks which methods come from interfaces 049 Map interfaceMethods = new HashMap(); 050 051 Location location = specification.getLocation(); 052 053 Class current = enhancedClass; 054 055 while (true) 056 { 057 addInterfaceMethods(current, interfaceMethods); 058 059 // Inside Eclipse, for abstract classes, getDeclaredMethods() does NOT report methods 060 // inherited from interfaces. For Sun JDK and abstract classes, getDeclaredMethods() 061 // DOES report interface methods 062 // (as if they were declared by the class itself). This code is needlessly complex so 063 // that the checks work in both 064 // situations. Basically, I think Eclipse is right and Sun JDK is wrong and we're using 065 // the interfaceMethods map as a filter to ignore methods that Sun JDK is attributing 066 // to the class. 067 068 Method[] methods = current.getDeclaredMethods(); 069 070 for (int i = 0; i < methods.length; i++) 071 { 072 Method m = methods[i]; 073 074 MethodSignature s = new MethodSignature(m); 075 076 boolean isAbstract = Modifier.isAbstract(m.getModifiers()); 077 078 if (isAbstract) 079 { 080 if (interfaceMethods.containsKey(s)) 081 continue; 082 083 // If a superclass defines an abstract method that a subclass implements, then 084 // all's OK. 085 086 if (implementedMethods.contains(s)) 087 continue; 088 089 _errorLog.error(EnhanceMessages.noImplForAbstractMethod( 090 m, 091 current, 092 baseClass, 093 enhancedClass), location, null); 094 } 095 096 implementedMethods.add(s); 097 } 098 099 current = current.getSuperclass(); 100 101 // No need to check Object.class; it is concrete and doesn't implement any interfaces, 102 // or provide any methods 103 // that might be declared in an interface. 104 105 if (current == null || current == Object.class) 106 break; 107 } 108 109 Iterator i = interfaceMethods.entrySet().iterator(); 110 while (i.hasNext()) 111 { 112 Map.Entry entry = (Map.Entry) i.next(); 113 114 MethodSignature sig = (MethodSignature) entry.getKey(); 115 116 if (implementedMethods.contains(sig)) 117 continue; 118 119 Method method = (Method) entry.getValue(); 120 121 _errorLog.error(EnhanceMessages.unimplementedInterfaceMethod( 122 method, 123 baseClass, 124 enhancedClass), location, null); 125 } 126 127 } 128 129 private void addInterfaceMethods(Class current, Map interfaceMethods) 130 { 131 Class[] interfaces = current.getInterfaces(); 132 133 for (int i = 0; i < interfaces.length; i++) 134 addMethodsFromInterface(interfaces[i], interfaceMethods); 135 } 136 137 private void addMethodsFromInterface(Class interfaceClass, Map interfaceMethods) 138 { 139 Method[] methods = interfaceClass.getMethods(); 140 141 for (int i = 0; i < methods.length; i++) 142 { 143 MethodSignature sig = new MethodSignature(methods[i]); 144 145 if (interfaceMethods.containsKey(sig)) 146 continue; 147 148 interfaceMethods.put(sig, methods[i]); 149 } 150 } 151 152 public void setErrorLog(ErrorLog errorLog) 153 { 154 _errorLog = errorLog; 155 } 156 }