Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -174,16 +174,6 @@
}
}

/**
* Create a String method declaration from a Dictionary/value pair.
*
* @param key Dictionary
* @param defaultValue default value
*/
public void genSimpleMethodDecl(String key, String defaultValue) {
genMethodDecl("String", defaultValue, key);
}

/**
* Create method args based upon the default value.
*
Expand Down Expand Up @@ -248,13 +238,17 @@
throw new IllegalStateException(
"File '"
+ resourceFile
+ "' cannot be used to generate message classes, as it has no key/value pairs defined.");

Check warning on line 241 in user/src/com/google/gwt/i18n/rebind/AbstractLocalizableInterfaceCreator.java

View workflow job for this annotation

GitHub Actions / build (21)

[checkstyle] reported by reviewdog 🐶 Line is longer than 100 characters (found 103). Raw Output: /home/runner/work/gwt/gwt/gwt/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableInterfaceCreator.java:241:0: warning: Line is longer than 100 characters (found 103). (com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck)
}
generateMethods(p, keys);
composer.commit(new PrintWriterTreeLogger());
}

void generateMethods(LocalizedProperties properties, String[] keys) {
for (String key : keys) {
String value = p.getProperty(key);
genSimpleMethodDecl(key, value);
String value = properties.getProperty(key);
genMethodDecl(key, value);
}
composer.commit(new PrintWriterTreeLogger());
}

private void addFormatters() {
Expand All @@ -265,14 +259,14 @@
formatters.add(new RenameDuplicates());
}

private String formatKey(String key) {
protected String formatKey(String key) {
for (ResourceKeyFormatter formatter : formatters) {
key = formatter.format(key);
}
return key;
}

private void genMethodDecl(String type, String defaultValue, String key) {
private void genMethodDecl(String defaultValue, String key) {
composer.beginJavaDocComment();
String escaped = makeJavaString(defaultValue);
composer.println("Translated " + escaped + ".\n");
Expand All @@ -281,7 +275,7 @@
genValueAnnotation(defaultValue);
composer.println("@Key(" + makeJavaString(key) + ")");
String methodName = formatKey(key);
composer.print(type + " " + methodName);
composer.print("String " + methodName);
composer.print("(");
genMethodArgs(defaultValue);
composer.print(");\n");
Expand Down
191 changes: 161 additions & 30 deletions user/src/com/google/gwt/i18n/rebind/MessagesInterfaceCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@
package com.google.gwt.i18n.rebind;

import com.google.gwt.i18n.client.Messages;
import com.google.gwt.i18n.rebind.MessageFormatParser.ArgumentChunk;
import com.google.gwt.i18n.rebind.MessageFormatParser.TemplateChunk;
import com.google.gwt.i18n.server.MessageFormatUtils;
import com.google.gwt.i18n.server.MessageFormatUtils.ArgumentChunk;
import com.google.gwt.i18n.server.MessageFormatUtils.TemplateChunk;

import org.apache.tapestry.util.text.LocalizedProperties;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.HashSet;
import java.util.Set;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
* Creates a MessagesInterface from a Resource file.
Expand All @@ -32,22 +36,21 @@ public class MessagesInterfaceCreator extends
AbstractLocalizableInterfaceCreator {

/**
* Searches for MessageFormat-style args in the template string and returns
* a set of argument indices seen.
* Searches for MessageFormat-style args in the template string and returns a map of of argument
* indices seen.
*
* @param template template to parse
* @return set of argument indices seen
* @throws ParseException if the template is incorrect.
*/
private static Set<Integer> numberOfMessageArgs(String template)
throws ParseException {
Set<Integer> seenArgs = new HashSet<Integer>();
for (TemplateChunk chunk : MessageFormatParser.parse(template)) {
private static Map<Integer, ArgumentChunk> getMessageArgs(String template) throws ParseException {
HashMap<Integer, ArgumentChunk> args = new HashMap<>();
for (TemplateChunk chunk : MessageFormatUtils.MessageStyle.MESSAGE_FORMAT.parse(template)) {
if (chunk instanceof ArgumentChunk) {
seenArgs.add(((ArgumentChunk) chunk).getArgumentNumber());
args.put(((ArgumentChunk) chunk).getArgumentNumber(), (ArgumentChunk) chunk);
}
}
return seenArgs;
return args;
}

/**
Expand All @@ -65,28 +68,61 @@ public MessagesInterfaceCreator(String className, String packageName,
Messages.class);
}

@Override
void generateMethods(LocalizedProperties properties, String[] keys) {
for (int i = 0; i < keys.length; i++) {
String key = keys[i];
String value = properties.getProperty(key);
Map<String, String> plurals = new HashMap<>();
while (i + 1 < keys.length && isNextPlural(key, keys[i + 1])) {
i++;
plurals.put(keys[i], properties.getProperty(keys[i]));
}
genMethodDecl(value, key, plurals);
}
}

@Override
protected void genMethodArgs(String defaultValue) {
try {
Set<Integer> seenArgs = numberOfMessageArgs(defaultValue);
int maxArgSeen = -1;
for (int arg : seenArgs) {
if (arg > maxArgSeen) {
maxArgSeen = arg;
}
}

private boolean isNextPlural(String key, String nextKey) {
return nextKey.matches(key + "\\[.*\\]");
}

private void genMethodArgs(Map<Integer, ArgumentChunk> args) {
for (int i = 0; i <= Collections.max(args.keySet()); i++) {
if (i > 0) {
composer.print(", ");
}
for (int i = 0; i <= maxArgSeen; i++) {
if (i > 0) {
composer.print(", ");
}
if (!seenArgs.contains(i)) {
composer.print("@Optional ");
}
composer.print("String arg" + i);
if (!args.containsKey(i)) {
composer.print("@Optional String arg" + i);
continue;
}
} catch (ParseException e) {
throw new RuntimeException(defaultValue
+ " could not be parsed as a MessageFormat string.", e);
String format = (format = args.get(i).getFormat()) != null ? format : "string";
String subFormat = (subFormat = args.get(i).getSubFormat()) != null ? subFormat : "";
if (args.get(i).isList()) {
composer.print("java.util.List<");
}
switch (format) {
case "number":
determineNumberType(subFormat);
break;
case "date":
case "time":
case "localdatetime":
composer.print("java.util.Date");
break;
case "safehtml":
composer.print("com.google.gwt.safehtml.shared.SafeHtml");
break;
default:
composer.print("String");
}
if (args.get(i).isList()) {
composer.print(">");
}
composer.print(" arg" + i);
}
}

Expand All @@ -100,4 +136,99 @@ protected String javaDocComment(String path) {
return "Interface to represent the messages contained in resource bundle:\n\t"
+ path + "'.";
}

private void determineNumberType(String subFormat) {
switch (subFormat) {
case "integer":
composer.print("Integer");
break;
case "currency":
case "percent":
default:
if (subFormat.contains(".")) {
composer.print("Double");
} else {
composer.print("Integer");
}
}
}

private String determineReturnType(Map<Integer, ArgumentChunk> args) {
for (ArgumentChunk arg : args.values()) {
if ("safehtml".equals(arg.getFormat())) {
return "com.google.gwt.safehtml.shared.SafeHtml";
}
}
return "String";
}

private void genPluralsAnnotation(Map<String, String> plurals) {
composer.print("@AlternateMessage({");
String[] keys = plurals.keySet().toArray(new String[] {});
if (keys.length > 1) {
composer.println();
composer.indent();
}
for (int i = 0; i < keys.length; i++) {
String key = keys[i];
if (i > 0) {
composer.println(",");
}
composer.print("\"");
composer.print(key.substring(key.indexOf('[') + 1, key.length() - 1));
composer.print("\", \"");
composer.print(makeJavaString(plurals.get(key)));
composer.println("\"");
}
if (keys.length > 1) {
composer.println();
composer.outdent();
}
composer.println("})");
}

private void genMethodDecl(String defaultValue, String key, Map<String, String> plurals) {
try {
Map<Integer, ArgumentChunk> args = getMessageArgs(defaultValue);
genMethodJavaDoc(defaultValue, args);
genValueAnnotation(defaultValue);
if (!plurals.isEmpty()) {
genPluralsAnnotation(plurals);
}
composer.println("@Key(" + makeJavaString(key) + ")");
String methodName = formatKey(key);
String type = determineReturnType(args);
composer.print(type + " " + methodName);
composer.print("(");
if (!plurals.isEmpty()) {
composer.print("@PluralCount ");
}
if (!args.isEmpty()) {
genMethodArgs(args);
}
composer.print(");\n");
} catch (ParseException e) {
throw new RuntimeException(defaultValue + " could not be parsed as a MessageFormat string.",
e);
}
}

private void genMethodJavaDoc(String defaultValue, Map<Integer, ArgumentChunk> args) {
composer.beginJavaDocComment();
String escaped = makeJavaString(defaultValue);
composer.println("Translated " + escaped + ".\n");
if (!args.isEmpty()) {
for (int i = 0; i <= Collections.max(args.keySet()); i++) {
composer.print("@param arg" + i);
if (args.containsKey(i)) {
composer.println(" " + makeJavaString(args.get(i).getAsMessageFormatString()));
} else {
composer.println(" optional");
}
}
}
composer.println("@return translated " + escaped);
composer.endJavaDocComment();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
stringArg={0}
integerArg={0,number}
currencyArg={0,number,currency}
percentArg={0,number,percent}
doubleArg={0,number,###,##0.00}
dateArg={0,date}
timeArg={0,time}
localdatetime={0,localdatetime}
safehtmlArg={0,safehtml}
optionalArgs={1} and {3}
pluralArg={0,number} of items
pluralArg[none]=There are no items
pluralArg[one]=There is one item
pluralArg[two]=There are two items
pluralArg[few]=There are a few items
pluralArg[many]=There are many items
pluralArg[\=4]= There is exactly 4 items

5 changes: 5 additions & 0 deletions user/test/com/google/gwt/i18n/tools/I18NSyncTest_.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ public void testMessagesQuoting() throws IOException {
I18NSync.createMessagesInterfaceFromClassName(className, CLIENT_SOURCE_DIR);
}

public void testMessagesArgTypes() throws IOException {
String className = CLIENT_SOURCE_PACKAGE + "TestMessagesArgTypes";
I18NSync.createMessagesInterfaceFromClassName(className, CLIENT_SOURCE_DIR);
}

public void testMethodRenaming() throws IOException {
String className = CLIENT_SOURCE_PACKAGE + "TestBadKeys";
I18NSync.createConstantsWithLookupInterfaceFromClassName(className,
Expand Down
Loading