blob: cf93f8fa4bc0ee4036cae98a3c0a0fcd3415caf1 [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.user.rebind.rpc;
import com.google.gwt.core.ext.typeinfo.JClassType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
* Collection of utility methods for dealing with type hierarchies.
*/
class TypeHierarchyUtils {
/**
* Returns <code>true</code> if the type directly implements the specified
* interface. The test is done on erased types; any paramaterizations
* supplied in the arguments are ignored.
*
* @param type type to check
* @param intf interface to look for
* @return <code>true</code> if the type directly implements the specified
* interface
*/
public static boolean directlyImplementsInterface(JClassType type,
JClassType intf) {
type = type.getErasedType();
intf = intf.getErasedType();
return directlyImplementsInterfaceRecursive(new HashSet<JClassType>(),
type, intf);
}
/**
* Returns all types on the path from the root type to the serializable
* leaves.
*
* @param root the root type
* @param leaves the set of serializable leaf types
* @return all types on the path from the root type to the serializable leaves
*/
public static List<JClassType> getAllTypesBetweenRootTypeAndLeaves(
JClassType root, Collection<JClassType> leaves) {
Map<JClassType, List<JClassType>> adjList = getInvertedTypeHierarchy(root.getErasedType());
Set<JClassType> types = new HashSet<JClassType>();
for (JClassType type : leaves) {
depthFirstSearch(types, adjList, type.getErasedType());
}
return Arrays.asList(types.toArray(new JClassType[0]));
}
/**
* Returns the immediate subtypes of the erased class argument.
*/
public static List<JClassType> getImmediateSubtypes(JClassType clazz) {
List<JClassType> immediateSubtypes = new ArrayList<JClassType>();
clazz = clazz.getErasedType();
for (JClassType subclass : clazz.getSubtypes()) {
JClassType superclass = subclass.getSuperclass();
if (superclass != null) {
superclass = superclass.getErasedType();
}
if (superclass == clazz || clazz.isInterface() != null
&& directlyImplementsInterface(subclass, clazz)) {
immediateSubtypes.add(subclass);
}
}
return immediateSubtypes;
}
private static void addEdge(Map<JClassType, List<JClassType>> adjList,
JClassType subclass, JClassType clazz) {
List<JClassType> edges = adjList.get(subclass);
if (edges == null) {
edges = new ArrayList<JClassType>();
adjList.put(subclass, edges);
}
edges.add(clazz);
}
private static void depthFirstSearch(Set<JClassType> seen,
Map<JClassType, List<JClassType>> adjList, JClassType type) {
if (seen.contains(type)) {
return;
}
seen.add(type);
List<JClassType> children = adjList.get(type);
if (children != null) {
for (JClassType child : children) {
if (!seen.contains(child)) {
depthFirstSearch(seen, adjList, child);
}
}
}
}
private static boolean directlyImplementsInterfaceRecursive(
Set<JClassType> seen, JClassType clazz, JClassType intf) {
assert (clazz.getErasedType() == clazz);
assert (intf.getErasedType() == intf);
if (clazz == intf) {
return true;
}
JClassType[] intfImpls = clazz.getImplementedInterfaces();
for (JClassType intfImpl : intfImpls) {
intfImpl = intfImpl.getErasedType();
if (!seen.contains(intfImpl)) {
seen.add(intfImpl);
if (directlyImplementsInterfaceRecursive(seen, intfImpl, intf)) {
return true;
}
}
}
return false;
}
/**
* Given a root type return an adjacency list that is the inverted type
* hierarchy.
*/
private static Map<JClassType, List<JClassType>> getInvertedTypeHierarchy(
JClassType root) {
Map<JClassType, List<JClassType>> adjList = new HashMap<JClassType, List<JClassType>>();
Set<JClassType> seen = new HashSet<JClassType>();
Stack<JClassType> queue = new Stack<JClassType>();
queue.push(root);
while (!queue.isEmpty()) {
JClassType clazz = queue.pop();
if (seen.contains(clazz)) {
continue;
}
seen.add(clazz);
List<JClassType> immediateSubtypes = getImmediateSubtypes(clazz);
for (JClassType immediateSubtype : immediateSubtypes) {
// Add an edge from the immediate subtype to the supertype
addEdge(adjList, immediateSubtype, clazz);
queue.push(immediateSubtype);
}
}
return adjList;
}
}