Source code: com/puppycrawl/tools/checkstyle/checks/usage/transmogrify/ExternalClass.java
1
2 // Transmogrify License
3 //
4 // Copyright (c) 2001, ThoughtWorks, Inc.
5 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions
8 // are met:
9 // - Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // - Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 // Neither the name of the ThoughtWorks, Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from this
16 // software without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 package com.puppycrawl.tools.checkstyle.checks.usage.transmogrify;
30
31
32
33 import java.lang.reflect.Constructor;
34 import java.lang.reflect.Field;
35 import java.lang.reflect.Method;
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.HashSet;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Set;
42 import java.util.SortedSet;
43 import java.util.TreeSet;
44
45 //import com.puppycrawl.tools.checkstyle.checks.lint.parser.DotUtils;
46
47 public class ExternalClass extends ExternalDefinition implements IClass {
48 private Class _javaClass;
49
50 private List _subclasses;
51 private List _implementors;
52
53 private Set _methods;
54
55 public ExternalClass(Class javaClass) {
56 _javaClass = javaClass;
57 _subclasses = new ArrayList();
58 _implementors = new ArrayList();
59 }
60
61 public Class getJavaClass() {
62 return _javaClass;
63 }
64
65 public String getName() {
66 return getLocalName();
67 }
68
69 private String getLocalName() {
70 String fullName = _javaClass.getName();
71 return fullName.substring(fullName.lastIndexOf(".") + 1);
72 }
73
74 public IClass getSuperclass() {
75 IClass result = null;
76 Class javaSuperclass = _javaClass.getSuperclass();
77
78 if (javaSuperclass != null) {
79 result = new ExternalClass(javaSuperclass);
80 }
81 else {
82 if (_javaClass.isInterface()) {
83 // according to Java, java.lang.Object is not the superclass of
84 // interfaces, which makes sense. However, we need Object to be
85 // the superclass of everything to make type compatibility work.
86 result = new ExternalClass(Object.class);
87 }
88 }
89
90 return result;
91 }
92
93 public IClass[] getInterfaces() {
94 Class[] javaInterfaces = _javaClass.getInterfaces();
95
96 IClass[] result = new IClass[javaInterfaces.length];
97
98 // should we cache this?
99 for (int i = 0; i < javaInterfaces.length; i++) {
100 result[i] = new ExternalClass(javaInterfaces[i]);
101 }
102
103 return result;
104 }
105
106 public IClass getClassDefinition(String name) {
107 IClass result = null;
108
109 String qualifiedName = getQualifiedName() + "$" + name;
110
111 Class[] classes = getJavaClass().getClasses();
112 for (int i = 0; i < classes.length; i++) {
113 String candidateQualifiedName = classes[i].getName();
114
115 if (qualifiedName.equals(candidateQualifiedName)) {
116 result = new ExternalClass(classes[i]);
117 break;
118 }
119 }
120
121 return result;
122 }
123
124 // REDTAG -- this should be a template method!
125 public IVariable getVariableDefinition(String name) {
126 IVariable result = null;
127 Field javaField = null;
128
129 try {
130 javaField = _javaClass.getDeclaredField(name);
131 }
132 catch (NoSuchFieldException ignoreMe) {}
133
134 if (javaField == null) {
135 Class[] interfaces = _javaClass.getInterfaces();
136 for (int i = 0; i < interfaces.length && javaField == null; i++) {
137 try {
138 javaField = interfaces[i].getDeclaredField(name);
139 }
140 catch (NoSuchFieldException ignoreMe) {}
141 }
142 }
143
144 if (javaField != null) {
145 result = new ExternalVariable(javaField);
146 }
147 else {
148 if (getSuperclass() != null) {
149 result = getSuperclass().getVariableDefinition(name);
150 }
151 }
152
153 return result;
154 }
155
156 public IMethod getMethodDefinition(String name,
157 ISignature signature) {
158 IMethod result = null;
159
160 if (name.equals(getName())) {
161 result = getConstructorDefinition(signature);
162 }
163 else {
164 Method[] methods = _javaClass.getDeclaredMethods();
165
166 for (int i = 0; i < methods.length; i++) {
167 if (name.equals(methods[i].getName())) {
168 IMethod method = new ExternalMethod(methods[i]);
169 if (method.hasSameSignature(signature)) {
170 result = method;
171 break;
172 }
173 }
174 }
175
176 if (result == null) {
177 result = getMostCompatibleMethod(name, signature);
178 }
179
180 if (result == null) {
181 if (getSuperclass() != null) {
182 result = getSuperclass().getMethodDefinition(name, signature);
183 }
184 }
185
186 if (result == null) {
187 IClass[] interfaces = getInterfaces();
188 for (int i = 0; i < interfaces.length; i++) {
189 result = interfaces[i].getMethodDefinition(name, signature);
190
191 if (result != null) {
192 break;
193 }
194
195 }
196 }
197 }
198
199 return result;
200 }
201
202 public IMethod getMostCompatibleMethod(String name, ISignature signature) {
203 IMethod result = null;
204
205 SortedSet compatibleMethods
206 = new TreeSet(new MethodSpecificityComparator());
207
208 Iterator it = getMethods().iterator();
209 while (it.hasNext()) {
210 IMethod method = (IMethod)it.next();
211 if ( name.equals( method.getName() ) ) {
212 if ( method.hasCompatibleSignature( signature ) ) {
213 compatibleMethods.add(method);
214 }
215 }
216 }
217
218 if (!compatibleMethods.isEmpty()) {
219 result = (IMethod)compatibleMethods.first();
220 }
221
222 return result;
223 }
224
225 private Collection getMethods() {
226 if (_methods == null) {
227 _methods = new HashSet();
228
229 Method[] methods = _javaClass.getDeclaredMethods();
230 for (int i = 0; i < methods.length; i++) {
231 _methods.add(new ExternalMethod(methods[i]));
232 }
233
234 }
235
236 return _methods;
237 }
238
239 public IMethod getConstructorDefinition(ISignature signature) {
240 IMethod result = null;
241
242 if (_javaClass.isInterface()) {
243 result = getInterfaceConstructor(signature);
244 }
245 else {
246 result = getClassConstructor(signature);
247 }
248
249 return result;
250 }
251
252 private IMethod getInterfaceConstructor(ISignature signature) {
253 IMethod result = null;
254
255 if (signature.getParameters().length == 0) {
256 result = new InterfaceConstructor(_javaClass);
257 }
258
259 return result;
260 }
261
262 private IMethod getClassConstructor(ISignature signature) {
263 IMethod result = null;
264
265 Constructor[] constructors = _javaClass.getConstructors();
266
267 for (int i = 0; i < constructors.length; i++) {
268 IMethod constructor = new ExternalConstructor(constructors[i]);
269 if (constructor.hasSameSignature(signature)) {
270 result = constructor;
271 break;
272 }
273 }
274
275 if (result == null) {
276 for (int i = 0; i < constructors.length; i++) {
277 IMethod constructor = new ExternalConstructor(constructors[i]);
278 if (constructor.hasCompatibleSignature(signature)) {
279 result = constructor;
280 break;
281 }
282 }
283 }
284
285 return result;
286 }
287
288 public boolean isCompatibleWith(IClass type) {
289 boolean result = false;
290
291 if (isPrimitive() && type.isPrimitive()) {
292 result = PrimitiveClasses.typesAreCompatible((ExternalClass)type,
293 this);
294 }
295 else {
296 // check myself
297 if (type.equals(this)) {
298 result = true;
299 }
300 // check my superclass
301 else if (getSuperclass() != null && getSuperclass().isCompatibleWith(type)) {
302 result = true;
303 }
304 // check my interfaces
305 else if (_javaClass.getInterfaces().length > 0) {
306 Class[] interfaces = _javaClass.getInterfaces();
307 for (int i = 0; i < interfaces.length; i++) {
308 if (new ExternalClass(interfaces[i]).isCompatibleWith(type)) {
309 result = true;
310 break;
311 }
312 }
313 }
314 }
315
316 return result;
317 }
318
319 private boolean isPrimitiveCompatibleWith(IClass type) {
320 boolean result = false;
321
322 return result;
323 }
324
325 public void addImplementor(ClassDef implementor) {
326 _implementors.add(implementor);
327 }
328
329 public List getImplementors() {
330 return _implementors;
331 }
332
333 public void addSubclass(ClassDef subclass) {
334 _subclasses.add(subclass);
335 }
336
337 public List getSubclasses() {
338 return _subclasses;
339 }
340
341 public String getQualifiedName() {
342 return _javaClass.getName();
343 }
344
345 public boolean isPrimitive() {
346 return getJavaClass().isPrimitive();
347 }
348
349 public IClass[] getInnerClasses() {
350 Class[] innerJavaClasses = getJavaClass().getDeclaredClasses();
351 IClass[] result = new IClass[innerJavaClasses.length];
352
353 for (int i = 0; i < result.length; i++) {
354 result[i] = new ExternalClass(innerJavaClasses[i]);
355 }
356
357 return result;
358 }
359
360 public String toString() {
361 return getClass() + "[" + getQualifiedName() + "]";
362 }
363
364 public boolean equals(Object o) {
365 boolean result = false;
366
367 if (o instanceof ExternalClass) {
368 ExternalClass compared = (ExternalClass)o;
369 result = (getJavaClass().equals(compared.getJavaClass()));
370 }
371
372 return result;
373 }
374
375 public int hashCode() {
376 return getJavaClass().hashCode();
377 }
378
379 }