| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library analysis.type_hierarhy; |
| |
| import 'dart:async'; |
| import 'dart:collection'; |
| |
| import 'package:analysis_server/src/computer/element.dart' show |
| engineElementToJson; |
| import 'package:analysis_services/constants.dart'; |
| import 'package:analysis_services/json.dart'; |
| import 'package:analysis_services/search/hierarchy.dart'; |
| import 'package:analysis_services/search/search_engine.dart'; |
| import 'package:analyzer/src/generated/element.dart'; |
| |
| /** |
| * A computer for a type hierarchy of an [Element]. |
| */ |
| class TypeHierarchyComputer { |
| final SearchEngine _searchEngine; |
| |
| ElementKind _pivotKind; |
| String _pivotName; |
| |
| final List<TypeHierarchyItem> _items = <TypeHierarchyItem>[]; |
| final Map<Element, TypeHierarchyItem> _elementItemMap = |
| new HashMap<Element, TypeHierarchyItem>(); |
| |
| TypeHierarchyComputer(this._searchEngine); |
| |
| /** |
| * Returns the computed type hierarchy, maybe `null`. |
| */ |
| Future<List<TypeHierarchyItem>> compute(Element element) { |
| _pivotKind = element.kind; |
| _pivotName = element.name; |
| if (element is ExecutableElement && |
| element.enclosingElement is ClassElement) { |
| element = element.enclosingElement; |
| } |
| if (element is ClassElement) { |
| InterfaceType type = element.type; |
| _createSuperItem(type); |
| return _createSubclasses(_items[0], type).then((_) { |
| return new Future.value(_items); |
| }); |
| } |
| return new Future.value([]); |
| } |
| |
| Future _createSubclasses(TypeHierarchyItem item, InterfaceType type) { |
| var future = getDirectSubClasses(_searchEngine, type.element); |
| return future.then((Set<ClassElement> subElements) { |
| List<TypeHierarchyItem> subItems = <TypeHierarchyItem>[]; |
| for (ClassElement subElement in subElements) { |
| // check for recursion |
| TypeHierarchyItem subItem = _elementItemMap[subElement]; |
| if (subItem != null) { |
| int id = _items.indexOf(subItem); |
| subItem.subclasses.add(id); |
| continue; |
| } |
| // create a subclass item |
| ExecutableElement subMemberElement = _findMemberElement(subElement); |
| subItem = new TypeHierarchyItem( |
| _items.length, |
| subElement, |
| subMemberElement, |
| null, |
| item.id, |
| <int>[], |
| <int>[]); |
| // remember |
| _elementItemMap[subElement] = subItem; |
| _items.add(subItem); |
| // add to hierarchy |
| item.subclasses.add(subItem.id); |
| subItems.add(subItem); |
| } |
| // compute subclasses of subclasses |
| return Future.forEach(subItems, (TypeHierarchyItem subItem) { |
| InterfaceType subType = subItem.classElement.type; |
| return _createSubclasses(subItem, subType); |
| }); |
| }); |
| } |
| |
| int _createSuperItem(InterfaceType type) { |
| // check for recursion |
| TypeHierarchyItem item = _elementItemMap[type.element]; |
| if (item != null) { |
| return _items.indexOf(item); |
| } |
| // create an empty item now |
| { |
| String displayName = null; |
| if (type.typeArguments.isNotEmpty) { |
| displayName = type.toString(); |
| } |
| ClassElement classElement = type.element; |
| ExecutableElement memberElement = _findMemberElement(classElement); |
| item = new TypeHierarchyItem( |
| _items.length, |
| classElement, |
| memberElement, |
| displayName, |
| null, |
| <int>[], |
| <int>[]); |
| _elementItemMap[classElement] = item; |
| _items.add(item); |
| } |
| // superclass |
| { |
| InterfaceType superType = type.superclass; |
| if (superType != null) { |
| item.superclass = _createSuperItem(superType); |
| } |
| } |
| // mixins |
| type.mixins.forEach((InterfaceType type) { |
| int id = _createSuperItem(type); |
| item.mixins.add(id); |
| }); |
| // interfaces |
| type.interfaces.forEach((InterfaceType type) { |
| int id = _createSuperItem(type); |
| item.interfaces.add(id); |
| }); |
| // done |
| return item.id; |
| } |
| |
| ExecutableElement _findMemberElement(ClassElement classElement) { |
| if (_pivotKind == ElementKind.METHOD) { |
| return classElement.getMethod(_pivotName); |
| } |
| if (_pivotKind == ElementKind.GETTER) { |
| return classElement.getGetter(_pivotName); |
| } |
| if (_pivotKind == ElementKind.SETTER) { |
| return classElement.getSetter(_pivotName); |
| } |
| return null; |
| } |
| } |
| |
| |
| class TypeHierarchyItem implements HasToJson { |
| final int id; |
| final ClassElement classElement; |
| final Element memberElement; |
| final String displayName; |
| int superclass; |
| final List<int> mixins; |
| final List<int> interfaces; |
| final List<int> subclasses = <int>[]; |
| |
| TypeHierarchyItem(this.id, this.classElement, this.memberElement, |
| this.displayName, this.superclass, this.mixins, this.interfaces); |
| |
| Map<String, Object> toJson() { |
| Map<String, Object> json = {}; |
| json[CLASS_ELEMENT] = engineElementToJson(classElement); |
| if (memberElement != null) { |
| json[MEMBER_ELEMENT] = engineElementToJson(memberElement); |
| } |
| if (displayName != null) { |
| json[DISPLAY_NAME] = displayName; |
| } |
| if (superclass != null) { |
| json[SUPERCLASS] = objectToJson(superclass); |
| } |
| json[INTERFACES] = objectToJson(interfaces); |
| json[MIXINS] = objectToJson(mixins); |
| json[SUBCLASSES] = objectToJson(subclasses); |
| return json; |
| } |
| } |