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

Unified Diff: packages/intl/lib/extract_messages.dart

Issue 2989763002: Update charted to 0.4.8 and roll (Closed)
Patch Set: Removed Cutch from list of reviewers Created 3 years, 5 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
« no previous file with comments | « packages/intl/lib/date_time_patterns.dart ('k') | packages/intl/lib/generate_localized.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: packages/intl/lib/extract_messages.dart
diff --git a/packages/intl/lib/extract_messages.dart b/packages/intl/lib/extract_messages.dart
deleted file mode 100644
index 6bc7032e2f3203b6c24e8e5ba8e77ba73d50f3b0..0000000000000000000000000000000000000000
--- a/packages/intl/lib/extract_messages.dart
+++ /dev/null
@@ -1,512 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/**
- * This is for use in extracting messages from a Dart program
- * using the Intl.message() mechanism and writing them to a file for
- * translation. This provides only the stub of a mechanism, because it
- * doesn't define how the file should be written. It provides an
- * [IntlMessage] class that holds the extracted data and [parseString]
- * and [parseFile] methods which
- * can extract messages that conform to the expected pattern:
- * (parameters) => Intl.message("Message $parameters", desc: ...);
- * It uses the analyzer package to do the parsing, so may
- * break if there are changes to the API that it provides.
- * An example can be found in test/message_extraction/extract_to_json.dart
- *
- * Note that this does not understand how to follow part directives, so it
- * has to explicitly be given all the files that it needs. A typical use case
- * is to run it on all .dart files in a directory.
- */
-library extract_messages;
-
-import 'dart:io';
-
-import 'package:analyzer/analyzer.dart';
-import 'package:intl/src/intl_message.dart';
-
-/**
- * If this is true, print warnings for skipped messages. Otherwise, warnings
- * are suppressed.
- */
-bool suppressWarnings = false;
-
-/**
- * If this is true, then treat all warnings as errors.
- */
-bool warningsAreErrors = false;
-
-/**
- * This accumulates a list of all warnings/errors we have found. These are
- * saved as strings right now, so all that can really be done is print and
- * count them.
- */
-List<String> warnings = [];
-
-/** Were there any warnings or errors in extracting messages. */
-bool get hasWarnings => warnings.isNotEmpty;
-
-/** Are plural and gender expressions required to be at the top level
- * of an expression, or are they allowed to be embedded in string literals.
- *
- * For example, the following expression
- * 'There are ${Intl.plural(...)} items'.
- * is legal if [allowEmbeddedPluralsAndGenders] is true, but illegal
- * if [allowEmbeddedPluralsAndGenders] is false.
- */
-bool allowEmbeddedPluralsAndGenders = true;
-
-/**
- * Parse the source of the Dart program file [file] and return a Map from
- * message names to [IntlMessage] instances.
- */
-Map<String, MainMessage> parseFile(File file) {
- try {
- _root = parseDartFile(file.path);
- } on AnalyzerErrorGroup catch (e) {
- print("Error in parsing ${file.path}, no messages extracted.");
- print(" $e");
- return {};
- }
- _origin = file.path;
- var visitor = new MessageFindingVisitor();
- _root.accept(visitor);
- return visitor.messages;
-}
-
-/**
- * The root of the compilation unit, and the first node we visit. We hold
- * on to this for error reporting, as it can give us line numbers of other
- * nodes.
- */
-CompilationUnit _root;
-
-/**
- * An arbitrary string describing where the source code came from. Most
- * obviously, this could be a file path. We use this when reporting
- * invalid messages.
- */
-String _origin;
-
-String _reportErrorLocation(AstNode node) {
- var result = new StringBuffer();
- if (_origin != null) result.write(" from $_origin");
- var info = _root.lineInfo;
- if (info != null) {
- var line = info.getLocation(node.offset);
- result.write(" line: ${line.lineNumber}, column: ${line.columnNumber}");
- }
- return result.toString();
-}
-
-/**
- * This visits the program source nodes looking for Intl.message uses
- * that conform to its pattern and then creating the corresponding
- * IntlMessage objects. We have to find both the enclosing function, and
- * the Intl.message invocation.
- */
-class MessageFindingVisitor extends GeneralizingAstVisitor {
- MessageFindingVisitor();
-
- /**
- * Accumulates the messages we have found, keyed by name.
- */
- final Map<String, MainMessage> messages = new Map<String, MainMessage>();
-
- /**
- * We keep track of the data from the last MethodDeclaration,
- * FunctionDeclaration or FunctionExpression that we saw on the way down,
- * as that will be the nearest parent of the Intl.message invocation.
- */
- FormalParameterList parameters;
- String name;
-
- /** Return true if [node] matches the pattern we expect for Intl.message() */
- bool looksLikeIntlMessage(MethodInvocation node) {
- const validNames = const ["message", "plural", "gender", "select"];
- if (!validNames.contains(node.methodName.name)) return false;
- if (!(node.target is SimpleIdentifier)) return false;
- SimpleIdentifier target = node.target;
- return target.token.toString() == "Intl";
- }
-
- Message _expectedInstance(String type) {
- switch (type) {
- case 'message':
- return new MainMessage();
- case 'plural':
- return new Plural();
- case 'gender':
- return new Gender();
- case 'select':
- return new Select();
- default:
- return null;
- }
- }
-
- /**
- * Returns a String describing why the node is invalid, or null if no
- * reason is found, so it's presumed valid.
- */
- String checkValidity(MethodInvocation node) {
- // The containing function cannot have named parameters.
- if (parameters.parameters.any((each) => each.kind == ParameterKind.NAMED)) {
- return "Named parameters on message functions are not supported.";
- }
- var arguments = node.argumentList.arguments;
- var instance = _expectedInstance(node.methodName.name);
- return instance.checkValidity(node, arguments, name, parameters);
- }
-
- /**
- * Record the parameters of the function or method declaration we last
- * encountered before seeing the Intl.message call.
- */
- void visitMethodDeclaration(MethodDeclaration node) {
- parameters = node.parameters;
- if (parameters == null) {
- parameters = new FormalParameterList(null, [], null, null, null);
- }
- name = node.name.name;
- super.visitMethodDeclaration(node);
- }
-
- /**
- * Record the parameters of the function or method declaration we last
- * encountered before seeing the Intl.message call.
- */
- void visitFunctionDeclaration(FunctionDeclaration node) {
- parameters = node.functionExpression.parameters;
- if (parameters == null) {
- parameters = new FormalParameterList(null, [], null, null, null);
- }
- name = node.name.name;
- super.visitFunctionDeclaration(node);
- }
-
- /**
- * Examine method invocations to see if they look like calls to Intl.message.
- * If we've found one, stop recursing. This is important because we can have
- * Intl.message(...Intl.plural...) and we don't want to treat the inner
- * plural as if it was an outermost message.
- */
- void visitMethodInvocation(MethodInvocation node) {
- if (!addIntlMessage(node)) {
- super.visitMethodInvocation(node);
- }
- }
-
- /**
- * Check that the node looks like an Intl.message invocation, and create
- * the [IntlMessage] object from it and store it in [messages]. Return true
- * if we successfully extracted a message and should stop looking. Return
- * false if we didn't, so should continue recursing.
- */
- bool addIntlMessage(MethodInvocation node) {
- if (!looksLikeIntlMessage(node)) return false;
- var reason = checkValidity(node);
- if (reason != null) {
- if (!suppressWarnings) {
- var err = new StringBuffer()
- ..write("Skipping invalid Intl.message invocation\n <$node>\n")
- ..writeAll([" reason: $reason\n", _reportErrorLocation(node)]);
- warnings.add(err.toString());
- print(err);
- }
- // We found one, but it's not valid. Stop recursing.
- return true;
- }
- var message;
- if (node.methodName.name == "message") {
- message = messageFromIntlMessageCall(node);
- } else {
- message = messageFromDirectPluralOrGenderCall(node);
- }
- if (message != null) messages[message.name] = message;
- return true;
- }
-
- /**
- * Create a MainMessage from [node] using the name and
- * parameters of the last function/method declaration we encountered,
- * and the values we get by calling [extract]. We set those values
- * by calling [setAttribute]. This is the common parts between
- * [messageFromIntlMessageCall] and [messageFromDirectPluralOrGenderCall].
- */
- MainMessage _messageFromNode(
- MethodInvocation node, Function extract, Function setAttribute) {
- var message = new MainMessage();
- message.name = name;
- message.arguments =
- parameters.parameters.map((x) => x.identifier.name).toList();
- var arguments = node.argumentList.arguments;
- var extractionResult = extract(message, arguments);
- if (extractionResult == null) return null;
-
- for (var namedArgument in arguments.where((x) => x is NamedExpression)) {
- var name = namedArgument.name.label.name;
- var exp = namedArgument.expression;
- var evaluator = new ConstantEvaluator();
- var basicValue = exp.accept(evaluator);
- var value = basicValue == ConstantEvaluator.NOT_A_CONSTANT
- ? exp.toString()
- : basicValue;
- setAttribute(message, name, value);
- }
- return message;
- }
-
- /**
- * Create a MainMessage from [node] using the name and
- * parameters of the last function/method declaration we encountered
- * and the parameters to the Intl.message call.
- */
- MainMessage messageFromIntlMessageCall(MethodInvocation node) {
- MainMessage extractFromIntlCall(MainMessage message, List arguments) {
- try {
- var interpolation = new InterpolationVisitor(message);
- arguments.first.accept(interpolation);
- if (interpolation.pieces.any((x) => x is Plural || x is Gender) &&
- !allowEmbeddedPluralsAndGenders) {
- if (interpolation.pieces.any((x) => x is String && x.isNotEmpty)) {
- throw new IntlMessageExtractionException(
- "Plural and gender expressions must be at the top level, "
- "they cannot be embedded in larger string literals.\n"
- "Error at $node");
- }
- }
- message.messagePieces.addAll(interpolation.pieces);
- } on IntlMessageExtractionException catch (e) {
- message = null;
- var err = new StringBuffer()
- ..writeAll(["Error ", e, "\nProcessing <", node, ">\n"])
- ..write(_reportErrorLocation(node));
- print(err);
- warnings.add(err.toString());
- }
- return message; // Because we may have set it to null on an error.
- }
-
- void setValue(MainMessage message, String fieldName, Object fieldValue) {
- message[fieldName] = fieldValue;
- }
-
- return _messageFromNode(node, extractFromIntlCall, setValue);
- }
-
- /**
- * Create a MainMessage from [node] using the name and
- * parameters of the last function/method declaration we encountered
- * and the parameters to the Intl.plural or Intl.gender call.
- */
- MainMessage messageFromDirectPluralOrGenderCall(MethodInvocation node) {
- MainMessage extractFromPluralOrGender(MainMessage message, _) {
- var visitor = new PluralAndGenderVisitor(message.messagePieces, message);
- node.accept(visitor);
- return message;
- }
-
- void setAttribute(MainMessage msg, String fieldName, String fieldValue) {
- if (msg.attributeNames.contains(fieldName)) {
- msg[fieldName] = fieldValue;
- }
- }
- return _messageFromNode(node, extractFromPluralOrGender, setAttribute);
- }
-}
-
-/**
- * Given an interpolation, find all of its chunks, validate that they are only
- * simple variable substitutions or else Intl.plural/gender calls,
- * and keep track of the pieces of text so that other parts
- * of the program can deal with the simple string sections and the generated
- * parts separately. Note that this is a SimpleAstVisitor, so it only
- * traverses one level of children rather than automatically recursing. If we
- * find a plural or gender, which requires recursion, we do it with a separate
- * special-purpose visitor.
- */
-class InterpolationVisitor extends SimpleAstVisitor {
- final Message message;
-
- InterpolationVisitor(this.message);
-
- List pieces = [];
- String get extractedMessage => pieces.join();
-
- void visitAdjacentStrings(AdjacentStrings node) {
- node.visitChildren(this);
- super.visitAdjacentStrings(node);
- }
-
- void visitStringInterpolation(StringInterpolation node) {
- node.visitChildren(this);
- super.visitStringInterpolation(node);
- }
-
- void visitSimpleStringLiteral(SimpleStringLiteral node) {
- pieces.add(node.value);
- super.visitSimpleStringLiteral(node);
- }
-
- void visitInterpolationString(InterpolationString node) {
- pieces.add(node.value);
- super.visitInterpolationString(node);
- }
-
- void visitInterpolationExpression(InterpolationExpression node) {
- if (node.expression is SimpleIdentifier) {
- return handleSimpleInterpolation(node);
- } else {
- return lookForPluralOrGender(node);
- }
- // Note that we never end up calling super.
- }
-
- lookForPluralOrGender(InterpolationExpression node) {
- var visitor = new PluralAndGenderVisitor(pieces, message);
- node.accept(visitor);
- if (!visitor.foundPluralOrGender) {
- throw new IntlMessageExtractionException(
- "Only simple identifiers and Intl.plural/gender/select expressions "
- "are allowed in message "
- "interpolation expressions.\nError at $node");
- }
- }
-
- void handleSimpleInterpolation(InterpolationExpression node) {
- var index = arguments.indexOf(node.expression.toString());
- if (index == -1) {
- throw new IntlMessageExtractionException(
- "Cannot find argument ${node.expression}");
- }
- pieces.add(index);
- }
-
- List get arguments => message.arguments;
-}
-
-/**
- * A visitor to extract information from Intl.plural/gender sends. Note that
- * this is a SimpleAstVisitor, so it doesn't automatically recurse. So this
- * needs to be called where we expect a plural or gender immediately below.
- */
-class PluralAndGenderVisitor extends SimpleAstVisitor {
- /**
- * A plural or gender always exists in the context of a parent message,
- * which could in turn also be a plural or gender.
- */
- final ComplexMessage parent;
-
- /**
- * The pieces of the message. We are given an initial version of this
- * from our parent and we add to it as we find additional information.
- */
- List pieces;
-
- /** This will be set to true if we find a plural or gender. */
- bool foundPluralOrGender = false;
-
- PluralAndGenderVisitor(this.pieces, this.parent) : super();
-
- visitInterpolationExpression(InterpolationExpression node) {
- // TODO(alanknight): Provide better errors for malformed expressions.
- if (!looksLikePluralOrGender(node.expression)) return;
- var reason = checkValidity(node.expression);
- if (reason != null) throw reason;
- var message = messageFromMethodInvocation(node.expression);
- foundPluralOrGender = true;
- pieces.add(message);
- super.visitInterpolationExpression(node);
- }
-
- visitMethodInvocation(MethodInvocation node) {
- pieces.add(messageFromMethodInvocation(node));
- super.visitMethodInvocation(node);
- }
-
- /** Return true if [node] matches the pattern for plural or gender message.*/
- bool looksLikePluralOrGender(MethodInvocation node) {
- if (!["plural", "gender", "select"].contains(node.methodName.name)) {
- return false;
- }
- if (!(node.target is SimpleIdentifier)) return false;
- SimpleIdentifier target = node.target;
- return target.token.toString() == "Intl";
- }
-
- /**
- * Returns a String describing why the node is invalid, or null if no
- * reason is found, so it's presumed valid.
- */
- String checkValidity(MethodInvocation node) {
- // TODO(alanknight): Add reasonable validity checks.
- return null;
- }
-
- /**
- * Create a MainMessage from [node] using the name and
- * parameters of the last function/method declaration we encountered
- * and the parameters to the Intl.message call.
- */
- Message messageFromMethodInvocation(MethodInvocation node) {
- var message;
- switch (node.methodName.name) {
- case "gender":
- message = new Gender();
- break;
- case "plural":
- message = new Plural();
- break;
- case "select":
- message = new Select();
- break;
- default:
- throw new IntlMessageExtractionException(
- "Invalid plural/gender/select message");
- }
- message.parent = parent;
-
- var arguments = message.argumentsOfInterestFor(node);
- arguments.forEach((key, value) {
- try {
- var interpolation = new InterpolationVisitor(message);
- value.accept(interpolation);
- message[key] = interpolation.pieces;
- } on IntlMessageExtractionException catch (e) {
- message = null;
- var err = new StringBuffer()
- ..writeAll(["Error ", e, "\nProcessing <", node, ">"])
- ..write(_reportErrorLocation(node));
- print(err);
- warnings.add(err.toString());
- }
- });
- var mainArg = node.argumentList.arguments
- .firstWhere((each) => each is! NamedExpression);
- if (mainArg is SimpleStringLiteral) {
- message.mainArgument = mainArg.toString();
- } else {
- message.mainArgument = mainArg.name;
- }
- return message;
- }
-}
-
-/**
- * Exception thrown when we cannot process a message properly.
- */
-class IntlMessageExtractionException implements Exception {
- /**
- * A message describing the error.
- */
- final String message;
-
- /**
- * Creates a new exception with an optional error [message].
- */
- const IntlMessageExtractionException([this.message = ""]);
-
- String toString() => "IntlMessageExtractionException: $message";
-}
« no previous file with comments | « packages/intl/lib/date_time_patterns.dart ('k') | packages/intl/lib/generate_localized.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698