/* * Copyright (c) 2013, the Dart project authors. * * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html * * 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.dart.engine.internal.resolver; import com.google.dart.engine.element.ClassElement; import com.google.dart.engine.element.CompilationUnitElement; import com.google.dart.engine.element.LibraryElement; import com.google.dart.engine.type.InterfaceType; import java.util.HashMap; import java.util.HashSet; /** * Instances of this class manage the knowledge of what the set of subtypes are for a given type. */ public class SubtypeManager { /** * A map between {@link ClassElement}s and a set of {@link ClassElement}s that are subtypes of the * key. */ private HashMap<ClassElement, HashSet<ClassElement>> subtypeMap = new HashMap<ClassElement, HashSet<ClassElement>>(); /** * The set of all {@link LibraryElement}s that have been visited by the manager. This is used both * to prevent infinite loops in the recursive methods, and also as a marker for the scope of the * libraries visited by this manager. */ private HashSet<LibraryElement> visitedLibraries = new HashSet<LibraryElement>(); /** * Given some {@link ClassElement}, return the set of all subtypes, and subtypes of subtypes. * * @param classElement the class to recursively return the set of subtypes of */ public HashSet<ClassElement> computeAllSubtypes(ClassElement classElement) { // Ensure that we have generated the subtype map for the library computeSubtypesInLibrary(classElement.getLibrary()); // use the subtypeMap to compute the set of all subtypes and subtype's subtypes HashSet<ClassElement> allSubtypes = new HashSet<ClassElement>(); safelyComputeAllSubtypes(classElement, new HashSet<ClassElement>(), allSubtypes); return allSubtypes; } /** * Given some {@link LibraryElement}, visit all of the types in the library, the passed library, * and any imported libraries, will be in the {@link #visitedLibraries} set. * * @param libraryElement the library to visit, it it hasn't been visited already */ public void ensureLibraryVisited(LibraryElement libraryElement) { computeSubtypesInLibrary(libraryElement); } /** * Given some {@link ClassElement}, this method adds all of the pairs combinations of itself and * all of its supertypes to the {@link #subtypeMap} map. * * @param classElement the class element */ private void computeSubtypesInClass(ClassElement classElement) { InterfaceType supertypeType = classElement.getSupertype(); if (supertypeType != null) { ClassElement supertypeElement = supertypeType.getElement(); if (supertypeElement != null) { putInSubtypeMap(supertypeElement, classElement); } } InterfaceType[] interfaceTypes = classElement.getInterfaces(); for (InterfaceType interfaceType : interfaceTypes) { ClassElement interfaceElement = interfaceType.getElement(); if (interfaceElement != null) { putInSubtypeMap(interfaceElement, classElement); } } InterfaceType[] mixinTypes = classElement.getMixins(); for (InterfaceType mixinType : mixinTypes) { ClassElement mixinElement = mixinType.getElement(); if (mixinElement != null) { putInSubtypeMap(mixinElement, classElement); } } } /** * Given some {@link CompilationUnitElement}, this method calls * {@link #computeAllSubtypes(ClassElement)} on all of the {@link ClassElement}s in the * compilation unit. * * @param unitElement the compilation unit element */ private void computeSubtypesInCompilationUnit(CompilationUnitElement unitElement) { ClassElement[] classElements = unitElement.getTypes(); for (ClassElement classElement : classElements) { computeSubtypesInClass(classElement); } } /** * Given some {@link LibraryElement}, this method calls * {@link #computeAllSubtypes(CompilationUnitElement)} on all of the {@link ClassElement}s in the * compilation unit, and itself for all imported and exported libraries. All visited libraries are * added to the {@link #visitedLibraries} set. * * @param libraryElement the library element */ private void computeSubtypesInLibrary(LibraryElement libraryElement) { if (libraryElement == null || visitedLibraries.contains(libraryElement)) { return; } visitedLibraries.add(libraryElement); computeSubtypesInCompilationUnit(libraryElement.getDefiningCompilationUnit()); CompilationUnitElement[] parts = libraryElement.getParts(); for (CompilationUnitElement part : parts) { computeSubtypesInCompilationUnit(part); } LibraryElement[] imports = libraryElement.getImportedLibraries(); for (LibraryElement importElt : imports) { computeSubtypesInLibrary(importElt.getLibrary()); } LibraryElement[] exports = libraryElement.getExportedLibraries(); for (LibraryElement exportElt : exports) { computeSubtypesInLibrary(exportElt.getLibrary()); } } /** * Add some key/ value pair into the {@link #subtypeMap} map. * * @param supertypeElement the key for the {@link #subtypeMap} map * @param subtypeElement the value for the {@link #subtypeMap} map */ private void putInSubtypeMap(ClassElement supertypeElement, ClassElement subtypeElement) { HashSet<ClassElement> subtypes = subtypeMap.get(supertypeElement); if (subtypes == null) { subtypes = new HashSet<ClassElement>(); subtypeMap.put(supertypeElement, subtypes); } subtypes.add(subtypeElement); } /** * Given some {@link ClassElement} and a {@link HashSet<ClassElement>}, this method recursively * adds all of the subtypes of the {@link ClassElement} to the passed array. * * @param classElement the type to compute the set of subtypes of * @param visitedClasses the set of class elements that this method has already recursively seen * @param allSubtypes the computed set of subtypes of the passed class element */ private void safelyComputeAllSubtypes(ClassElement classElement, HashSet<ClassElement> visitedClasses, HashSet<ClassElement> allSubtypes) { if (!visitedClasses.add(classElement)) { // if this class has already been called on this class element return; } HashSet<ClassElement> subtypes = subtypeMap.get(classElement); if (subtypes == null) { return; } for (ClassElement subtype : subtypes) { safelyComputeAllSubtypes(subtype, visitedClasses, allSubtypes); } allSubtypes.addAll(subtypes); } }