diff --git a/lib/core/annotation_src.dart b/lib/core/annotation_src.dart index a8c1fb043..42a0febf3 100644 --- a/lib/core/annotation_src.dart +++ b/lib/core/annotation_src.dart @@ -6,19 +6,18 @@ RegExp _ATTR_NAME = new RegExp(r'\[([^\]]+)\]$'); const String SHADOW_DOM_INJECTOR_NAME = 'SHADOW_INJECTOR'; -skipShadow(Injector injector) - => injector.name == SHADOW_DOM_INJECTOR_NAME ? injector.parent : injector; +skipShadow(Injector injector) => + injector.name == SHADOW_DOM_INJECTOR_NAME ? injector.parent : injector; -localVisibility (Injector requesting, Injector defining) - => identical(skipShadow(requesting), defining); +localVisibility (Injector requesting, Injector defining) => + identical(skipShadow(requesting), defining); directChildrenVisibility(Injector requesting, Injector defining) { requesting = skipShadow(requesting); return identical(requesting.parent, defining) || localVisibility(requesting, defining); } -Directive cloneWithNewMap(Directive annotation, map) - => annotation._cloneWithNewMap(map); +Directive cloneWithNewMap(Directive annotation, map) => annotation._cloneWithNewMap(map); String mappingSpec(DirectiveAnnotation annotation) => annotation._mappingSpec; @@ -223,9 +222,11 @@ abstract class Directive { this.exportExpressionAttrs: const [] }); - toString() => selector; - get hashCode => selector.hashCode; - operator==(other) => + String toString() => selector; + + int get hashCode => selector.hashCode; + + bool operator==(other) => other is Directive && selector == other.selector; Directive _cloneWithNewMap(newMap); diff --git a/lib/core/formatter.dart b/lib/core/formatter.dart index 99fa15f5d..3bb3b99d4 100644 --- a/lib/core/formatter.dart +++ b/lib/core/formatter.dart @@ -8,10 +8,10 @@ part of angular.core_internal; class FormatterMap extends AnnotationMap { Injector _injector; FormatterMap(Injector injector, MetadataExtractor extractMetadata) - : this._injector = injector, + : _injector = injector, super(injector, extractMetadata); - call(String name) { + Function call(String name) { var formatter = new Formatter(name: name); var formatterType = this[formatter]; return _injector.get(formatterType); diff --git a/lib/core/registry.dart b/lib/core/registry.dart index 9d66de3ed..f993696a5 100644 --- a/lib/core/registry.dart +++ b/lib/core/registry.dart @@ -2,8 +2,14 @@ library angular.core.registry; import 'package:di/di.dart' show Injector; +/** + * The [AnnotationMap] maps annotations to [Type]s. + * + * The [AnnotationMap] contains all annotated [Type]s provided by the [Injector] passed as a + * constructor argument. Every single annotation maps to only one [Type]. + */ abstract class AnnotationMap { - final Map _map = {}; + final _map = {}; AnnotationMap(Injector injector, MetadataExtractor extractMetadata) { injector.types.forEach((type) { @@ -15,16 +21,19 @@ abstract class AnnotationMap { }); } + /// Returns the [Type] annotated with [annotation]. Type operator[](K annotation) { var value = _map[annotation]; if (value == null) throw 'No $annotation found!'; return value; } - void forEach(fn(K, Type)) { - _map.forEach(fn); + /// Executes the [function] for all registered annotations. + void forEach(function(K, Type)) { + _map.forEach(function); } + /// Returns a list of all the annotations applied to the [Type]. List annotationsFor(Type type) { final res = []; forEach((ann, annType) { @@ -34,37 +43,47 @@ abstract class AnnotationMap { } } +/** + * The [AnnotationsMap] maps annotations to [Type]s. + * + * The [AnnotationsMap] contains all annotated [Type]s provided by the [Injector] + * given in the constructor argument. Every single annotation can maps to only + * multiple [Type]s. + */ abstract class AnnotationsMap { - final Map> map = {}; + final _map = >{}; AnnotationsMap(Injector injector, MetadataExtractor extractMetadata) { injector.types.forEach((type) { extractMetadata(type) .where((annotation) => annotation is K) .forEach((annotation) { - map.putIfAbsent(annotation, () => []).add(type); + _map.putIfAbsent(annotation, () => []).add(type); }); }); } - List operator[](K annotation) { - var value = map[annotation]; + /// Returns a list of [Type]s annotated with [annotation]. + List operator[](K annotation) { + var value = _map[annotation]; if (value == null) throw 'No $annotation found!'; return value; } - void forEach(fn(K, Type)) { - map.forEach((annotation, types) { + /// Executes the [function] for all registered (annotation, type) pairs. + void forEach(function(K, Type)) { + _map.forEach((K annotation, List types) { types.forEach((type) { - fn(annotation, type); + function(annotation, type); }); }); } + /// Returns a list of all the annotations applied to the [Type]. List annotationsFor(Type type) { var res = []; - forEach((ann, annType) { - if (annType == type) res.add(ann); + _map.forEach((K annotation, List types) { + if (types.contains(type)) res.add(annotation); }); return res; } diff --git a/lib/core/registry_dynamic.dart b/lib/core/registry_dynamic.dart index 54ea6573d..18994f385 100644 --- a/lib/core/registry_dynamic.dart +++ b/lib/core/registry_dynamic.dart @@ -7,9 +7,9 @@ import 'package:angular/core/registry.dart'; export 'package:angular/core/registry.dart' show MetadataExtractor; -var _fieldMetadataCache = new Map>(); - class DynamicMetadataExtractor implements MetadataExtractor { + static final _fieldMetadataCache = >{}; + final _fieldAnnotations = [ reflectType(NgAttr), reflectType(NgOneWay), @@ -21,71 +21,66 @@ class DynamicMetadataExtractor implements MetadataExtractor { Iterable call(Type type) { if (reflectType(type) is TypedefMirror) return []; var metadata = reflectClass(type).metadata; - if (metadata == null) { - metadata = []; - } else { - metadata = metadata.map((InstanceMirror im) => map(type, im.reflectee)); - } - return metadata; + return metadata == null ? + [] : metadata.map((InstanceMirror im) => _mergeFieldAnnotations(type, im.reflectee)); } - map(Type type, obj) { - if (obj is Directive) { - return mapDirectiveAnnotation(type, obj); - } else { - return obj; - } - } - - Directive mapDirectiveAnnotation(Type type, Directive annotation) { + /** + * Merges the field annotations with the [AbstractNgAttrAnnotation.map] definition from the + * directive. + * + * [_mergeFieldAnnotations] throws when a field annotation has already been defined via + * [Directive.map] + */ + dynamic _mergeFieldAnnotations(Type type, annotation) { + if (annotation is! Directive) return annotation; var match; - var fieldMetadata = fieldMetadataExtractor(type); + var fieldMetadata = _fieldMetadataExtractor(type); if (fieldMetadata.isNotEmpty) { var newMap = annotation.map == null ? {} : new Map.from(annotation.map); fieldMetadata.forEach((String fieldName, DirectiveAnnotation ann) { var attrName = ann.attrName; if (newMap.containsKey(attrName)) { throw 'Mapping for attribute $attrName is already defined (while ' - 'processing annottation for field $fieldName of $type)'; + 'processing annottation for field $fieldName of $type)'; } - newMap[attrName] = '${mappingSpec(ann)}$fieldName'; + newMap[attrName] = mappingSpec(ann) + fieldName; }); annotation = cloneWithNewMap(annotation, newMap); } return annotation; } - - Map fieldMetadataExtractor(Type type) => - _fieldMetadataCache.putIfAbsent(type, () => _fieldMetadataExtractor(reflectType(type))); - - Map _fieldMetadataExtractor(ClassMirror cm) { - var fields = {}; - if(cm.superclass != null) { - fields.addAll(_fieldMetadataExtractor(cm.superclass)); - } else { - fields = {}; - } - Map declarations = cm.declarations; - declarations.forEach((symbol, dm) { - if(dm is VariableMirror || - dm is MethodMirror && (dm.isGetter || dm.isSetter)) { - var fieldName = MirrorSystem.getName(symbol); - if (dm is MethodMirror && dm.isSetter) { - // Remove "=" from the end of the setter. - fieldName = fieldName.substring(0, fieldName.length - 1); - } - dm.metadata.forEach((InstanceMirror meta) { - if (_fieldAnnotations.contains(meta.type)) { - if (fields.containsKey(fieldName)) { - throw 'Attribute annotation for $fieldName is defined more ' - 'than once in ${cm.reflectedType}'; - } - fields[fieldName] = meta.reflectee as DirectiveAnnotation; - } - }); + /// Extract metadata defined on fields via a [DirectiveAnnotation] + Map _fieldMetadataExtractor(Type type) { + if (!_fieldMetadataCache.containsKey(type)) { + var fields = {}; + ClassMirror cm = reflectType(type); + if (cm.superclass != null) { + fields.addAll(_fieldMetadataExtractor(cm.superclass.reflectedType)); } - }); - return fields; + Map declarations = cm.declarations; + declarations.forEach((symbol, dm) { + if (dm is VariableMirror || + dm is MethodMirror && (dm.isGetter || dm.isSetter)) { + var fieldName = MirrorSystem.getName(symbol); + if (dm is MethodMirror && dm.isSetter) { + // Remove "=" from the end of the setter. + fieldName = fieldName.substring(0, fieldName.length - 1); + } + dm.metadata.forEach((InstanceMirror meta) { + if (_fieldAnnotations.contains(meta.type)) { + if (fields.containsKey(fieldName)) { + throw 'Attribute annotation for $fieldName is defined more ' + 'than once in ${cm.reflectedType}'; + } + fields[fieldName] = meta.reflectee as DirectiveAnnotation; + } + }); + } + }); + _fieldMetadataCache[type] = fields; + } + return _fieldMetadataCache[type]; } } diff --git a/lib/core/registry_static.dart b/lib/core/registry_static.dart index fcb287b13..5e6042c19 100644 --- a/lib/core/registry_static.dart +++ b/lib/core/registry_static.dart @@ -6,7 +6,7 @@ import 'package:angular/core/registry.dart'; @Injectable() class StaticMetadataExtractor extends MetadataExtractor { final Map metadataMap; - final List empty = const []; + final empty = const []; StaticMetadataExtractor(this.metadataMap); diff --git a/lib/core/scope.dart b/lib/core/scope.dart index 145d55be9..4badeb50f 100644 --- a/lib/core/scope.dart +++ b/lib/core/scope.dart @@ -234,7 +234,7 @@ class Scope { } else if (expression.startsWith(':')) { expression = expression.substring(1); fn = (value, last) { - if (value != null) reactionFn(value, last); + if (value != null) reactionFn(value, last); }; } } diff --git a/lib/core_dom/common.dart b/lib/core_dom/common.dart index a60a050ee..5e3fd9974 100644 --- a/lib/core_dom/common.dart +++ b/lib/core_dom/common.dart @@ -1,8 +1,6 @@ part of angular.core.dom_internal; -List cloneElements(elements) { - return elements.map((el) => el.clone(true)).toList(); -} +List cloneElements(elements) => elements.map((el) => el.clone(true)).toList(); class MappingParts { final String attrName; @@ -18,7 +16,7 @@ class DirectiveRef { final Type type; final Directive annotation; final String value; - final mappings = new List(); + final mappings = []; DirectiveRef(this.element, this.type, this.annotation, [ this.value ]); diff --git a/lib/core_dom/directive_map.dart b/lib/core_dom/directive_map.dart index 0e92c21b2..24c21b78a 100644 --- a/lib/core_dom/directive_map.dart +++ b/lib/core_dom/directive_map.dart @@ -2,7 +2,7 @@ part of angular.core.dom_internal; @Injectable() class DirectiveMap extends AnnotationsMap { - DirectiveSelectorFactory _directiveSelectorFactory; + final DirectiveSelectorFactory _directiveSelectorFactory; DirectiveSelector _selector; DirectiveSelector get selector { if (_selector != null) return _selector; diff --git a/lib/core_dom/element_binder.dart b/lib/core_dom/element_binder.dart index 79be68c1c..da7289a38 100644 --- a/lib/core_dom/element_binder.dart +++ b/lib/core_dom/element_binder.dart @@ -110,7 +110,7 @@ class ElementBinder { } } - _bindOneWay(tasks, expression, scope, dstPathFn, controller, formatters) { + void _bindOneWay(tasks, expression, scope, dstPathFn, controller, formatters) { var taskId = tasks.registerTask(); Expression attrExprFn = _parser(expression); @@ -227,9 +227,8 @@ class ElementBinder { if (directive is AttachAware) { var taskId = tasks.registerTask(); Watch watch; - watch = scope.watch('1', // Cheat a bit. + watch = scope.watch('::1', // Cheat a bit. (_, __) { - watch.remove(); tasks.completeTask(taskId); }); } @@ -400,6 +399,7 @@ class TaggedTextBinder { final int offsetIndex; TaggedTextBinder(this.binder, this.offsetIndex); + String toString() => "[TaggedTextBinder binder:$binder offset:$offsetIndex]"; } diff --git a/lib/core_dom/node_cursor.dart b/lib/core_dom/node_cursor.dart index 5b6109a8b..ae71a7252 100644 --- a/lib/core_dom/node_cursor.dart +++ b/lib/core_dom/node_cursor.dart @@ -45,5 +45,5 @@ class NodeCursor { NodeCursor remove() => new NodeCursor([elements.removeAt(index)..remove()]); - toString() => "[NodeCursor: $elements $index]"; + String toString() => "[NodeCursor: $elements $index]"; } diff --git a/lib/core_dom/view.dart b/lib/core_dom/view.dart index c53309f7d..af9ca21be 100644 --- a/lib/core_dom/view.dart +++ b/lib/core_dom/view.dart @@ -37,8 +37,7 @@ class ViewPort { dom.Node previousNode = _lastNode(insertAfter); _viewsInsertAfter(view, insertAfter); - _animate.insert(view.nodes, placeholder.parentNode, - insertBefore: previousNode.nextNode); + _animate.insert(view.nodes, placeholder.parentNode, insertBefore: previousNode.nextNode); } void remove(View view) { @@ -51,8 +50,7 @@ class ViewPort { _views.remove(view); _viewsInsertAfter(view, moveAfter); - _animate.move(view.nodes, placeholder.parentNode, - insertBefore: previousNode.nextNode); + _animate.move(view.nodes, placeholder.parentNode, insertBefore: previousNode.nextNode); } void _viewsInsertAfter(View view, View insertAfter) { @@ -61,7 +59,5 @@ class ViewPort { } dom.Node _lastNode(View insertAfter) => - insertAfter == null - ? placeholder - : insertAfter.nodes.last; + insertAfter == null ? placeholder : insertAfter.nodes.last; } diff --git a/lib/core_dom/view_factory.dart b/lib/core_dom/view_factory.dart index 73936e356..4912a736c 100644 --- a/lib/core_dom/view_factory.dart +++ b/lib/core_dom/view_factory.dart @@ -41,8 +41,7 @@ class WalkingViewFactory implements ViewFactory { eb is ElementBinderTreeRef)); } - BoundViewFactory bind(Injector injector) => - new BoundViewFactory(this, injector); + BoundViewFactory bind(Injector injector) => new BoundViewFactory(this, injector); View call(Injector injector, [List nodes]) { if (nodes == null) nodes = cloneElements(templateElements); diff --git a/test/core/registry_spec.dart b/test/core/registry_spec.dart index 1a00d2af0..a96b41e8c 100644 --- a/test/core/registry_spec.dart +++ b/test/core/registry_spec.dart @@ -53,11 +53,13 @@ class MyMap extends AnnotationMap { class MyAnnotation { final String name; - const MyAnnotation(String this.name); + const MyAnnotation(this.name); - toString() => name; - get hashCode => name.hashCode; - operator==(other) => this.name == other.name; + String toString() => name; + + int get hashCode => name.hashCode; + + bool operator==(other) => name == other.name; } @MyAnnotation('A') @MyAnnotation('B') class A1 {}