blob: b6644bca204aa3f31dd8224033da4e949c9ba990 [file] [log] [blame]
// Copyright (c) 2018, 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.
/// Handling for special elements within Dart. When identified these
/// may alter the interpretation and documentation generated for other
/// [ModelElement]s.
///
/// Finding these must not depend on canonicalization.
library dartdoc.special_elements;
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:dartdoc/src/model.dart';
/// Which of the [SpecialClasses] to get.
enum SpecialClass {
/// From dart:core, Object
object,
/// From dart:_interceptors, Interceptor
interceptor,
/// From dart:core, pragma
pragma,
}
/// A declaration of a special [Class] and how to find it.
class _SpecialClassDefinition {
/// Which specialElement this is.
final SpecialClass specialClass;
/// Name of the ModelElement.
final String name;
/// The library name for the [LibraryElement] in which this [ModelElement]
/// can be found.
final String libraryName;
/// The package name in which this [ModelElement] can be found.
final String packageName;
/// The URI for the library in which this [ModelElement] is defined.
final String specialFileUri;
/// If true, require this element to exist in the packageGraph when
/// calling the [SpecialClasses] constructor.
final bool required;
_SpecialClassDefinition(
this.specialClass, this.name, this.libraryName, this.specialFileUri,
{this.required = true, this.packageName = 'Dart'});
/// Get the filename for the Dart Library where this [specialClass] is
/// declared.
String getSpecialFilename(DartSdk sdk) =>
sdk.mapDartUri(specialFileUri)?.fullName;
bool matchesClass(Class modelClass) {
return modelClass.name == name &&
modelClass.library.element.name == libraryName &&
modelClass.package.name == packageName;
}
}
/// List all special classes we need to find here.
final List<_SpecialClassDefinition> _specialClassDefinitions = [
new _SpecialClassDefinition(
SpecialClass.object, 'Object', 'dart.core', 'dart:core'),
new _SpecialClassDefinition(SpecialClass.interceptor, 'Interceptor',
'_interceptors', 'dart:_interceptors',
required: false),
new _SpecialClassDefinition(
SpecialClass.pragma, 'pragma', 'dart.core', 'dart:core',
required: false),
];
/// Given a SDK, resolve URIs for the libraries containing our special
/// clases.
Set<String> specialLibraryFiles(DartSdk sdk) => _specialClassDefinitions
.map((_SpecialClassDefinition d) => d.getSpecialFilename(sdk))
.where((String s) => s != null)
.toSet();
Set<String> __specialLibraryNames;
/// These library names can be checked against the [LibraryElement] names
/// to avoid traversing libraries we don't need to.
Set<String> get _specialLibraryNames {
if (__specialLibraryNames == null) {
__specialLibraryNames = _specialClassDefinitions
.map((_SpecialClassDefinition d) => d.libraryName)
.toSet();
}
return __specialLibraryNames;
}
/// Class for managing special [Class] objects inside Dartdoc.
class SpecialClasses {
final PackageGraph packageGraph;
final Map<SpecialClass, Class> _specialClass = {};
SpecialClasses(this.packageGraph) {
Set<LibraryElement> doneKeys = new Set();
Set<LibraryElement> keysToDo = new Set.from(packageGraph.allLibraries.keys);
// Loops because traversing the libraries can instantiate additional
// libraries, and does so in this manner to avoid running into iterable
// modification exceptions.
while (keysToDo.isNotEmpty) {
keysToDo.forEach((LibraryElement e) {
if (_specialLibraryNames.contains(e.name)) {
packageGraph.allLibraries[e].allClasses.forEach((Class aClass) {
_specialClassDefinitions.forEach((_SpecialClassDefinition d) {
if (d.matchesClass(aClass)) {
assert(!_specialClass.containsKey(d.specialClass) ||
_specialClass[d.specialClass] == aClass);
_specialClass[d.specialClass] = aClass;
}
});
});
}
doneKeys.add(e);
});
keysToDo = new Set.from(packageGraph.allLibraries.keys
.where((LibraryElement e) => !doneKeys.contains(e)));
}
_specialClassDefinitions.forEach((_SpecialClassDefinition d) {
if (d.required) assert(_specialClass.containsKey(d.specialClass));
});
}
Class operator [](SpecialClass specialClass) => _specialClass[specialClass];
}