| // 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/ast/ast.dart'; | |
| import 'package:analyzer/dart/element/element.dart'; | |
| import 'package:analyzer/dart/element/type.dart'; | |
| /// Information about a class with nested properties. | |
| /// | |
| /// If the property value is set, and the expression is an | |
| /// [InstanceCreationExpression], we add nested properties for parameters of | |
| /// the used constructor. | |
| /// | |
| /// If the property value is not set, but its type is a class, we still could | |
| /// add nested properties for this property. But we need to know how to | |
| /// materialize it - which constructor to call, and with which arguments. | |
| /// | |
| /// This class provides such "how to materialize" information. | |
| class ClassDescription { | |
| final ClassElement element; | |
| final ConstructorElement constructor; | |
| new(this.element, this.constructor); | |
| } | |
| /// The lazy-fill registry of [ClassDescription]. | |
| class ClassDescriptionRegistry { | |
| final Map<ClassElement, ClassDescription> _map = {}; | |
| /// Flush all data, because there was a change to a file. | |
| void flush() { | |
| _map.clear(); | |
| } | |
| /// If we know how to materialize the [element], return [ClassDescription]. | |
| /// Otherwise return `null`. | |
| ClassDescription? get(InterfaceElement element) { | |
| if (element is! ClassElement) { | |
| return null; | |
| } | |
| var description = _map[element]; | |
| if (description == null) { | |
| description = _classDescription(element); | |
| if (description != null) { | |
| _map[element] = description; | |
| } | |
| } | |
| return description; | |
| } | |
| /// Return `true` if properties should be created for instances of [type]. | |
| bool hasNestedProperties(DartType type) { | |
| if (type is InterfaceType) { | |
| return _isOptedInClass(type.element); | |
| } | |
| return false; | |
| } | |
| ClassDescription? _classDescription(ClassElement element) { | |
| if (!_isOptedInClass(element)) return null; | |
| var constructor = element.unnamedConstructor; | |
| if (constructor == null) return null; | |
| for (var parameter in constructor.formalParameters) { | |
| if (parameter.isRequired || parameter.metadata.hasRequired) { | |
| return null; | |
| } | |
| } | |
| return ClassDescription(element, constructor); | |
| } | |
| bool _isOptedInClass(InterfaceElement element) { | |
| if (element is! ClassElement) { | |
| return false; | |
| } | |
| return _isClass( | |
| element, | |
| 'package:flutter/src/widgets/container.dart', | |
| 'Container', | |
| ) || | |
| _isClass( | |
| element, | |
| 'package:flutter/src/painting/text_style.dart', | |
| 'TextStyle', | |
| ); | |
| } | |
| static bool _isClass(ClassElement element, String uri, String name) { | |
| return element.name == name && element.library.uri.toString() == uri; | |
| } | |
| } |