// 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.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Vector;
/**
* <code>ClassDef</code> contains all the information needed to
* represent a java class or interface. This includes the superclass,
* whether it's a class or an interface, the interfaces it implements,
* a list of its (direct?) subclasses, and the classes that implement
* it if it is an interface
*
* @see Scope
*/
public class ClassDef extends DefaultScope implements IClass {
private long id = 0;
private IClass superclass = null;
private List interfaces = new Vector();
private List subclasses = new Vector();
private List implementors = new Vector();
private Set importedPackages = new HashSet();
// variable definitions will use elements from parent
private Set methods = new HashSet();
private Hashtable imports = new Hashtable();
private Vector unprocessedImports = null;
protected MethodDef _defaultConstructor;
public ClassDef(String name, Scope parentScope, SymTabAST node) {
super(name, parentScope, node);
_defaultConstructor = new DefaultConstructor(this);
addDefinition(_defaultConstructor);
}
public long getNextAnonymousId() {
return ++id;
}
public void setSuperclass(IClass newSuperclass) {
this.superclass = newSuperclass;
}
public IClass getSuperclass() {
return superclass;
}
public void addUnprocessedImports(Vector aImports) {
unprocessedImports = (Vector) (aImports.clone());
}
public Vector getUnprocessedImports() {
return unprocessedImports;
}
public void importPackage(IPackage pkg) {
importedPackages.add(pkg);
}
public void importClass(IClass imported) {
imports.put(imported.getName(), imported);
}
// begin definitions interface
public void addDefinition(MethodDef method) {
if (method.getName().equals(getName())) {
methods.remove(_defaultConstructor);
}
methods.add(method);
}
protected Enumeration getDefinitions() {
Vector allElements = new Vector();
allElements.addAll(elements.values());
allElements.addAll(methods);
allElements.addAll(labels.values());
allElements.addAll(classes.values());
return allElements.elements();
}
public IClass getClassDefinition(String name) {
IClass result = null;
result = (ClassDef) (classes.get(name));
if (result == null) {
result = (IClass) (imports.get(name));
}
if (result == null) {
Iterator it = importedPackages.iterator();
while (it.hasNext() && result == null) {
IPackage pkg = (IPackage) it.next();
result = pkg.getClass(name);
}
}
if (result == null) {
result = getParentScope().getClassDefinition(name);
}
//TODO: check for a class in the same package?
if (result == null) {
final String packageName = getParentScope().getQualifiedName();
final String fullName = packageName + "." + name;
Class theClass = null;
try {
theClass = ClassManager.getClassLoader().loadClass(fullName);
result = new ExternalClass(theClass);
}
catch (ClassNotFoundException e) {
// no-op
}
}
return result;
}
public IMethod getMethodDefinition(String name, ISignature signature) {
IMethod result = null;
result = getDeclaredMethod(name, signature);
if (result == null) {
result = getMostCompatibleMethod(name, signature);
}
if (result == null) {
if (superclass != null) {
result = superclass.getMethodDefinition(name, signature);
}
}
if (result == null) {
IClass[] myInterfaces = getInterfaces();
for (int index = 0;
index < myInterfaces.length && result == null;
index++) {
result = myInterfaces[index].getMethodDefinition(name, signature);
}
}
// not sure why this is here -- inner classes, maybe?
// regardless, write better
if (result == null) {
if (getParentScope() != null) {
result = getParentScope().getMethodDefinition(name, signature);
}
}
return result;
}
public IMethod getMostCompatibleMethod(String name, ISignature signature) {
IMethod result = null;
SortedSet compatibleMethods =
new TreeSet(new MethodSpecificityComparator());
Iterator it = methods.iterator();
while (it.hasNext()) {
MethodDef method = (MethodDef) it.next();
if (name.equals(method.getName())) {
if (method.hasCompatibleSignature(signature)) {
compatibleMethods.add(method);
}
}
}
if (!compatibleMethods.isEmpty()) {
result = (IMethod) compatibleMethods.first();
}
return result;
}
public IMethod getDeclaredMethod(String name, ISignature signature) {
// finds methods declared by this class with the given signature
IMethod result = null;
Iterator it = methods.iterator();
while (it.hasNext()) {
MethodDef method = (MethodDef) it.next();
if (name.equals(method.getName())) {
if (method.hasSameSignature(signature)) {
result = method;
break;
}
}
}
return result;
}
public IVariable getVariableDefinition(String name) {
IVariable result = null;
// in keeping with getField in java.lang.Class
// 1) current class
// 2) direct superinterfaces
// 3) superclass
// then we do the parent scope in case its an inner class
result = (VariableDef) (elements.get(name));
if (result == null) {
IClass[] superinterfaces = getInterfaces();
for (int i = 0;
i < superinterfaces.length && result == null;
i++) {
result = superinterfaces[i].getVariableDefinition(name);
}
}
if (result == null) {
if (superclass != null) {
result = superclass.getVariableDefinition(name);
}
}
if (result == null) {
if (getParentScope() != null) {
result = getParentScope().getVariableDefinition(name);
}
}
return result;
}
// end definitions interface
public void addInterface(IClass implemented) {
interfaces.add(implemented);
}
public IClass[] getInterfaces() {
IClass[] type = new IClass[0];
return (IClass[]) interfaces.toArray(type);
}
public ClassDef getEnclosingClass() {
return this;
}
public void addSubclass(ClassDef subclass) {
subclasses.add(subclass);
}
public List getSubclasses() {
return subclasses;
}
public void addImplementor(ClassDef implementor) {
implementors.add(implementor);
}
public List getImplementors() {
return implementors;
}
public IClass[] getInnerClasses() {
Iterator it = getClasses();
List result = new ArrayList();
while (it.hasNext()) {
result.add(it.next());
}
return (IClass[]) result.toArray(new IClass[0]);
}
public boolean isSuperclassOf(IClass possibleChild) {
// justify my existence
boolean result = subclasses.contains(possibleChild);
/*
Iterator it = subclasses.iterator();
while (it.hasNext() && !result) {
IClass child = (IClass)it.next();
result = child.isSuperclassOf(possibleChild);
}
*/
return result;
}
public boolean isCompatibleWith(IClass type) {
boolean result = false;
// check myself
if (type.equals(this)) {
result = true;
}
// check my superclass
else if (superclass != null && superclass.isCompatibleWith(type)) {
result = true;
}
// check my interfaces
else if (!interfaces.isEmpty()) {
Iterator it = interfaces.iterator();
while (it.hasNext() && !result) {
IClass current = (IClass) it.next();
if (current.isCompatibleWith(type)) {
result = true;
}
}
}
return result;
}
public boolean isPrimitive() {
return false;
}
}