blob: 650f8a7cc03f3a02b7e56becd4156cc029990b31 [file] [log] [blame]
// 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(null);
}
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;
}
}