Chromium Code Reviews| Index: pkg/intl/lib/src/intl_message.dart |
| diff --git a/pkg/intl/lib/src/intl_message.dart b/pkg/intl/lib/src/intl_message.dart |
| index ae3179876010680b4b27812bccdf12990b783417..8619eeb8cf60ba73c051822a61b487fd5f496386 100644 |
| --- a/pkg/intl/lib/src/intl_message.dart |
| +++ b/pkg/intl/lib/src/intl_message.dart |
| @@ -33,6 +33,8 @@ |
| */ |
| library intl_message; |
| +import 'package:analyzer_experimental/analyzer.dart'; |
| + |
| /** A default function for the [Message.expanded] method. */ |
| _nullTransform(msg, chunk) => chunk; |
| @@ -59,6 +61,39 @@ abstract class Message { |
| */ |
| get arguments => parent == null ? const [] : parent.arguments; |
| + String checkValidity(MethodInvocation node, List arguments, |
| + String outerName, FormalParameterList outerArgs) { |
| + var hasArgs = arguments.any( |
| + (each) => each is NamedExpression && each.name.label.name == 'args'); |
| + var hasParameters = !outerArgs.parameters.isEmpty; |
| + if (!hasArgs && hasParameters) { |
| + return "The 'args' argument for Intl.message must be specified"; |
| + } |
| + |
| + var messageName = arguments.firstWhere( |
| + (eachArg) => eachArg is NamedExpression && |
| + eachArg.name.label.name == 'name', |
| + orElse: () => null); |
| + if (messageName == null) { |
| + return "The 'name' argument for Intl.message must be specified"; |
| + } |
| + if ((messageName.expression is! SimpleStringLiteral) |
| + || messageName.expression.value != outerName) { |
| + return "The 'name' argument for Intl.message must be a simple string " |
| + "literal and match the containing function name."; |
| + } |
| + var simpleArguments = arguments.where( |
| + (each) => each is NamedExpression |
| + && ["desc", "locale", "name"].contains(each.name.label.name)); |
| + var values = simpleArguments.map((each) => each.expression).toList(); |
| + for (var arg in values) { |
| + if (arg is! SimpleStringLiteral) { |
| + return "Intl.message argument '${arg.name.label.name}' must be " |
| + "a simple string literal"; |
| + } |
| + } |
| + } |
| + |
| /** |
| * Turn a value, typically read from a translation file or created out of an |
| * AST for a source program, into the appropriate |
| @@ -234,6 +269,16 @@ class MainMessage extends ComplexMessage { |
| */ |
| List<Message> messagePieces = []; |
| + /** Verify that this looks like a correct Intl.message invocation. */ |
| + String checkValidity(MethodInvocation node, List arguments, |
| + String outerName, FormalParameterList outerArgs) { |
| + if (arguments.first is! StringLiteral) { |
| + return "Intl.message messages must be string literals"; |
| + } |
| + |
| + return super.checkValidity(node, arguments, outerName, outerArgs); |
| + } |
| + |
| void addPieces(List<Message> messages) { |
| for (var each in messages) { |
| messagePieces.add(Message.from(each, this)); |
| @@ -384,6 +429,18 @@ abstract class SubMessage extends ComplexMessage { |
| String mainArgument; |
| /** |
| + * Return the arguments that affect this SubMessage as a map of |
| + * argument names and values. |
| + */ |
| + Map argumentsOfInterestFor(MethodInvocation node) { |
| + var basicArguments = node.argumentList.arguments.elements; |
| + var others = basicArguments.where((each) => each is NamedExpression); |
| + return new Map.fromIterable(others, |
| + key: (node) => node.name.label.token.value(), |
| + value: (node) => node.expression); |
| + } |
| + |
| + /** |
| * Return the list of attribute names to use when generating code. This |
| * may be different from [attributeNames] if there are multiple aliases |
| * that map to the same clause. |
| @@ -423,10 +480,10 @@ class Gender extends SubMessage { |
| Gender(); |
| /** |
| - * Create a new IntlGender providing [mainArgument] and the list of possible |
| + * Create a new Gender providing [mainArgument] and the list of possible |
| * clauses. Each clause is expected to be a list whose first element is a |
| - * variable name and whose second element is either a String or |
| - * a list of strings and IntlMessageSends or IntlVariableSubstitution. |
| + * variable name and whose second element is either a [String] or |
| + * a list of strings and [Message] or [VariableSubstitution]. |
| */ |
| Gender.from(mainArgument, List clauses, parent) : |
| super.from(mainArgument, clauses, parent); |
| @@ -519,3 +576,69 @@ class Plural extends SubMessage { |
| } |
| } |
| +/** |
| + * Represents a message send of [Intl.select] inside a message that is to |
| + * be internationalized. This corresponds to an ICU message syntax "select" |
| + * with arbitrary options. |
| + */ |
| +class Select extends SubMessage { |
| + |
| + Select(); |
| + /** |
| + * Create a new [Select] providing [mainArgument] and the list of possible |
|
Emily Fortuna
2013/08/08 23:49:55
what types can mainArgument be since it's not type
Alan Knight
2013/08/09 17:09:16
It's a String. On the superclass it's typed as a f
|
| + * clauses. Each clause is expected to be a list whose first element is a |
| + * variable name and whose second element is either a String or |
| + * a list of strings and [Message]s or [VariableSubstitution]s. |
| + */ |
| + Select.from(mainArgument, List clauses, parent) : |
| + super.from(mainArgument, clauses, parent); |
| + |
| + Map<String, Message> cases = new Map<String, Message>(); |
| + |
| + String get icuMessageName => "select"; |
| + String get dartMessageName => 'Intl.select'; |
| + |
| + get attributeNames => cases.keys; |
| + get codeAttributeNames => attributeNames; |
| + |
| + void operator []=(attributeName, rawValue) { |
| + var value = Message.from(rawValue, this); |
| + cases[attributeName] = value; |
| + } |
| + |
| + Message operator [](String attributeName) { |
| + var exact = cases[attributeName]; |
| + return exact == null ? cases["other"] : exact; |
| + } |
| + |
| + /** |
| + * Return the arguments that we care about for the select. In this |
| + * case they will all be passed in as a Map rather than as the named |
| + * arguments used in Plural/Gender. |
| + */ |
| + Map argumentsOfInterestFor(MethodInvocation node) { |
|
Emily Fortuna
2013/08/08 23:49:55
should this actually be private? It seems more lik
Alan Knight
2013/08/09 17:09:16
It's somewhat of a helper, but it's a helper for t
|
| + var casesArgument = node.argumentList.arguments.elements[1]; |
| + return new Map.fromIterable(casesArgument.entries, |
| + key: (node) => node.key.value, |
| + value: (node) => node.value); |
| + } |
| + |
| + /** |
| + * Write out the generated representation of this message. This differs |
| + * from Plural/Gender in that it prints a literal map rather than |
| + * named arguments. |
| + */ |
| + String toCode() { |
| + var out = new StringBuffer(); |
| + out.write('\${'); |
| + out.write(dartMessageName); |
| + out.write('('); |
| + out.write(mainArgument); |
| + var args = codeAttributeNames; |
| + out.write(", {"); |
| + args.fold(out, (buffer, arg) => buffer..write( |
| + "'$arg': '${this[arg].toCode()}', ")); |
| + out.write("})}"); |
| + return out.toString(); |
| + } |
| +} |