Chromium Code Reviews| Index: pkg/analysis_services/lib/search/hierarchy.dart |
| diff --git a/pkg/analysis_services/lib/search/hierarchy.dart b/pkg/analysis_services/lib/search/hierarchy.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8612912902bce40f4b07ac6a4b741d60ff74a274 |
| --- /dev/null |
| +++ b/pkg/analysis_services/lib/search/hierarchy.dart |
| @@ -0,0 +1,212 @@ |
| +// 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. |
| + |
| +// This code was auto-generated, is not intended to be edited, and is subject to |
| +// significant change. Please see the README file for more information. |
| + |
| +library services.hierarchy; |
| + |
| +import 'dart:async'; |
| +import 'dart:collection'; |
| + |
| +import 'package:analysis_services/search/search_engine.dart'; |
| +import 'package:analyzer/src/generated/element.dart'; |
| + |
| + |
| +/** |
| + * Returns direct non-synthetic children of the given [ClassElement]. |
| + * |
| + * Includes: fields, accessors and methods. |
| + * Excludes: constructors and synthetic elements. |
| + */ |
| +List<Element> getClassMembers(ClassElement clazz, [String name]) { |
| + List<Element> members = <Element>[]; |
| + clazz.accept(new _ElementVisitorAdapter((Element element) { |
|
Brian Wilkerson
2014/07/15 19:56:16
If you used 'visitChildren' instead of 'accept' yo
scheglov
2014/07/15 20:30:12
Done.
|
| + if (element == clazz) { |
| + return true; |
| + } |
| + if (element.isSynthetic) { |
| + return false; |
| + } |
| + if (element is ConstructorElement) { |
| + return false; |
| + } |
| + if (name != null && element.displayName != name) { |
| + return false; |
| + } |
| + if (element is ExecutableElement) { |
| + members.add(element); |
| + } |
| + if (element is FieldElement) { |
| + members.add(element); |
| + } |
| + return false; |
| + })); |
| + return members; |
| +} |
| + |
| + |
| +/** |
| + * Returns direct children of [parent]. |
| + */ |
| +List<Element> getChildren(Element parent, [String name]) { |
| + List<Element> children = <Element>[]; |
| + parent.accept(new _ElementVisitorAdapter((Element element) { |
|
Brian Wilkerson
2014/07/15 19:56:16
If you used 'visitChildren' instead of 'accept' yo
scheglov
2014/07/15 20:30:12
Done.
|
| + if (element == parent) { |
| + return true; |
| + } else { |
| + if (name == null || element.displayName == name) { |
| + children.add(element); |
| + } |
| + return false; |
| + } |
| + })); |
| + return children; |
| +} |
| + |
| + |
| +/** |
| + * Returns a [Set] with direct subclasses of [seed]. |
| + */ |
| +Future<Set<ClassElement>> getDirectSubClasses(SearchEngine searchEngine, |
| + ClassElement seed) { |
| + return searchEngine.searchSubtypes(seed).then((List<SearchMatch> matches) { |
| + Set<ClassElement> subClasses = new HashSet<ClassElement>(); |
| + for (SearchMatch match in matches) { |
| + ClassElement subClass = match.element; |
| + if (subClass.context == seed.context) { |
| + subClasses.add(subClass); |
| + } |
| + } |
| + return subClasses; |
| + }); |
| +} |
| + |
| + |
| +/** |
| + * Returns a [Set] with all direct and indirect subclasses of [seed]. |
| + */ |
| +Future<Set<ClassElement>> getSubClasses(SearchEngine searchEngine, |
| + ClassElement seed) { |
| + Set<ClassElement> subs = new HashSet<ClassElement>(); |
| + // prepare queue |
| + List<ClassElement> queue = new List<ClassElement>(); |
| + queue.add(seed); |
| + // schedule subclasss search |
| + addSubClasses() { |
| + // queue is empty, done |
| + if (queue.isEmpty) { |
| + subs.remove(seed); |
| + return subs; |
| + } |
| + // add direct subclasses of the next class |
| + while (true) { |
| + ClassElement clazz = queue.removeLast(); |
| + if (subs.add(clazz)) { |
| + return getDirectSubClasses(searchEngine, clazz).then((directSubs) { |
| + queue.addAll(directSubs); |
| + return new Future(addSubClasses); |
| + }); |
| + } |
| + } |
| + } |
| + return new Future(addSubClasses); |
| +} |
| + |
| + |
| +/** |
| + * @return all implementations of the given {@link ClassMemberElement} is its superclasses and |
| + * their subclasses. |
| + */ |
| +Future<Set<ClassMemberElement>> getHierarchyMembers(SearchEngine searchEngine, |
| + ClassMemberElement member) { |
| + Set<ClassMemberElement> result = new HashSet<ClassMemberElement>(); |
| + // constructor |
| + if (member is ConstructorElement) { |
| + result.add(member); |
| + return new Future.value(result); |
| + } |
| + // method, field, etc |
| + String name = member.displayName; |
| + ClassElement memberClass = member.enclosingElement; |
| + List<Future> futures = <Future>[]; |
| + Set<ClassElement> searchClasses = getSuperClasses(memberClass); |
| + searchClasses.add(memberClass); |
| + for (ClassElement superClass in searchClasses) { |
| + // ignore if super- class does not declare member |
| + if (getClassMembers(superClass, name).isEmpty) { |
| + continue; |
| + } |
| + // check all sub- classes |
| + var subClassFuture = getSubClasses(searchEngine, superClass); |
| + var membersFuture = subClassFuture.then((Set<ClassElement> subClasses) { |
| + subClasses.add(superClass); |
| + for (ClassElement subClass in subClasses) { |
| + List<Element> subClassMembers = getChildren(subClass, name); |
| + for (Element member in subClassMembers) { |
| + if (member is ClassMemberElement) { |
| + result.add(member); |
| + } |
| + } |
| + } |
| + }); |
| + futures.add(membersFuture); |
| + } |
| + return Future.wait(futures).then((_) { |
| + return result; |
| + }); |
| +} |
| + |
| + |
| +/** |
| + * Returns a [Set] with all direct and indirect superclasses of [seed]. |
| + */ |
| +Set<ClassElement> getSuperClasses(ClassElement seed) { |
| + Set<ClassElement> result = new HashSet<ClassElement>(); |
| + // prepare queue |
| + List<ClassElement> queue = new List<ClassElement>(); |
| + queue.add(seed); |
| + // process queue |
| + while (!queue.isEmpty) { |
| + ClassElement current = queue.removeLast(); |
| + // add if not checked already |
| + if (!result.add(current)) { |
| + continue; |
| + } |
| + // append supertype |
| + { |
| + InterfaceType superType = current.supertype; |
| + if (superType != null) { |
| + queue.add(superType.element); |
| + } |
| + } |
| + // append interfaces |
| + for (InterfaceType intf in current.interfaces) { |
| + queue.add(intf.element); |
| + } |
| + } |
| + // we don't need "seed" itself |
| + result.remove(seed); |
| + return result; |
| +} |
| + |
| + |
| +typedef bool ElementProcessor(Element element); |
| + |
| +/** |
| + * A [GeneralizingElementVisitor] that adapter for [ElementProcessor]. |
| + */ |
| +class _ElementVisitorAdapter extends GeneralizingElementVisitor { |
| + final ElementProcessor processor; |
| + |
| + _ElementVisitorAdapter(this.processor); |
| + |
| + @override |
| + void visitElement(Element element) { |
| + bool visitChildren = processor(element); |
| + if (visitChildren) { |
| + super.visitElement(element); |
| + } |
| + } |
| +} |