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

Unified 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, 12 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 side-by-side diff with in-line comments
Download patch
Index: third_party/pkg/angular/lib/tools/source_metadata_extractor.dart
diff --git a/third_party/pkg/angular/lib/tools/source_metadata_extractor.dart b/third_party/pkg/angular/lib/tools/source_metadata_extractor.dart
new file mode 100644
index 0000000000000000000000000000000000000000..897c30d79a7c5b465daa78cae3fa905524cc73ac
--- /dev/null
+++ b/third_party/pkg/angular/lib/tools/source_metadata_extractor.dart
@@ -0,0 +1,217 @@
+library angular.source_metadata_extractor ;
+
+import 'package:analyzer/src/generated/ast.dart';
+
+import 'source_crawler.dart';
+import '../utils.dart';
+import 'common.dart';
+
+const String _COMPONENT = '-component';
+const String _DIRECTIVE = '-directive';
+String _ATTR_DIRECTIVE = '-attr' + _DIRECTIVE;
+RegExp _ATTR_SELECTOR_REGEXP = new RegExp(r'\[([^\]]+)\]');
+const List<String> _specs = const ['=>!', '=>', '<=>', '@', '&'];
+const Map<String, String> _attrAnnotationsToSpec = const {
+ 'NgAttr': '@',
+ 'NgOneWay': '=>',
+ 'NgOneWayOneTime': '=>!',
+ 'NgTwoWay': '<=>',
+ 'NgCallback': '&'
+};
+
+class SourceMetadataExtractor {
+ SourceCrawler sourceCrawler;
+ DirectiveMetadataCollectingVisitor metadataVisitor;
+
+ SourceMetadataExtractor(this.sourceCrawler, [ this.metadataVisitor ]) {
+ if (metadataVisitor == null) {
+ metadataVisitor = new DirectiveMetadataCollectingVisitor();
+ }
+ }
+
+ List<DirectiveInfo> gatherDirectiveInfo(root) {
+ sourceCrawler.crawl(root, metadataVisitor);
+
+ List<DirectiveInfo> directives = <DirectiveInfo>[];
+ metadataVisitor.metadata.forEach((DirectiveMetadata meta) {
+ DirectiveInfo dirInfo = new DirectiveInfo();
+ dirInfo.selector = meta.selector;
+ dirInfo.template = meta.template;
+ meta.attributeMappings.forEach((attrName, mappingSpec) {
+ var spec = _specs
+ .firstWhere((specPrefix) => mappingSpec.startsWith(specPrefix),
+ orElse: () => throw '$mappingSpec no matching spec');
+ if (spec != '@') {
+ dirInfo.expressionAttrs.add(snakecase(attrName));
+ }
+ if (mappingSpec.length == 1) { // Shorthand. Remove.
+ // TODO(pavelgj): Figure out if short-hand LHS should be expanded
+ // and added to the expressions list.
+ if (attrName != '.') {
+ dirInfo.expressions.add(_maybeCamelCase(attrName));
+ }
+ } else {
+ mappingSpec = mappingSpec.substring(spec.length);
+ if (mappingSpec.startsWith('.')) {
+ mappingSpec = mappingSpec.substring(1);
+ }
+ dirInfo.expressions.add(mappingSpec);
+ }
+ });
+
+ meta.exportExpressionAttrs.forEach((attr) {
+ attr = snakecase(attr);
+ if (!dirInfo.expressionAttrs.contains(attr)) {
+ dirInfo.expressionAttrs.add(attr);
+ }
+ });
+
+ meta.exportExpressions.forEach((expr) {
+ if (!dirInfo.expressions.contains(expr)) {
+ dirInfo.expressions.add(expr);
+ }
+ });
+
+
+ // No explicit selector specified on the directive, compute one.
+ var className = snakecase(meta.className);
+ if (dirInfo.selector == null) {
+ if (meta.type == COMPONENT) {
+ if (className.endsWith(_COMPONENT)) {
+ dirInfo.selector = className.
+ substring(0, className.length - _COMPONENT.length);
+ } else {
+ throw "Directive name '$className' must end with $_DIRECTIVE, "
+ "$_ATTR_DIRECTIVE, $_COMPONENT or have a \$selector field.";
+ }
+ } else {
+ if (className.endsWith(_ATTR_DIRECTIVE)) {
+ var attrName = className.
+ substring(0, className.length - _ATTR_DIRECTIVE.length);
+ dirInfo.selector = '[$attrName]';
+ } else if (className.endsWith(_DIRECTIVE)) {
+ dirInfo.selector = className.
+ substring(0, className.length - _DIRECTIVE.length);
+ } else {
+ throw "Directive name '$className' must end with $_DIRECTIVE, "
+ "$_ATTR_DIRECTIVE, $_COMPONENT or have a \$selector field.";
+ }
+ }
+ }
+ var reprocessedAttrs = <String>[];
+ dirInfo.expressionAttrs.forEach((String attr) {
+ if (attr == '.') {
+ var matches = _ATTR_SELECTOR_REGEXP.allMatches(dirInfo.selector);
+ if (matches.length > 0) {
+ reprocessedAttrs.add(matches.last.group(1));
+ }
+ } else {
+ reprocessedAttrs.add(attr);
+ }
+ });
+ dirInfo.expressionAttrs = reprocessedAttrs;
+ directives.add(dirInfo);
+
+ });
+
+ return directives;
+ }
+}
+
+String _maybeCamelCase(String s) => (s.indexOf('-') > -1) ? camelcase(s) : s;
+
+class DirectiveMetadataCollectingVisitor {
+ List<DirectiveMetadata> metadata = <DirectiveMetadata>[];
+
+ call(CompilationUnit cu) {
+ cu.declarations.forEach((CompilationUnitMember declaration) {
+ // We only care about classes.
+ if (declaration is! ClassDeclaration) return;
+ ClassDeclaration clazz = declaration;
+ // Check class annotations for presense of NgComponent/NgDirective.
+ DirectiveMetadata meta;
+ clazz.metadata.forEach((Annotation ann) {
+ if (ann.arguments == null) return; // Ignore non-class annotations.
+ // TODO(pavelj): this is not a safe check for the type of the
+ // annotations, but good enough for now.
+ if (ann.name.name != 'NgComponent'
+ && ann.name.name != 'NgDirective') return;
+
+ bool isComponent = ann.name.name == 'NgComponent';
+
+ meta = new DirectiveMetadata()
+ ..className = clazz.name.name
+ ..type = isComponent ? COMPONENT : DIRECTIVE;
+ metadata.add(meta);
+
+ ann.arguments.arguments.forEach((Expression arg) {
+ if (arg is NamedExpression) {
+ NamedExpression namedArg = arg;
+ var paramName = namedArg.name.label.name;
+ if (paramName == 'selector') {
+ meta.selector = assertString(namedArg.expression).stringValue;
+ }
+ if (paramName == 'template') {
+ meta.template = assertString(namedArg.expression).stringValue;
+ }
+ if (paramName == 'map') {
+ MapLiteral map = namedArg.expression;
+ map.entries.forEach((MapLiteralEntry entry) {
+ meta.attributeMappings[assertString(entry.key).stringValue] =
+ assertString(entry.value).stringValue;
+ });
+ }
+ if (paramName == 'exportExpressions') {
+ meta.exportExpressions = getStringValues(namedArg.expression);
+ }
+ if (paramName == 'exportExpressionAttrs') {
+ meta.exportExpressionAttrs = getStringValues(namedArg.expression);
+ }
+ }
+ });
+ });
+
+ // Check fields/getters/setter for presense of attr mapping annotations.
+ if (meta != null) {
+ clazz.members.forEach((ClassMember member) {
+ if (member is FieldDeclaration ||
+ (member is MethodDeclaration &&
+ (member.isSetter || member.isGetter))) {
+ member.metadata.forEach((Annotation ann) {
+ if (_attrAnnotationsToSpec.containsKey(ann.name.name)) {
+ String fieldName;
+ if (member is FieldDeclaration) {
+ fieldName = member.fields.variables.first.name.name;
+ } else { // MethodDeclaration
+ fieldName = (member as MethodDeclaration).name.name;
+ }
+ StringLiteral attNameLiteral = ann.arguments.arguments.first;
+ if (meta.attributeMappings
+ .containsKey(attNameLiteral.stringValue)) {
+ throw 'Attribute mapping already defined for $fieldName';
+ }
+ meta.attributeMappings[attNameLiteral.stringValue] =
+ _attrAnnotationsToSpec[ann.name.name] + fieldName;
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+}
+
+List<String> getStringValues(ListLiteral listLiteral) {
+ List<String> res = <String>[];
+ for (Expression element in listLiteral.elements) {
+ res.add(assertString(element).stringValue);
+ }
+ return res;
+}
+
+StringLiteral assertString(Expression key) {
+ if (key is! StringLiteral) {
+ throw 'must be a string literal: ${key.runtimeType}';
+ }
+ return key;
+}

Powered by Google App Engine
This is Rietveld 408576698