@@ -3,6 +3,7 @@ library angular2.transform.directive_processor.visitors;
33import 'dart:async' ;
44import 'package:analyzer/analyzer.dart' ;
55import 'package:analyzer/src/generated/java_core.dart' ;
6+ import 'package:angular2/annotations.dart' show LifecycleEvent;
67import 'package:angular2/src/render/xhr.dart' show XHR;
78import 'package:angular2/src/transform/common/annotation_matcher.dart' ;
89import 'package:angular2/src/transform/common/async_string_writer.dart' ;
@@ -216,9 +217,11 @@ class AnnotationsTransformVisitor extends ToSourceVisitor {
216217 final AssetId _assetId;
217218 final bool _inlineViews;
218219 final ConstantEvaluator _evaluator = new ConstantEvaluator ();
220+ final Set <String > _ifaceLifecycleEntries = new Set <String >();
221+ bool _isLifecycleWritten = false ;
219222 bool _isProcessingView = false ;
220223 bool _isProcessingDirective = false ;
221- String _lifecycleValue = null ;
224+ String _ifaceLifecyclePrefix = '' ;
222225
223226 AnnotationsTransformVisitor (AsyncStringWriter writer, this ._xhr,
224227 this ._annotationMatcher, this ._interfaceMatcher, this ._assetId,
@@ -231,12 +234,10 @@ class AnnotationsTransformVisitor extends ToSourceVisitor {
231234 /// populates `_lifecycleValue` with the appropriate values if so. If none are
232235 /// present, `_lifecycleValue` is not modified.
233236 void _populateLifecycleValue (ClassDeclaration node) {
234- var lifecycleEntries = [];
235- var prefix = '' ;
236237 var populateImport = (Identifier name) {
237- if (prefix .isNotEmpty) return ;
238+ if (_ifaceLifecyclePrefix .isNotEmpty) return ;
238239 var import = _interfaceMatcher.getMatchingImport (name, _assetId);
239- prefix =
240+ _ifaceLifecyclePrefix =
240241 import != null && import.prefix != null ? '${import .prefix }.' : '' ;
241242 };
242243
@@ -254,30 +255,32 @@ class AnnotationsTransformVisitor extends ToSourceVisitor {
254255
255256 namesToTest.forEach ((name) {
256257 if (_interfaceMatcher.isOnChange (name, _assetId)) {
257- lifecycleEntries .add ('onChange' );
258+ _ifaceLifecycleEntries .add ('${ LifecycleEvent . onChange } ' );
258259 populateImport (name);
259260 }
260261 if (_interfaceMatcher.isOnDestroy (name, _assetId)) {
261- lifecycleEntries .add ('onDestroy' );
262+ _ifaceLifecycleEntries .add ('${ LifecycleEvent . onDestroy } ' );
262263 populateImport (name);
263264 }
264265 if (_interfaceMatcher.isOnCheck (name, _assetId)) {
265- lifecycleEntries .add ('onCheck' );
266+ _ifaceLifecycleEntries .add ('${ LifecycleEvent . onCheck } ' );
266267 populateImport (name);
267268 }
268269 if (_interfaceMatcher.isOnInit (name, _assetId)) {
269- lifecycleEntries .add ('onInit' );
270+ _ifaceLifecycleEntries .add ('${ LifecycleEvent . onInit } ' );
270271 populateImport (name);
271272 }
272273 if (_interfaceMatcher.isOnAllChangesDone (name, _assetId)) {
273- lifecycleEntries .add ('onAllChangesDone' );
274+ _ifaceLifecycleEntries .add ('${ LifecycleEvent . onAllChangesDone } ' );
274275 populateImport (name);
275276 }
276277 });
277- if (lifecycleEntries.isNotEmpty) {
278- _lifecycleValue = 'const [${prefix }LifecycleEvent.'
279- '${lifecycleEntries .join (", ${prefix }LifecycleEvent." )}]' ;
280- }
278+ }
279+
280+ void _resetState () {
281+ _isLifecycleWritten = _isProcessingView = _isProcessingDirective = false ;
282+ _ifaceLifecycleEntries.clear ();
283+ _ifaceLifecyclePrefix = '' ;
281284 }
282285
283286 @override
@@ -294,7 +297,7 @@ class AnnotationsTransformVisitor extends ToSourceVisitor {
294297 }
295298 writer.print (']' );
296299
297- _lifecycleValue = null ;
300+ _resetState () ;
298301 return null ;
299302 }
300303
@@ -322,59 +325,110 @@ class AnnotationsTransformVisitor extends ToSourceVisitor {
322325 }
323326 args[i].accept (this );
324327 }
325- if (_lifecycleValue != null && _isProcessingDirective) {
326- writer.print (''', lifecycle: $_lifecycleValue ''' );
328+ if (! _isLifecycleWritten && _isProcessingDirective) {
329+ var lifecycleValue = _getLifecycleValue ();
330+ if (lifecycleValue.isNotEmpty) {
331+ writer.print (', lifecycle: $lifecycleValue ' );
332+ _isLifecycleWritten = true ;
333+ }
327334 }
328335 writer.print (')' );
329336 }
330337 return null ;
331338 }
332339
340+ String _getLifecycleValue () {
341+ if (_ifaceLifecycleEntries.isNotEmpty) {
342+ var entries = _ifaceLifecycleEntries.toList ();
343+ entries.sort ();
344+ return 'const [${_ifaceLifecyclePrefix }'
345+ '${entries .join (", ${_ifaceLifecyclePrefix }" )}]' ;
346+ }
347+ return '' ;
348+ }
349+
333350 /// These correspond to the annotation parameters.
334351 @override
335352 Object visitNamedExpression (NamedExpression node) {
353+ if (! _isProcessingView && ! _isProcessingDirective) {
354+ return super .visitNamedExpression (node);
355+ }
336356 // TODO(kegluneq): Remove this limitation.
337- if (! _isProcessingView ||
338- node.name is ! Label ||
339- node.name.label is ! SimpleIdentifier ) {
357+ if (node.name is ! Label || node.name.label is ! SimpleIdentifier ) {
340358 return super .visitNamedExpression (node);
341359 }
342360 var keyString = '${node .name .label }' ;
343- if (_inlineViews) {
344- if (keyString == 'templateUrl' ) {
345- // Inline the templateUrl
346- var url = node.expression.accept (_evaluator);
361+ if (_isProcessingView && _inlineViews) {
362+ var isSuccess = this ._inlineView (keyString, node.expression);
363+ if (isSuccess) return null ;
364+ }
365+ if (_isProcessingDirective && keyString == 'lifecycle' ) {
366+ var isSuccess = _populateLifecycleFromNamedExpression (node.expression);
367+ if (isSuccess) {
368+ _isLifecycleWritten = true ;
369+ writer.print ('lifecycle: ${_getLifecycleValue ()}' );
370+ return null ;
371+ } else {
372+ logger.warning ('Failed to parse `lifecycle` value. '
373+ 'The following `LifecycleEvent`s may not be called: '
374+ '(${_ifaceLifecycleEntries .join (', ' )})' );
375+ _isLifecycleWritten = true ;
376+ // Do not return -- we will use the default processing here, maintaining
377+ // the original value for `lifecycle`.
378+ }
379+ }
380+ return super .visitNamedExpression (node);
381+ }
382+
383+ /// Populates the lifecycle values from explicitly declared values.
384+ /// Returns whether `node` was successfully processed.
385+ bool _populateLifecycleFromNamedExpression (AstNode node) {
386+ var nodeVal = node.toSource ();
387+ for (var evt in LifecycleEvent .values) {
388+ var evtStr = '$evt ' ;
389+ if (nodeVal.contains (evtStr)) {
390+ _ifaceLifecycleEntries.add (evtStr);
391+ }
392+ }
393+ return true ;
394+ }
395+
396+ /// Inlines the template and/or style refered to by `keyString` .
397+ /// Returns whether the `keyString` value was successfully processed.
398+ bool _inlineView (String keyString, AstNode node) {
399+ if (keyString == 'templateUrl' ) {
400+ // Inline the templateUrl
401+ var url = node.accept (_evaluator);
402+ if (url is String ) {
403+ writer.print ("template: r'''" );
404+ writer.asyncPrint (_readOrEmptyString (url));
405+ writer.print ("'''" );
406+
407+ // We keep the templateUrl in case the body of the template includes
408+ // relative urls that might be inlined later on (e.g. @import
409+ // directives or url() css values in style tags).
410+ writer.print (", templateUrl: r'$url '" );
411+ return true ;
412+ } else {
413+ logger.warning ('template url is not a String $url ' );
414+ }
415+ } else if (keyString == 'styleUrls' ) {
416+ // Inline the styleUrls
417+ var urls = node.accept (_evaluator);
418+ writer.print ('styles: const [' );
419+ for (var url in urls) {
347420 if (url is String ) {
348- writer.print ("template: r'''" );
421+ writer.print ("r'''" );
349422 writer.asyncPrint (_readOrEmptyString (url));
350- writer.print ("'''" );
351-
352- // We keep the templateUrl in case the body of the template includes
353- // relative urls that might be inlined later on (e.g. @import
354- // directives or url() css values in style tags).
355- writer.print (", templateUrl: r'$url '" );
356- return null ;
423+ writer.print ("''', " );
357424 } else {
358- logger.warning ('template url is not a String $url ' );
359- }
360- } else if (keyString == 'styleUrls' ) {
361- // Inline the styleUrls
362- var urls = node.expression.accept (_evaluator);
363- writer.print ('styles: const [' );
364- for (var url in urls) {
365- if (url is String ) {
366- writer.print ("r'''" );
367- writer.asyncPrint (_readOrEmptyString (url));
368- writer.print ("''', " );
369- } else {
370- logger.warning ('style url is not a String ${url }' );
371- }
425+ logger.warning ('style url is not a String ${url }' );
372426 }
373- writer.print (']' );
374- return null ;
375427 }
428+ writer.print (']' );
429+ return true ;
376430 }
377- return super . visitNamedExpression (node) ;
431+ return false ;
378432 }
379433
380434 /// Attempts to read the content from {@link url}, if it returns null then
0 commit comments