/* * 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; } }