// Transmogrify License
//
// Copyright (c) 2001, ThoughtWorks, Inc.
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// Neither the name of the ThoughtWorks, Inc. nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.puppycrawl.tools.checkstyle.checks.usage.transmogrify;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
//import com.puppycrawl.tools.checkstyle.checks.lint.parser.DotUtils;
public class ExternalClass extends ExternalDefinition implements IClass {
private Class _javaClass;
private List _subclasses;
private List _implementors;
private Set _methods;
public ExternalClass(Class javaClass) {
_javaClass = javaClass;
_subclasses = new ArrayList();
_implementors = new ArrayList();
}
public Class getJavaClass() {
return _javaClass;
}
public String getName() {
return getLocalName();
}
private String getLocalName() {
String fullName = _javaClass.getName();
return fullName.substring(fullName.lastIndexOf(".") + 1);
}
public IClass getSuperclass() {
IClass result = null;
Class javaSuperclass = _javaClass.getSuperclass();
if (javaSuperclass != null) {
result = new ExternalClass(javaSuperclass);
}
else {
if (_javaClass.isInterface()) {
// according to Java, java.lang.Object is not the superclass of
// interfaces, which makes sense. However, we need Object to be
// the superclass of everything to make type compatibility work.
result = new ExternalClass(Object.class);
}
}
return result;
}
public IClass[] getInterfaces() {
Class[] javaInterfaces = _javaClass.getInterfaces();
IClass[] result = new IClass[javaInterfaces.length];
// should we cache this?
for (int i = 0; i < javaInterfaces.length; i++) {
result[i] = new ExternalClass(javaInterfaces[i]);
}
return result;
}
public IClass getClassDefinition(String name) {
IClass result = null;
String qualifiedName = getQualifiedName() + "$" + name;
Class[] classes = getJavaClass().getClasses();
for (int i = 0; i < classes.length; i++) {
String candidateQualifiedName = classes[i].getName();
if (qualifiedName.equals(candidateQualifiedName)) {
result = new ExternalClass(classes[i]);
break;
}
}
return result;
}
// REDTAG -- this should be a template method!
public IVariable getVariableDefinition(String name) {
IVariable result = null;
Field javaField = null;
try {
javaField = _javaClass.getDeclaredField(name);
}
catch (NoSuchFieldException ignoreMe) {}
if (javaField == null) {
Class[] interfaces = _javaClass.getInterfaces();
for (int i = 0; i < interfaces.length && javaField == null; i++) {
try {
javaField = interfaces[i].getDeclaredField(name);
}
catch (NoSuchFieldException ignoreMe) {}
}
}
if (javaField != null) {
result = new ExternalVariable(javaField);
}
else {
if (getSuperclass() != null) {
result = getSuperclass().getVariableDefinition(name);
}
}
return result;
}
public IMethod getMethodDefinition(String name,
ISignature signature) {
IMethod result = null;
if (name.equals(getName())) {
result = getConstructorDefinition(signature);
}
else {
Method[] methods = _javaClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (name.equals(methods[i].getName())) {
IMethod method = new ExternalMethod(methods[i]);
if (method.hasSameSignature(signature)) {
result = method;
break;
}
}
}
if (result == null) {
result = getMostCompatibleMethod(name, signature);
}
if (result == null) {
if (getSuperclass() != null) {
result = getSuperclass().getMethodDefinition(name, signature);
}
}
if (result == null) {
IClass[] interfaces = getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
result = interfaces[i].getMethodDefinition(name, signature);
if (result != null) {
break;
}
}
}
}
return result;
}
public IMethod getMostCompatibleMethod(String name, ISignature signature) {
IMethod result = null;
SortedSet compatibleMethods
= new TreeSet(new MethodSpecificityComparator());
Iterator it = getMethods().iterator();
while (it.hasNext()) {
IMethod method = (IMethod)it.next();
if ( name.equals( method.getName() ) ) {
if ( method.hasCompatibleSignature( signature ) ) {
compatibleMethods.add(method);
}
}
}
if (!compatibleMethods.isEmpty()) {
result = (IMethod)compatibleMethods.first();
}
return result;
}
private Collection getMethods() {
if (_methods == null) {
_methods = new HashSet();
Method[] methods = _javaClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
_methods.add(new ExternalMethod(methods[i]));
}
}
return _methods;
}
public IMethod getConstructorDefinition(ISignature signature) {
IMethod result = null;
if (_javaClass.isInterface()) {
result = getInterfaceConstructor(signature);
}
else {
result = getClassConstructor(signature);
}
return result;
}
private IMethod getInterfaceConstructor(ISignature signature) {
IMethod result = null;
if (signature.getParameters().length == 0) {
result = new InterfaceConstructor(_javaClass);
}
return result;
}
private IMethod getClassConstructor(ISignature signature) {
IMethod result = null;
Constructor[] constructors = _javaClass.getConstructors();
for (int i = 0; i < constructors.length; i++) {
IMethod constructor = new ExternalConstructor(constructors[i]);
if (constructor.hasSameSignature(signature)) {
result = constructor;
break;
}
}
if (result == null) {
for (int i = 0; i < constructors.length; i++) {
IMethod constructor = new ExternalConstructor(constructors[i]);
if (constructor.hasCompatibleSignature(signature)) {
result = constructor;
break;
}
}
}
return result;
}
public boolean isCompatibleWith(IClass type) {
boolean result = false;
if (isPrimitive() && type.isPrimitive()) {
result = PrimitiveClasses.typesAreCompatible((ExternalClass)type,
this);
}
else {
// check myself
if (type.equals(this)) {
result = true;
}
// check my superclass
else if (getSuperclass() != null && getSuperclass().isCompatibleWith(type)) {
result = true;
}
// check my interfaces
else if (_javaClass.getInterfaces().length > 0) {
Class[] interfaces = _javaClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (new ExternalClass(interfaces[i]).isCompatibleWith(type)) {
result = true;
break;
}
}
}
}
return result;
}
public void addImplementor(ClassDef implementor) {
_implementors.add(implementor);
}
public List getImplementors() {
return _implementors;
}
public void addSubclass(ClassDef subclass) {
_subclasses.add(subclass);
}
public List getSubclasses() {
return _subclasses;
}
public String getQualifiedName() {
return _javaClass.getName();
}
public boolean isPrimitive() {
return getJavaClass().isPrimitive();
}
public IClass[] getInnerClasses() {
Class[] innerJavaClasses = getJavaClass().getDeclaredClasses();
IClass[] result = new IClass[innerJavaClasses.length];
for (int i = 0; i < result.length; i++) {
result[i] = new ExternalClass(innerJavaClasses[i]);
}
return result;
}
public String toString() {
return getClass() + "[" + getQualifiedName() + "]";
}
public boolean equals(Object o) {
boolean result = false;
if (o instanceof ExternalClass) {
ExternalClass compared = (ExternalClass)o;
result = (getJavaClass().equals(compared.getJavaClass()));
}
return result;
}
public int hashCode() {
return getJavaClass().hashCode();
}
}