Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(254)

Side by Side Diff: third_party/pkg/angular/lib/tools/source_metadata_extractor.dart

Issue 124053002: Adding Angular and dependent packages for testing (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 library angular.source_metadata_extractor ;
2
3 import 'package:analyzer/src/generated/ast.dart';
4
5 import 'source_crawler.dart';
6 import '../utils.dart';
7 import 'common.dart';
8
9 const String _COMPONENT = '-component';
10 const String _DIRECTIVE = '-directive';
11 String _ATTR_DIRECTIVE = '-attr' + _DIRECTIVE;
12 RegExp _ATTR_SELECTOR_REGEXP = new RegExp(r'\[([^\]]+)\]');
13 const List<String> _specs = const ['=>!', '=>', '<=>', '@', '&'];
14 const Map<String, String> _attrAnnotationsToSpec = const {
15 'NgAttr': '@',
16 'NgOneWay': '=>',
17 'NgOneWayOneTime': '=>!',
18 'NgTwoWay': '<=>',
19 'NgCallback': '&'
20 };
21
22 class SourceMetadataExtractor {
23 SourceCrawler sourceCrawler;
24 DirectiveMetadataCollectingVisitor metadataVisitor;
25
26 SourceMetadataExtractor(this.sourceCrawler, [ this.metadataVisitor ]) {
27 if (metadataVisitor == null) {
28 metadataVisitor = new DirectiveMetadataCollectingVisitor();
29 }
30 }
31
32 List<DirectiveInfo> gatherDirectiveInfo(root) {
33 sourceCrawler.crawl(root, metadataVisitor);
34
35 List<DirectiveInfo> directives = <DirectiveInfo>[];
36 metadataVisitor.metadata.forEach((DirectiveMetadata meta) {
37 DirectiveInfo dirInfo = new DirectiveInfo();
38 dirInfo.selector = meta.selector;
39 dirInfo.template = meta.template;
40 meta.attributeMappings.forEach((attrName, mappingSpec) {
41 var spec = _specs
42 .firstWhere((specPrefix) => mappingSpec.startsWith(specPrefix),
43 orElse: () => throw '$mappingSpec no matching spec');
44 if (spec != '@') {
45 dirInfo.expressionAttrs.add(snakecase(attrName));
46 }
47 if (mappingSpec.length == 1) { // Shorthand. Remove.
48 // TODO(pavelgj): Figure out if short-hand LHS should be expanded
49 // and added to the expressions list.
50 if (attrName != '.') {
51 dirInfo.expressions.add(_maybeCamelCase(attrName));
52 }
53 } else {
54 mappingSpec = mappingSpec.substring(spec.length);
55 if (mappingSpec.startsWith('.')) {
56 mappingSpec = mappingSpec.substring(1);
57 }
58 dirInfo.expressions.add(mappingSpec);
59 }
60 });
61
62 meta.exportExpressionAttrs.forEach((attr) {
63 attr = snakecase(attr);
64 if (!dirInfo.expressionAttrs.contains(attr)) {
65 dirInfo.expressionAttrs.add(attr);
66 }
67 });
68
69 meta.exportExpressions.forEach((expr) {
70 if (!dirInfo.expressions.contains(expr)) {
71 dirInfo.expressions.add(expr);
72 }
73 });
74
75
76 // No explicit selector specified on the directive, compute one.
77 var className = snakecase(meta.className);
78 if (dirInfo.selector == null) {
79 if (meta.type == COMPONENT) {
80 if (className.endsWith(_COMPONENT)) {
81 dirInfo.selector = className.
82 substring(0, className.length - _COMPONENT.length);
83 } else {
84 throw "Directive name '$className' must end with $_DIRECTIVE, "
85 "$_ATTR_DIRECTIVE, $_COMPONENT or have a \$selector field.";
86 }
87 } else {
88 if (className.endsWith(_ATTR_DIRECTIVE)) {
89 var attrName = className.
90 substring(0, className.length - _ATTR_DIRECTIVE.length);
91 dirInfo.selector = '[$attrName]';
92 } else if (className.endsWith(_DIRECTIVE)) {
93 dirInfo.selector = className.
94 substring(0, className.length - _DIRECTIVE.length);
95 } else {
96 throw "Directive name '$className' must end with $_DIRECTIVE, "
97 "$_ATTR_DIRECTIVE, $_COMPONENT or have a \$selector field.";
98 }
99 }
100 }
101 var reprocessedAttrs = <String>[];
102 dirInfo.expressionAttrs.forEach((String attr) {
103 if (attr == '.') {
104 var matches = _ATTR_SELECTOR_REGEXP.allMatches(dirInfo.selector);
105 if (matches.length > 0) {
106 reprocessedAttrs.add(matches.last.group(1));
107 }
108 } else {
109 reprocessedAttrs.add(attr);
110 }
111 });
112 dirInfo.expressionAttrs = reprocessedAttrs;
113 directives.add(dirInfo);
114
115 });
116
117 return directives;
118 }
119 }
120
121 String _maybeCamelCase(String s) => (s.indexOf('-') > -1) ? camelcase(s) : s;
122
123 class DirectiveMetadataCollectingVisitor {
124 List<DirectiveMetadata> metadata = <DirectiveMetadata>[];
125
126 call(CompilationUnit cu) {
127 cu.declarations.forEach((CompilationUnitMember declaration) {
128 // We only care about classes.
129 if (declaration is! ClassDeclaration) return;
130 ClassDeclaration clazz = declaration;
131 // Check class annotations for presense of NgComponent/NgDirective.
132 DirectiveMetadata meta;
133 clazz.metadata.forEach((Annotation ann) {
134 if (ann.arguments == null) return; // Ignore non-class annotations.
135 // TODO(pavelj): this is not a safe check for the type of the
136 // annotations, but good enough for now.
137 if (ann.name.name != 'NgComponent'
138 && ann.name.name != 'NgDirective') return;
139
140 bool isComponent = ann.name.name == 'NgComponent';
141
142 meta = new DirectiveMetadata()
143 ..className = clazz.name.name
144 ..type = isComponent ? COMPONENT : DIRECTIVE;
145 metadata.add(meta);
146
147 ann.arguments.arguments.forEach((Expression arg) {
148 if (arg is NamedExpression) {
149 NamedExpression namedArg = arg;
150 var paramName = namedArg.name.label.name;
151 if (paramName == 'selector') {
152 meta.selector = assertString(namedArg.expression).stringValue;
153 }
154 if (paramName == 'template') {
155 meta.template = assertString(namedArg.expression).stringValue;
156 }
157 if (paramName == 'map') {
158 MapLiteral map = namedArg.expression;
159 map.entries.forEach((MapLiteralEntry entry) {
160 meta.attributeMappings[assertString(entry.key).stringValue] =
161 assertString(entry.value).stringValue;
162 });
163 }
164 if (paramName == 'exportExpressions') {
165 meta.exportExpressions = getStringValues(namedArg.expression);
166 }
167 if (paramName == 'exportExpressionAttrs') {
168 meta.exportExpressionAttrs = getStringValues(namedArg.expression);
169 }
170 }
171 });
172 });
173
174 // Check fields/getters/setter for presense of attr mapping annotations.
175 if (meta != null) {
176 clazz.members.forEach((ClassMember member) {
177 if (member is FieldDeclaration ||
178 (member is MethodDeclaration &&
179 (member.isSetter || member.isGetter))) {
180 member.metadata.forEach((Annotation ann) {
181 if (_attrAnnotationsToSpec.containsKey(ann.name.name)) {
182 String fieldName;
183 if (member is FieldDeclaration) {
184 fieldName = member.fields.variables.first.name.name;
185 } else { // MethodDeclaration
186 fieldName = (member as MethodDeclaration).name.name;
187 }
188 StringLiteral attNameLiteral = ann.arguments.arguments.first;
189 if (meta.attributeMappings
190 .containsKey(attNameLiteral.stringValue)) {
191 throw 'Attribute mapping already defined for $fieldName';
192 }
193 meta.attributeMappings[attNameLiteral.stringValue] =
194 _attrAnnotationsToSpec[ann.name.name] + fieldName;
195 }
196 });
197 }
198 });
199 }
200 });
201 }
202 }
203
204 List<String> getStringValues(ListLiteral listLiteral) {
205 List<String> res = <String>[];
206 for (Expression element in listLiteral.elements) {
207 res.add(assertString(element).stringValue);
208 }
209 return res;
210 }
211
212 StringLiteral assertString(Expression key) {
213 if (key is! StringLiteral) {
214 throw 'must be a string literal: ${key.runtimeType}';
215 }
216 return key;
217 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698