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 }