@@ -5,6 +5,7 @@ import 'package:angular2/src/transform/common/annotation_matcher.dart';
55import 'package:angular2/src/transform/common/logging.dart' ;
66import 'package:angular2/src/transform/common/model/reflection_info_model.pb.dart' ;
77import 'package:angular2/src/transform/common/names.dart' ;
8+ import 'package:angular2/src/transform/common/property_utils.dart' ;
89import 'package:barback/barback.dart' show AssetId;
910
1011import 'annotation_code.dart' ;
@@ -16,20 +17,26 @@ class ReflectionInfoVisitor extends RecursiveAstVisitor<ReflectionInfoModel> {
1617 /// The file we are processing.
1718 final AssetId assetId;
1819
20+ /// Responsible for testing whether [Annotation] s are those recognized by
21+ /// Angular 2, for example `@Component` .
22+ final AnnotationMatcher _annotationMatcher;
23+
1924 final AnnotationVisitor _annotationVisitor;
2025 final ParameterVisitor _parameterVisitor = new ParameterVisitor ();
26+ final _PropertyMetadataVisitor _propMetadataVisitor;
2127
2228 /// Whether an Angular 2 `Reflection` has been found.
2329 bool _foundNgReflection = false ;
2430
25- /// Responsible for testing whether [Annotation] s are those recognized by
26- /// Angular 2, for example `@Component` .
27- final AnnotationMatcher _annotationMatcher;
31+ ReflectionInfoVisitor ._(this .assetId, this ._annotationMatcher,
32+ this ._annotationVisitor, this ._propMetadataVisitor);
2833
29- ReflectionInfoVisitor (AssetId assetId, AnnotationMatcher annotationMatcher)
30- : this .assetId = assetId,
31- _annotationMatcher = annotationMatcher,
32- _annotationVisitor = new AnnotationVisitor (assetId, annotationMatcher);
34+ factory ReflectionInfoVisitor (
35+ AssetId assetId, AnnotationMatcher annotationMatcher) {
36+ var annotationVisitor = new AnnotationVisitor (assetId, annotationMatcher);
37+ return new ReflectionInfoVisitor ._(assetId, annotationMatcher,
38+ annotationVisitor, new _PropertyMetadataVisitor (annotationVisitor));
39+ }
3340
3441 bool get shouldCreateNgDeps => _foundNgReflection;
3542
@@ -92,9 +99,44 @@ class ReflectionInfoVisitor extends RecursiveAstVisitor<ReflectionInfoModel> {
9299 model.interfaces.addAll (node.implementsClause.interfaces
93100 .map ((interface ) => '${interface .name }' ));
94101 }
102+
103+ // Record annotations attached to properties.
104+ for (var member in node.members) {
105+ var propMetaList = member.accept (_propMetadataVisitor);
106+ if (propMetaList != null ) {
107+ model.propertyMetadata.addAll (propMetaList);
108+ }
109+ }
110+ _coalesce (model.propertyMetadata);
111+
95112 return model;
96113 }
97114
115+ // If a class has a getter & a setter with the same name and each has
116+ // individual metadata, collapse to a single entry.
117+ void _coalesce (List <PropertyMetadataModel > propertyMetadata) {
118+ if (propertyMetadata.isEmpty) return ;
119+
120+ var firstSeenIdxMap = < String , int > {};
121+ firstSeenIdxMap[propertyMetadata[0 ].name] = 0 ;
122+ var i = 1 ;
123+ while (i < propertyMetadata.length) {
124+ var propName = propertyMetadata[i].name;
125+ if (firstSeenIdxMap.containsKey (propName)) {
126+ var propNameIdx = firstSeenIdxMap[propName];
127+ // We have seen this name before, combine the metadata lists.
128+ propertyMetadata[propNameIdx]
129+ .annotations
130+ .addAll (propertyMetadata[i].annotations);
131+ // Remove the higher index, okay since we directly check `length` above.
132+ propertyMetadata.removeAt (i);
133+ } else {
134+ firstSeenIdxMap[propName] = i;
135+ ++ i;
136+ }
137+ }
138+ }
139+
98140 @override
99141 ReflectionInfoModel visitFunctionDeclaration (FunctionDeclaration node) {
100142 if (! node.metadata
@@ -126,6 +168,53 @@ class ReflectionInfoVisitor extends RecursiveAstVisitor<ReflectionInfoModel> {
126168 }
127169}
128170
171+ /// Visitor responsible for parsing [ClassMember] s into
172+ /// [PropertyMetadataModel] s.
173+ class _PropertyMetadataVisitor
174+ extends SimpleAstVisitor <List <PropertyMetadataModel >> {
175+ final AnnotationVisitor _annotationVisitor;
176+
177+ _PropertyMetadataVisitor (this ._annotationVisitor);
178+
179+ @override
180+ List <PropertyMetadataModel > visitFieldDeclaration (FieldDeclaration node) {
181+ var retVal = null ;
182+ for (var variable in node.fields.variables) {
183+ var propModel = new PropertyMetadataModel ()..name = '${variable .name }' ;
184+ for (var meta in node.metadata) {
185+ var annotationModel = meta.accept (_annotationVisitor);
186+ if (annotationModel != null ) {
187+ propModel.annotations.add (annotationModel);
188+ }
189+ }
190+ if (propModel.annotations.isNotEmpty) {
191+ if (retVal == null ) {
192+ retVal = < PropertyMetadataModel > [];
193+ }
194+ retVal.add (propModel);
195+ }
196+ }
197+ return retVal;
198+ }
199+
200+ @override
201+ List <PropertyMetadataModel > visitMethodDeclaration (MethodDeclaration node) {
202+ if (node.isGetter || node.isSetter) {
203+ var propModel = new PropertyMetadataModel ()..name = '${node .name }' ;
204+ for (var meta in node.metadata) {
205+ var annotationModel = meta.accept (_annotationVisitor);
206+ if (annotationModel != null ) {
207+ propModel.annotations.add (annotationModel);
208+ }
209+ }
210+ if (propModel.annotations.isNotEmpty) {
211+ return < PropertyMetadataModel > [propModel];
212+ }
213+ }
214+ return null ;
215+ }
216+ }
217+
129218/// Defines the format in which an [ReflectionInfoModel] is expressed as Dart
130219/// code in a `.ng_deps.dart` file.
131220abstract class ReflectionWriterMixin
@@ -171,9 +260,25 @@ abstract class ReflectionWriterMixin
171260 _writeListWithSeparator (model.parameters, writeParameterModelForImpl,
172261 prefix: '(' , suffix: ')' );
173262 // Interfaces
263+ var hasPropertyMetadata =
264+ model.propertyMetadata != null && model.propertyMetadata.isNotEmpty;
174265 if (model.interfaces != null && model.interfaces.isNotEmpty) {
175266 _writeListWithSeparator (model.interfaces, buffer.write,
176267 prefix: ',\n const [' , suffix: ']' );
268+ } else if (hasPropertyMetadata) {
269+ buffer.write (',\n const []' );
270+ }
271+ // Property Metadata
272+ if (hasPropertyMetadata) {
273+ buffer.write (',\n const {' );
274+ for (var propMeta in model.propertyMetadata) {
275+ if (propMeta != model.propertyMetadata.first) {
276+ buffer.write (', ' );
277+ }
278+ _writeListWithSeparator (propMeta.annotations, writeAnnotationModel,
279+ prefix: "\n '${sanitize (propMeta .name )}': const [" , suffix: ']' );
280+ }
281+ buffer.write ('}' );
177282 }
178283 }
179284 buffer.writeln (')\n )' );
0 commit comments