| // 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 docgen.model_helpers; |
| |
| import 'dart:collection'; |
| |
| import '../exports/dart2js_mirrors.dart' as dart2js_mirrors; |
| import '../exports/mirrors_util.dart' as dart2js_util; |
| import '../exports/source_mirrors.dart'; |
| import '../exports/libraries.dart'; |
| |
| import '../library_helpers.dart' show includePrivateMembers; |
| import '../package_helpers.dart'; |
| |
| import 'annotation.dart'; |
| import 'generic.dart'; |
| import 'indexable.dart'; |
| import 'library.dart'; |
| import 'method.dart'; |
| import 'parameter.dart'; |
| import 'variable.dart'; |
| |
| String getLibraryDocName(LibraryMirror mirror) => |
| dart2js_util.qualifiedNameOf(mirror).replaceAll('.', '-'); |
| |
| /// Expand the method map [mapToExpand] into a more detailed map that |
| /// separates out setters, getters, constructors, operators, and methods. |
| Map expandMethodMap(Map<String, Method> mapToExpand) => { |
| 'setters': recurseMap(filterMap(mapToExpand, |
| (key, val) => val.mirror.isSetter)), |
| 'getters': recurseMap(filterMap(mapToExpand, |
| (key, val) => val.mirror.isGetter)), |
| 'constructors': recurseMap(filterMap(mapToExpand, |
| (key, val) => val.mirror.isConstructor)), |
| 'operators': recurseMap(filterMap(mapToExpand, |
| (key, val) => val.mirror.isOperator)), |
| 'methods': recurseMap(filterMap(mapToExpand, |
| (key, val) => val.mirror.isRegularMethod && !val.mirror.isOperator)) |
| }; |
| |
| String getDefaultValue(ParameterMirror mirror) { |
| if (!mirror.hasDefaultValue) return null; |
| return getDefaultValueFromConstMirror(mirror.defaultValue); |
| } |
| |
| String getDefaultValueFromConstMirror( |
| dart2js_mirrors.Dart2JsConstantMirror valueMirror) { |
| |
| if (valueMirror is dart2js_mirrors.Dart2JsStringConstantMirror) { |
| return '"${valueMirror.reflectee}"'; |
| } |
| |
| if (valueMirror is dart2js_mirrors.Dart2JsListConstantMirror) { |
| var buffer = new StringBuffer('['); |
| |
| var values = new Iterable.generate(valueMirror.length, |
| (i) => valueMirror.getElement(i)) |
| .map((e) => getDefaultValueFromConstMirror(e)); |
| |
| buffer.writeAll(values, ', '); |
| |
| buffer.write(']'); |
| return buffer.toString(); |
| } |
| |
| if (valueMirror is dart2js_mirrors.Dart2JsMapConstantMirror) { |
| // TODO(kevmoo) Handle non-empty case |
| if (valueMirror.length == 0) return '{}'; |
| } |
| |
| // TODO(kevmoo) Handle consts of non-core types |
| |
| return '${valueMirror}'; |
| } |
| |
| /// Returns a list of meta annotations assocated with a mirror. |
| List<Annotation> createAnnotations(DeclarationMirror mirror, |
| Library owningLibrary) { |
| var annotationMirrors = mirror.metadata |
| .where((e) => e is dart2js_mirrors.Dart2JsConstructedConstantMirror); |
| var annotations = []; |
| annotationMirrors.forEach((annotation) { |
| var docgenAnnotation = new Annotation(annotation, owningLibrary); |
| if (!_SKIPPED_ANNOTATIONS.contains(dart2js_util.qualifiedNameOf( |
| docgenAnnotation.mirror))) { |
| annotations.add(docgenAnnotation); |
| } |
| }); |
| return annotations; |
| } |
| |
| /// A declaration is private if itself is private, or the owner is private. |
| // Issue(12202) - A declaration is public even if it's owner is private. |
| bool isHidden(DeclarationSourceMirror mirror) { |
| if (mirror is LibraryMirror) { |
| return _isLibraryPrivate(mirror); |
| } else if (mirror.owner is LibraryMirror) { |
| return (mirror.isPrivate || _isLibraryPrivate(mirror.owner) || |
| mirror.isNameSynthetic); |
| } else { |
| return (mirror.isPrivate || isHidden(mirror.owner) || |
| mirror.isNameSynthetic); |
| } |
| } |
| |
| /// Transforms the map by calling toMap on each value in it. |
| Map recurseMap(Map inputMap) { |
| var outputMap = new SplayTreeMap(); |
| inputMap.forEach((key, value) { |
| if (value is Map) { |
| outputMap[key] = recurseMap(value); |
| } else { |
| outputMap[key] = value.toMap(); |
| } |
| }); |
| return outputMap; |
| } |
| |
| Map filterMap(Map map, Function test) { |
| var exported = new Map(); |
| map.forEach((key, value) { |
| if (test(key, value)) exported[key] = value; |
| }); |
| return exported; |
| } |
| |
| /// Read a pubspec and return the library name given a [LibraryMirror]. |
| String getPackageName(LibraryMirror mirror) { |
| if (mirror.uri.scheme != 'file') return ''; |
| var rootdir = getPackageDirectory(mirror); |
| if (rootdir == null) return ''; |
| return packageNameFor(rootdir); |
| } |
| |
| |
| /// Helper that maps [mirrors] to their simple name in map. |
| Map addAll(Map map, Iterable<DeclarationMirror> mirrors) { |
| for (var mirror in mirrors) { |
| map[dart2js_util.nameOf(mirror)] = mirror; |
| } |
| return map; |
| } |
| |
| /// For the given library determine what items (if any) are exported. |
| /// |
| /// Returns a Map with three keys: "classes", "methods", and "variables" the |
| /// values of which point to a map of exported name identifiers with values |
| /// corresponding to the actual DeclarationMirror. |
| Map<String, Map<String, DeclarationMirror>> calcExportedItems( |
| LibrarySourceMirror library) { |
| var exports = {}; |
| exports['classes'] = {}; |
| exports['methods'] = {}; |
| exports['variables'] = {}; |
| |
| // Determine the classes, variables and methods that are exported for a |
| // specific dependency. |
| void _populateExports(LibraryDependencyMirror export, bool showExport) { |
| if (!showExport) { |
| // Add all items, and then remove the hidden ones. |
| // Ex: "export foo hide bar" |
| addAll(exports['classes'], |
| dart2js_util.typesOf(export.targetLibrary.declarations)); |
| addAll(exports['methods'], |
| export.targetLibrary.declarations.values.where( |
| (mirror) => mirror is MethodMirror)); |
| addAll(exports['variables'], |
| dart2js_util.variablesOf(export.targetLibrary.declarations)); |
| } |
| for (CombinatorMirror combinator in export.combinators) { |
| for (String identifier in combinator.identifiers) { |
| var librarySourceMirror = |
| export.targetLibrary as DeclarationSourceMirror; |
| var declaration = librarySourceMirror.lookupInScope(identifier); |
| if (declaration == null) { |
| // Technically this should be a bug, but some of our packages |
| // (such as the polymer package) are curently broken in this |
| // way, so we just produce a warning. |
| print('Warning identifier $identifier not found in library ' |
| '${dart2js_util.qualifiedNameOf(export.targetLibrary)}'); |
| } else { |
| var subMap = exports['classes']; |
| if (declaration is MethodMirror) { |
| subMap = exports['methods']; |
| } else if (declaration is VariableMirror) { |
| subMap = exports['variables']; |
| } |
| if (showExport) { |
| subMap[identifier] = declaration; |
| } else { |
| subMap.remove(identifier); |
| } |
| } |
| } |
| } |
| } |
| |
| Iterable<LibraryDependencyMirror> exportList = |
| library.libraryDependencies.where((lib) => lib.isExport); |
| for (LibraryDependencyMirror export in exportList) { |
| // If there is a show in the export, add only the show items to the |
| // library. Ex: "export foo show bar" |
| // Otherwise, add all items, and then remove the hidden ones. |
| // Ex: "export foo hide bar" |
| _populateExports(export, |
| export.combinators.any((combinator) => combinator.isShow)); |
| } |
| return exports; |
| } |
| |
| |
| /// Returns a map of [Variable] objects constructed from [mirrorMap]. |
| /// The optional parameter [containingLibrary] is contains data for variables |
| /// defined at the top level of a library (potentially for exporting |
| /// purposes). |
| Map<String, Variable> createVariables(Iterable<VariableMirror> mirrors, |
| Indexable owner) { |
| var data = {}; |
| // TODO(janicejl): When map to map feature is created, replace the below |
| // with a filter. Issue(#9590). |
| mirrors.forEach((dart2js_mirrors.Dart2JsFieldMirror mirror) { |
| if (includePrivateMembers || !isHidden(mirror)) { |
| var mirrorName = dart2js_util.nameOf(mirror); |
| data[mirrorName] = new Variable(mirrorName, mirror, owner); |
| } |
| }); |
| return data; |
| } |
| |
| /// Returns a map of [Method] objects constructed from [mirrorMap]. |
| /// The optional parameter [containingLibrary] is contains data for variables |
| /// defined at the top level of a library (potentially for exporting |
| /// purposes). |
| Map<String, Method> createMethods(Iterable<MethodMirror> mirrors, |
| Indexable owner) { |
| var group = new Map<String, Method>(); |
| mirrors.forEach((MethodMirror mirror) { |
| if (includePrivateMembers || !mirror.isPrivate) { |
| group[dart2js_util.nameOf(mirror)] = new Method(mirror, owner); |
| } |
| }); |
| return group; |
| } |
| |
| /// Returns a map of [Parameter] objects constructed from [mirrorList]. |
| Map<String, Parameter> createParameters(List<ParameterMirror> mirrorList, |
| Indexable owner) { |
| var data = {}; |
| mirrorList.forEach((ParameterMirror mirror) { |
| data[dart2js_util.nameOf(mirror)] = |
| new Parameter(mirror, owner.owningLibrary); |
| }); |
| return data; |
| } |
| |
| /// Returns a map of [Generic] objects constructed from the class mirror. |
| Map<String, Generic> createGenerics(TypeMirror mirror) { |
| return new Map.fromIterable(mirror.typeVariables, |
| key: (e) => dart2js_util.nameOf(e), |
| value: (e) => new Generic(e)); |
| } |
| |
| /// Annotations that we do not display in the viewer. |
| const List<String> _SKIPPED_ANNOTATIONS = const [ |
| 'metadata.DocsEditable', '_js_helper.JSName', '_js_helper.Creates', |
| '_js_helper.Returns' |
| ]; |
| |
| /// Returns true if a library name starts with an underscore, and false |
| /// otherwise. |
| /// |
| /// An example that starts with _ is _js_helper. |
| /// An example that contains ._ is dart._collection.dev |
| bool _isLibraryPrivate(dart2js_mirrors.Dart2JsLibraryMirror mirror) { |
| // This method is needed because LibraryMirror.isPrivate returns `false` all |
| // the time. |
| var sdkLibrary = LIBRARIES[dart2js_util.nameOf(mirror)]; |
| if (sdkLibrary != null) { |
| return !sdkLibrary.documented; |
| } else if (dart2js_util.nameOf(mirror).startsWith('_') || dart2js_util.nameOf( |
| mirror).contains('._')) { |
| return true; |
| } |
| return false; |
| } |