/*
* This file is part of JOP, the Java Optimized Processor
* see <http://www.jopdesign.com/>
*
* Copyright (C) 2010, Stefan Hepp (stefan@stefant.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.common.graphutils;
import com.jopdesign.common.ClassInfo;
/**
* This traverser can be used to visit all sub-classes or ancestors of a class.
*
* @author Stefan Hepp (stefan@stefant.org)
*/
public class ClassHierarchyTraverser {
private ClassVisitor visitor;
private boolean visitExtensions;
private boolean visitImplementations;
private boolean visitInnerClasses;
/**
* Create a new traverser for the given visitor.
* By default, traverse down all subclasses but not the nested classes.
*
* @param visitor the visitor to use.
*/
public ClassHierarchyTraverser(ClassVisitor visitor) {
this.visitor = visitor;
visitExtensions = true;
visitImplementations = true;
visitInnerClasses = false;
}
public ClassVisitor getVisitor() {
return visitor;
}
public void setVisitor(ClassVisitor visitor) {
this.visitor = visitor;
}
public boolean doVisitExtensions() {
return visitExtensions;
}
public boolean doVisitImplementations() {
return visitImplementations;
}
/**
* Set which subclasses of a class to visit.
*
* @param visitExtensions if true, visit extensions, i.e. classes of the same type (class or interface)
* @param visitImplementations if true, visit implementations, i.e. classes which implement an interface
*/
public void setVisitSubclasses(boolean visitExtensions, boolean visitImplementations) {
this.visitExtensions = visitExtensions;
this.visitImplementations = visitImplementations;
}
public boolean doVisitInnerClasses() {
return visitInnerClasses;
}
public void setVisitInnerClasses(boolean visitInnerClasses) {
this.visitInnerClasses = visitInnerClasses;
}
/**
* Visit the given class, all its subclasses and nested classes, depending on the set modes.
* If the visitor returns false for a class, the subclasses/nested classes of this class are not
* visited and the traverser continues with its next sibling.
* <p>This traverser does not check if an interface has already been visited.</p>
*
* @param classInfo the class to visit first
*/
public void traverseDown(ClassInfo classInfo) {
if ( !visitor.visitClass(classInfo) ) {
return;
}
if ( visitImplementations || visitExtensions ) {
for (ClassInfo c : classInfo.getDirectSubclasses()) {
// if extensions only, only go down if the subclass is a class for classes or
// if the subclass is an interface for interfaces.
if ( (visitExtensions && c.isInterface() == classInfo.isInterface()) ||
(visitImplementations && !c.isInterface() && classInfo.isInterface()) )
{
traverseDown(c);
}
}
}
if ( visitInnerClasses ) {
for (ClassInfo c : classInfo.getDirectNestedClasses()) {
traverseDown(c);
}
}
visitor.finishClass(classInfo);
}
/**
* Visit the superclasses, its implemented interfaces and enclosing classes of a class.
* If the visitor returns false for a class, the superclasses/interfaces/enclosing classes of this class are not
* visited and the traverser continues with its next sibling.
* <p>
* For classes, the superclass is visited if extensions should be visited, and its implemented interfaces are
* visited if implementations should be visited.
* For interfaces, the extended interfaces are visited (this traverser does not check if an interface has
* already been visited), the implementations-flag is ignored.
* </p>
*
* @param classInfo the class to visit first
*/
public void traverseUp(ClassInfo classInfo) {
if ( !visitor.visitClass(classInfo) ) {
return;
}
// we never visit Object from interfaces
if ( visitExtensions && !classInfo.isInterface()) {
ClassInfo superClass = classInfo.getSuperClassInfo();
if ( superClass != null ) {
traverseUp(superClass);
}
}
if ( (visitExtensions && classInfo.isInterface()) ||
(visitImplementations && !classInfo.isInterface()) )
{
for (ClassInfo i : classInfo.getInterfaces()) {
traverseUp(i);
}
}
if (visitInnerClasses) {
ClassInfo outerClass = classInfo.getEnclosingClassInfo();
if ( outerClass != null ) {
traverseUp(outerClass);
}
}
visitor.finishClass(classInfo);
}
}