blob: 8047b0542d76dd26e05c9c73f1b1a1199c983f5e [file] [log] [blame]
// Copyright (c) 2019, 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.
import 'package:analyzer/dart/element/element.dart';
import 'package:dartdoc/src/model/model.dart';
import 'package:dartdoc/src/model_utils.dart' as model_utils;
import 'package:dartdoc/src/quiver.dart' as quiver;
import 'package:meta/meta.dart';
/// A [Container] represents a Dart construct that can contain methods,
/// operators, and fields, such as [Class], [Enum], or [Extension].
///
/// Member naming in [Container] follows these general rules:
///
/// **instance** : Members named 'instance' contain the children of this
/// container that can be referenced from within the container without a prefix.
/// Usually overridden in subclasses with calls to super.
/// **constant** : Members named 'constant' contain children declared constant.
/// **variable** : The opposite of constant. For the templating system.
/// **static** : Members named 'static' are related to static children of this
/// container.
/// **public** : Filtered versions of the getters showing only public items.
/// Mostly for the templating system.
/// **sorted** : Filtered versions of the getters creating a sorted list by
/// name. For the templating system.
/// **has** : boolean getters indicating whether the underlying getters are
/// empty. Mostly for the templating system.
/// **all** : Referring to all children.
abstract class Container extends ModelElement with TypeParameters {
Container(Element element, Library library, PackageGraph packageGraph)
: super(element, library, packageGraph, null);
bool get isClass => element is ClassElement;
bool get isExtension => element is ExtensionElement;
@mustCallSuper
Iterable<ModelElement> get allModelElements => quiver.concat([
instanceMethods,
instanceFields,
instanceOperators,
instanceAccessors,
staticFields,
staticAccessors,
staticMethods,
]);
/// All methods, including operators and statics, declared as part of this
/// [Container]. [declaredMethods] must be the union of [instanceMethods],
/// [staticMethods], and [instanceOperators].
Iterable<Method> get declaredMethods;
Iterable<Method> get instanceMethods => declaredMethods
.where((m) => !m.isStatic && !m.isOperator)
.toList(growable: false);
/// Whether any constructors are public.
///
/// This is only used in mustache templates.
bool get hasPublicConstructors => false;
Iterable<Constructor> get publicConstructorsSorted => [];
/// Whether all instance fields are inherited.
///
/// This is only used in mustache templates.
bool get publicInheritedInstanceFields => false;
/// Whether all instance methods are inherited.
///
/// This is only used in mustache templates.
bool get publicInheritedInstanceMethods => false;
Iterable<Operator> get publicInheritedInstanceOperators => [];
@nonVirtual
bool get hasPublicInstanceMethods =>
model_utils.filterNonPublic(instanceMethods).isNotEmpty;
Iterable<Method> get publicInstanceMethods =>
model_utils.filterNonPublic(instanceMethods);
List<Method> _publicInstanceMethodsSorted;
List<Method> get publicInstanceMethodsSorted =>
_publicInstanceMethodsSorted ?? publicInstanceMethods.toList()
..sort(byName);
Iterable<Operator> _declaredOperators;
@nonVirtual
Iterable<Operator> get declaredOperators {
_declaredOperators ??=
declaredMethods.whereType<Operator>().toList(growable: false);
return _declaredOperators;
}
Iterable<Operator> get instanceOperators => declaredOperators;
@nonVirtual
bool get hasPublicInstanceOperators =>
publicInstanceOperatorsSorted.isNotEmpty;
@nonVirtual
Iterable<Operator> get publicInstanceOperators =>
model_utils.filterNonPublic(instanceOperators);
List<Operator> _publicInstanceOperatorsSorted;
List<Operator> get publicInstanceOperatorsSorted =>
_publicInstanceOperatorsSorted ??= publicInstanceOperators.toList()
..sort(byName);
/// Fields fully declared in this [Container].
Iterable<Field> get declaredFields;
/// All fields accessible in this instance that are not static.
Iterable<Field> get instanceFields =>
declaredFields.where((f) => !f.isStatic);
bool get hasInstanceFields => instanceFields.isNotEmpty;
@nonVirtual
Iterable<Field> get publicInstanceFields =>
model_utils.filterNonPublic(instanceFields);
@nonVirtual
bool get hasPublicInstanceFields => publicInstanceFields.isNotEmpty;
List<Field> _publicInstanceFieldsSorted;
List<Field> get publicInstanceFieldsSorted => _publicInstanceFieldsSorted ??=
publicInstanceFields.toList()..sort(byName);
Iterable<Field> get constantFields => declaredFields.where((f) => f.isConst);
Iterable<Field> get publicConstantFields =>
model_utils.filterNonPublic(constantFields);
bool get hasPublicConstantFields => publicConstantFieldsSorted.isNotEmpty;
List<Field> _publicConstantFieldsSorted;
List<Field> get publicConstantFieldsSorted => _publicConstantFieldsSorted ??=
publicConstantFields.toList()..sort(byName);
Iterable<Accessor> get instanceAccessors =>
instanceFields.expand((f) => f.allAccessors);
Iterable<Accessor> get staticAccessors =>
staticFields.expand((f) => f.allAccessors);
/// This container might be canonical for elements it does not contain.
/// See [Inheritable.canonicalEnclosingContainer].
bool containsElement(Element element) => allElements.contains(element);
Set<Element> _allElements;
Set<Element> get allElements =>
_allElements ??= allModelElements.map((e) => e.element).toSet();
Map<String, List<ModelElement>> _membersByName;
/// Given a ModelElement that is a member of some other class, return
/// the member of this class that has the same name and runtime type.
///
/// This enables object substitution for canonicalization, such as Interceptor
/// for Object.
T memberByExample<T extends ModelElement>(T example) {
if (_membersByName == null) {
_membersByName = {};
for (var me in allModelElements) {
if (!_membersByName.containsKey(me.name)) {
_membersByName[me.name] = [];
}
_membersByName[me.name].add(me);
}
}
ModelElement member;
// [T] is insufficiently specific to disambiguate between different
// subtypes of [Inheritable] or other mixins/implementations of
// [ModelElement] via [Iterable.whereType].
var possibleMembers = _membersByName[example.name]
.where((e) => e.runtimeType == example.runtimeType);
if (example is Accessor) {
possibleMembers = possibleMembers
.where((e) => example.isGetter == (e as Accessor).isGetter);
}
member = possibleMembers.first;
assert(possibleMembers.length == 1);
return member;
}
Map<String, List<ModelElement>> _allModelElementsByNamePart;
/// Helper for `_MarkdownCommentReference._getResultsForClass`.
Map<String, List<ModelElement>> get allModelElementsByNamePart {
if (_allModelElementsByNamePart == null) {
_allModelElementsByNamePart = {};
for (var me in allModelElements) {
_allModelElementsByNamePart.update(
me.namePart, (List<ModelElement> v) => v..add(me),
ifAbsent: () => <ModelElement>[me]);
}
}
return _allModelElementsByNamePart;
}
bool get hasPublicStaticFields => publicStaticFieldsSorted.isNotEmpty;
Iterable<Field> get publicStaticFields =>
model_utils.filterNonPublic(staticFields);
List<Field> _publicStaticFieldsSorted;
List<Field> get publicStaticFieldsSorted =>
_publicStaticFieldsSorted ??= publicStaticFields.toList()..sort(byName);
Iterable<Field> get staticFields => declaredFields.where((f) => f.isStatic);
Iterable<Field> get variableStaticFields =>
staticFields.where((f) => !f.isConst);
bool get hasPublicVariableStaticFields =>
publicVariableStaticFieldsSorted.isNotEmpty;
Iterable<Field> get publicVariableStaticFields =>
model_utils.filterNonPublic(variableStaticFields);
List<Field> _publicVariableStaticFieldsSorted;
List<Field> get publicVariableStaticFieldsSorted =>
_publicVariableStaticFieldsSorted ??= publicVariableStaticFields.toList()
..sort(byName);
Iterable<Method> get staticMethods =>
declaredMethods.where((m) => m.isStatic);
bool get hasPublicStaticMethods =>
model_utils.filterNonPublic(publicStaticMethodsSorted).isNotEmpty;
Iterable<Method> get publicStaticMethods =>
model_utils.filterNonPublic(staticMethods);
List<Method> _publicStaticMethodsSorted;
List<Method> get publicStaticMethodsSorted =>
_publicStaticMethodsSorted ??= publicStaticMethods.toList()..sort(byName);
}