FIX: LookupMethodCreator create too large method
Bug: #8611
Bug-Link: https://github.com/gwtproject/gwt/issues/8611
Change-Id: I6156581364a571621234b9c1d7fd597beaa27c7c
diff --git a/user/src/com/google/gwt/i18n/rebind/ConstantsWithLookupImplCreator.java b/user/src/com/google/gwt/i18n/rebind/ConstantsWithLookupImplCreator.java
index 89813c9..0a5de8b 100644
--- a/user/src/com/google/gwt/i18n/rebind/ConstantsWithLookupImplCreator.java
+++ b/user/src/com/google/gwt/i18n/rebind/ConstantsWithLookupImplCreator.java
@@ -25,17 +25,54 @@
import com.google.gwt.core.ext.typeinfo.TypeOracleException;
import com.google.gwt.i18n.rebind.AbstractResource.ResourceList;
import com.google.gwt.i18n.shared.GwtLocale;
-import com.google.gwt.user.rebind.AbstractMethodCreator;
+import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.user.rebind.SourceWriter;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
class ConstantsWithLookupImplCreator extends ConstantsImplCreator {
+
+ /**
+ * Used partition size if no one is specified.
+ *
+ * Used in constructor without a partition size.
+ */
+ private static final int DEFAULT_PARTITIONS_SIZE = 500;
+
final JMethod[] allInterfaceMethods;
- private final Map<String, AbstractMethodCreator> namesToMethodCreators = new HashMap<String, AbstractMethodCreator>();
+ private final Map<String, LookupMethodCreator> namesToMethodCreators = new HashMap<>();
+
+ private final Map<JMethod, List<List<JMethod>>> neededPartitionLookups = new HashMap<>();
+ private final int partitionsSize;
+
+ /**
+ * Constructor for <code>ConstantsWithLookupImplCreator</code>. The default partition size of
+ * {@value #DEFAULT_PARTITIONS_SIZE} is used.
+ *
+ * @param logger logger to print errors
+ * @param writer <code>Writer</code> to print to
+ * @param localizableClass class/interface to conform to
+ * @param resourceList resource bundle used to generate the class
+ * @param oracle types
+ * @throws UnableToCompleteException
+ *
+ * @see LookupMethodCreator#DEFAULT_PARTITIONS_SIZE
+ */
+ ConstantsWithLookupImplCreator(TreeLogger logger, SourceWriter writer,
+ JClassType localizableClass, ResourceList resourceList, TypeOracle oracle)
+ throws UnableToCompleteException {
+ this(logger, writer, localizableClass, resourceList, oracle, DEFAULT_PARTITIONS_SIZE);
+ }
+
/**
* Constructor for <code>ConstantsWithLookupImplCreator</code>.
*
@@ -47,9 +84,10 @@
* @throws UnableToCompleteException
*/
ConstantsWithLookupImplCreator(TreeLogger logger, SourceWriter writer,
- JClassType localizableClass, ResourceList resourceList, TypeOracle oracle)
- throws UnableToCompleteException {
+ JClassType localizableClass, ResourceList resourceList, TypeOracle oracle,
+ int partitionsSize) throws UnableToCompleteException {
super(logger, writer, localizableClass, resourceList, oracle);
+ this.partitionsSize = partitionsSize;
try {
// Boolean
@@ -63,7 +101,9 @@
@Override
public String returnTemplate() {
- return "boolean answer = {0}();\ncache.put(\"{0}\",new Boolean(answer));\nreturn answer;";
+ return "boolean answer = {0}();\n"
+ + "cache.put(\"{0}\",new Boolean(answer));\n"
+ + "return answer;";
}
};
namesToMethodCreators.put("getBoolean", booleanMethod);
@@ -79,7 +119,9 @@
@Override
public String returnTemplate() {
- return "double answer = {0}();\ncache.put(\"{0}\",new Double(answer));\nreturn answer;";
+ return "double answer = {0}();\n"
+ + "cache.put(\"{0}\",new Double(answer));\n"
+ + "return answer;";
}
};
namesToMethodCreators.put("getDouble", doubleMethod);
@@ -94,7 +136,9 @@
@Override
public String returnTemplate() {
- return "int answer = {0}();\ncache.put(\"{0}\",new Integer(answer));\nreturn answer;";
+ return "int answer = {0}();\n"
+ + "cache.put(\"{0}\",new Integer(answer));\n"
+ + "return answer;";
}
};
@@ -105,7 +149,9 @@
LookupMethodCreator floatMethod = new LookupMethodCreator(this, floatType) {
@Override
public String returnTemplate() {
- String val = "float answer = {0}();\ncache.put(\"{0}\", new Float(answer));\nreturn answer;";
+ String val = "float answer = {0}();\n"
+ + "cache.put(\"{0}\", new Float(answer));\n"
+ + "return answer;";
return val;
}
@@ -132,7 +178,9 @@
stringType) {
@Override
public String returnTemplate() {
- return "String answer = {0}();\ncache.put(\"{0}\",answer);\nreturn answer;";
+ return "String answer = {0}();\n"
+ + "cache.put(\"{0}\",answer);\n"
+ + "return answer;";
}
};
namesToMethodCreators.put("getString", stringMethod);
@@ -149,6 +197,12 @@
}
}
+ @Override
+ protected void classEpilog() {
+ createNeededPartitionLookups();
+ super.classEpilog();
+ }
+
/**
* Create the method body associated with the given method. Arguments are
* arg0...argN.
@@ -159,9 +213,9 @@
checkMethod(logger, method);
if (method.getParameters().length == 1) {
String name = method.getName();
- AbstractMethodCreator c = namesToMethodCreators.get(name);
+ LookupMethodCreator c = getLookupMethodCreator(name);
if (c != null) {
- c.createMethodFor(logger, method, name, null, locale);
+ createMethodWithPartitionCheckFor(c, method);
return;
}
}
@@ -169,6 +223,68 @@
super.emitMethodBody(logger, method, locale);
}
+ void addNeededPartitionLookups(JMethod targetMethod,
+ List<List<JMethod>> methodToCreatePartitionLookups) {
+ neededPartitionLookups.put(targetMethod, methodToCreatePartitionLookups);
+ }
+
+ void createMethodWithPartitionCheckFor(LookupMethodCreator methodCreator, JMethod targetMethod) {
+ List<List<JMethod>> methodPartitions = findMethodsToCreateWithPartitionSize(targetMethod,
+ methodCreator.getReturnType());
+
+ String nextPartitionMethod = null;
+ final List<List<JMethod>> methodToCreatePartitionLookups;
+ final List<JMethod> methodsToCreate;
+ if (methodPartitions.size() > 1) {
+ nextPartitionMethod = createPartitionMethodName(targetMethod, 0);
+ methodsToCreate = methodPartitions.get(0);
+ methodToCreatePartitionLookups = methodPartitions.subList(1, methodPartitions.size());
+ } else {
+ methodsToCreate = methodPartitions.isEmpty() ? Collections.<JMethod> emptyList()
+ : methodPartitions.get(0);
+ methodToCreatePartitionLookups = Collections.emptyList();
+ }
+ addNeededPartitionLookups(targetMethod, methodToCreatePartitionLookups);
+ methodCreator.createCacheLookupFor();
+ methodCreator.createMethodFor(targetMethod, methodsToCreate, nextPartitionMethod);
+ }
+
+ String createPartitionMethodName(JMethod targetMethod, int partitionIndex) {
+ final String templatePartitionMethodName = "{0}FromPartition{1}";
+ return MessageFormat.format(templatePartitionMethodName, new Object[] {
+ targetMethod.getName(), partitionIndex});
+ }
+
+ List<JMethod> findAllMethodsToCreate(JMethod targetMethod, JType methodReturnType) {
+ JMethod[] allMethods = allInterfaceMethods;
+ JType erasedType = methodReturnType.getErasedType();
+ List<JMethod> methodsToCreate = new ArrayList<>();
+ for (JMethod methodToCheck : allMethods) {
+ if (methodToCheck.getReturnType().getErasedType().equals(erasedType)
+ && methodToCheck != targetMethod) {
+ methodsToCreate.add(methodToCheck);
+ }
+ }
+ return methodsToCreate;
+ }
+
+ List<List<JMethod>> findMethodsToCreateWithPartitionSize(JMethod targetMethod,
+ JType methodReturnType) {
+ List<JMethod> allMethodsToCreate = findAllMethodsToCreate(targetMethod, methodReturnType);
+ return Lists.partition(allMethodsToCreate, partitionsSize);
+ }
+
+ LookupMethodCreator getLookupMethodCreator(String name) {
+ return namesToMethodCreators.get(name);
+ }
+
+ /**
+ * Visible for testing only.
+ */
+ Map<JMethod, List<List<JMethod>>> getNeededPartitionLookups() {
+ return neededPartitionLookups;
+ }
+
/**
* Checks that the method has the right structure to implement
* <code>Constant</code>.
@@ -177,7 +293,7 @@
*/
private void checkMethod(TreeLogger logger, JMethod method)
throws UnableToCompleteException {
- if (namesToMethodCreators.get(method.getName()) != null) {
+ if (getLookupMethodCreator(method.getName()) != null) {
JParameter[] params = method.getParameters();
// user may have specified a method named getInt/etc with no parameters
// this isn't a conflict, so treat them like any other Constant methods
@@ -195,4 +311,26 @@
checkConstantMethod(logger, method);
}
}
+
+ private void createNeededPartitionLookups() {
+ for (Entry<JMethod, List<List<JMethod>>> neededPartitionLookup :
+ neededPartitionLookups.entrySet()) {
+ JMethod targetMethod = neededPartitionLookup.getKey();
+ LookupMethodCreator lookupMethodCreator = getLookupMethodCreator(targetMethod.getName());
+ List<List<JMethod>> methodForPartitionLookups = neededPartitionLookup.getValue();
+ int partitionStartIndex = 0;
+ Iterator<List<JMethod>> neededPartitionIterator = methodForPartitionLookups.iterator();
+ while (neededPartitionIterator.hasNext()) {
+ String currentPartitionLookupMethodName = createPartitionMethodName(targetMethod,
+ partitionStartIndex++);
+ List<JMethod> methodsToCreate = neededPartitionIterator.next();
+ String nextPartitionMethod = null;
+ if (neededPartitionIterator.hasNext()) {
+ nextPartitionMethod = createPartitionMethodName(targetMethod, partitionStartIndex);
+ }
+ lookupMethodCreator.createPartitionLookup(currentPartitionLookupMethodName, targetMethod,
+ methodsToCreate, nextPartitionMethod);
+ }
+ }
+ }
}
diff --git a/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java b/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java
index fed873c..6a4f29b 100644
--- a/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java
+++ b/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java
@@ -21,17 +21,18 @@
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.i18n.rebind.AbstractResource.ResourceList;
import com.google.gwt.i18n.shared.GwtLocale;
-import com.google.gwt.user.rebind.AbstractGeneratorClassCreator;
import com.google.gwt.user.rebind.AbstractMethodCreator;
import com.google.gwt.user.rebind.AbstractSourceCreator;
import java.text.MessageFormat;
+import java.util.List;
/**
* Method creator to call the correct Map for the given Dictionary.
*/
class LookupMethodCreator extends AbstractMethodCreator {
- private JType returnType;
+
+ private final JType returnType;
/**
* Constructor for <code>LookupMethodCreator</code>.
@@ -39,18 +40,17 @@
* @param classCreator parent class creator
* @param returnType associated return type
*/
- public LookupMethodCreator(AbstractGeneratorClassCreator classCreator,
- JType returnType) {
+ public LookupMethodCreator(ConstantsWithLookupImplCreator classCreator, JType returnType) {
super(classCreator);
this.returnType = returnType;
}
@Override
- public void createMethodFor(TreeLogger logger, JMethod targetMethod,
- String key, ResourceList resourceList, GwtLocale locale) {
- createMethodFor(targetMethod);
+ public void createMethodFor(TreeLogger logger, JMethod targetMethod, String key,
+ ResourceList resourceList, GwtLocale locale) {
+ getConstantsWithLookupCreator().createMethodWithPartitionCheckFor(this, targetMethod);
}
-
+
/**
* Returns a {@code String} containing the return type name.
*/
@@ -65,7 +65,7 @@
return type;
}
- void createMethodFor(JMethod targetMethod) {
+ void createCacheLookupFor() {
String template = "{0} target = ({0}) cache.get(arg0);";
String returnTypeName = getReturnTypeName();
String lookup = MessageFormat.format(template, new Object[] {returnTypeName});
@@ -75,27 +75,64 @@
printReturnTarget();
outdent();
println("}");
- JMethod[] methods = ((ConstantsWithLookupImplCreator) currentCreator).allInterfaceMethods;
- JType erasedType = returnType.getErasedType();
- for (int i = 0; i < methods.length; i++) {
- if (methods[i].getReturnType().getErasedType().equals(erasedType)
- && methods[i] != targetMethod) {
- String methodName = methods[i].getName();
- String body = "if(arg0.equals(" + wrap(methodName) + ")) {";
- println(body);
- indent();
- printFound(methodName);
- outdent();
- println("}");
- }
+ }
+
+ /**
+ *
+ * @param targetMethod the method to create the lookups for
+ * @param methodsToCreate the looked up methods
+ * @param nextPartitionMethod the name of next method is called or null if end this generated
+ * method with throwing an exception
+ */
+ void createMethodFor(JMethod targetMethod, List<JMethod> methodsToCreate,
+ String nextPartitionMethod) {
+ createMethodLookups(methodsToCreate);
+ final String result;
+ boolean isLastPartition = nextPartitionMethod == null;
+ if (isLastPartition) {
+ result = createThrowMissingResourceException();
+ } else {
+ String callPartitionMethodTemplate = "return {0}(arg0);";
+ result = MessageFormat.format(callPartitionMethodTemplate, new Object[] {
+ nextPartitionMethod});
}
- String format = "throw new java.util.MissingResourceException(\"Cannot find constant ''\" +"
- + "{0} + \"''; expecting a method name\", \"{1}\", {0});";
- String result = MessageFormat.format(format, "arg0",
- this.currentCreator.getTarget().getQualifiedSourceName());
println(result);
}
+ void createMethodLookups(List<JMethod> methodsToCreate) {
+ for (JMethod methodToCreate : methodsToCreate) {
+ String methodName = methodToCreate.getName();
+ String body = "if (arg0.equals(" + wrap(methodName) + ")) {";
+ println(body);
+ indent();
+ printFound(methodName);
+ outdent();
+ println("}");
+ }
+ }
+
+ void createPartitionLookup(String partitionMethodName, JMethod targetMethod,
+ List<JMethod> methodsToCreate, String nextPartitionMethod) {
+ println("");
+ final String templatePartitionMethodName = "private {0} {1}({2} arg0) '{";
+ final String argument0Type = targetMethod.getParameterTypes()[0].getQualifiedSourceName();
+ String partitionMethodSignature = MessageFormat.format(templatePartitionMethodName,
+ new Object[] {getReturnTypeName(), partitionMethodName, argument0Type});
+ println(partitionMethodSignature);
+ indent();
+ createMethodFor(targetMethod, methodsToCreate, nextPartitionMethod);
+ outdent();
+ println("}");
+ }
+
+ ConstantsWithLookupImplCreator getConstantsWithLookupCreator() {
+ return ((ConstantsWithLookupImplCreator) currentCreator);
+ }
+
+ JType getReturnType() {
+ return returnType;
+ }
+
void printFound(String methodName) {
println(MessageFormat.format(returnTemplate(), new Object[] {methodName}));
}
@@ -107,4 +144,13 @@
String returnTemplate() {
return "return {0}();";
}
+
+ private String createThrowMissingResourceException() {
+ final String result;
+ String format = "throw new java.util.MissingResourceException(\"Cannot find constant ''\" +"
+ + "{0} + \"''; expecting a method name\", \"{1}\", {0});";
+ result = MessageFormat.format(format, "arg0", this.currentCreator.getTarget()
+ .getQualifiedSourceName());
+ return result;
+ }
}
diff --git a/user/src/com/google/gwt/user/rebind/AbstractGeneratorClassCreator.java b/user/src/com/google/gwt/user/rebind/AbstractGeneratorClassCreator.java
index a77c502..fe18ed4 100644
--- a/user/src/com/google/gwt/user/rebind/AbstractGeneratorClassCreator.java
+++ b/user/src/com/google/gwt/user/rebind/AbstractGeneratorClassCreator.java
@@ -45,7 +45,7 @@
* @return interface methods.
*/
public static JMethod[] getAllInterfaceMethods(JClassType type) {
- Map<String, JMethod> methods = new LinkedHashMap<String, JMethod>();
+ Map<String, JMethod> methods = new LinkedHashMap<>();
getAllInterfaceMethodsAux(type, methods);
return methods.values().toArray(new JMethod[methods.size()]);
}
diff --git a/user/src/com/google/gwt/user/rebind/AbstractSourceCreator.java b/user/src/com/google/gwt/user/rebind/AbstractSourceCreator.java
index 5f5e31f..6939704 100644
--- a/user/src/com/google/gwt/user/rebind/AbstractSourceCreator.java
+++ b/user/src/com/google/gwt/user/rebind/AbstractSourceCreator.java
@@ -82,6 +82,8 @@
protected static String getJavaObjectTypeFor(JPrimitiveType type) {
if (type == JPrimitiveType.INT) {
return "Integer";
+ } else if (type == JPrimitiveType.CHAR) {
+ return "Character";
} else {
String s = type.getSimpleSourceName();
return s.substring(0, 1).toUpperCase(Locale.ROOT) + s.substring(1);
diff --git a/user/test/com/google/gwt/i18n/I18NJreSuite.java b/user/test/com/google/gwt/i18n/I18NJreSuite.java
index cf5d875..970373c 100644
--- a/user/test/com/google/gwt/i18n/I18NJreSuite.java
+++ b/user/test/com/google/gwt/i18n/I18NJreSuite.java
@@ -15,8 +15,10 @@
*/
package com.google.gwt.i18n;
+import com.google.gwt.i18n.rebind.ConstantsWithLookupImplCreatorTest;
import com.google.gwt.i18n.rebind.LocaleUtilsTest;
import com.google.gwt.i18n.rebind.LocalizableGeneratorTest;
+import com.google.gwt.i18n.rebind.LookupMethodCreatorTest;
import com.google.gwt.i18n.server.GwtLocaleTest;
import com.google.gwt.i18n.server.MessageFormatParserTest;
import com.google.gwt.i18n.server.PropertyCatalogFactoryTest;
@@ -59,6 +61,8 @@
*/
// suite.addTestSuite(TypeOracleMessageTest.class);
suite.addTestSuite(WordCountDirectionEstimatorTest.class);
+ suite.addTestSuite(LookupMethodCreatorTest.class);
+ suite.addTestSuite(ConstantsWithLookupImplCreatorTest.class);
// $JUnit-END$
return suite;
diff --git a/user/test/com/google/gwt/i18n/rebind/ConstantsWithLookupImplCreatorTest.java b/user/test/com/google/gwt/i18n/rebind/ConstantsWithLookupImplCreatorTest.java
new file mode 100644
index 0000000..c1e8d03
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/rebind/ConstantsWithLookupImplCreatorTest.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.i18n.rebind;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.core.ext.typeinfo.TypeOracleException;
+import com.google.gwt.dev.javac.TypeOracleTestingUtils;
+import com.google.gwt.dev.javac.testing.impl.MockJavaResource;
+import com.google.gwt.dev.shell.FailErrorLogger;
+import com.google.gwt.i18n.rebind.AbstractResource.ResourceList;
+import com.google.gwt.i18n.server.GwtLocaleFactoryImpl;
+import com.google.gwt.i18n.shared.GwtLocale;
+import com.google.gwt.i18n.shared.GwtLocaleFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.google.gwt.user.rebind.StringSourceWriter;
+
+import junit.framework.TestCase;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link ConstantsWithLookupImplCreator}.
+ */
+public class ConstantsWithLookupImplCreatorTest extends TestCase {
+
+ private static final int TEST_PARTITION_SIZE = 3;
+
+ private static final MockJavaResource SINGLE_ENTRY_MESSAGES = new MockJavaResource(
+ "foo.SingleEntryMessage") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package foo;\n");
+ code.append("public interface SingleEntryMessage extends foo.Lookup {\n");
+ code.append(" String stringEntry();\n");
+ code.append(" int intEntry();\n");
+ code.append("}");
+ return code;
+ }
+ };
+
+ private static final MockJavaResource FOUR_ENTRY_MESSAGES = new MockJavaResource(
+ "foo.FourEntryMessage") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package foo;\n");
+ code.append("public interface FourEntryMessage extends foo.Lookup {\n");
+ code.append(" String first();\n");
+ code.append(" String second();\n");
+ code.append(" String third();\n");
+ code.append(" String fourth();\n");
+ code.append("}");
+ return code;
+ }
+ };
+ private static final MockJavaResource PARTITION_SIZE_ENTRY_MESSAGES = new MockJavaResource(
+ "foo.PartitionSizeEntryMessage") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package foo;\n");
+ code.append("public interface PartitionSizeEntryMessage extends foo.Lookup {\n");
+ for (int i = 1; i <= TEST_PARTITION_SIZE; i++) {
+ code.append(" String lookupMethod");
+ code.append(i);
+ code.append("();\n");
+ }
+ code.append("}");
+ return code;
+ }
+ };
+
+ private static final MockJavaResource LOOKUP = new MockJavaResource("foo.Lookup") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package foo;\n");
+ code.append("public interface Lookup {\n");
+ code.append(" String getString(String arg0);\n");
+ code.append(" int getInt(String arg0);\n");
+ code.append("}");
+ return code;
+ }
+ };
+
+ private TreeLogger logger = new FailErrorLogger();
+
+ private GwtLocale locale;
+
+ private ConstantsWithLookupImplCreator constantsWithLookupImplCreator;
+
+ private SourceWriter sw = new StringSourceWriter();
+
+ private TypeOracle oracle;
+
+ private JMethod stringMethod;
+
+ private JMethod intMethod;
+
+ @Override
+ public void setUp() throws TypeOracleException, UnableToCompleteException {
+ GwtLocaleFactory factory = new GwtLocaleFactoryImpl();
+ locale = factory.fromString("en");
+ oracle = TypeOracleTestingUtils.buildStandardTypeOracleWith(logger, SINGLE_ENTRY_MESSAGES,
+ FOUR_ENTRY_MESSAGES, LOOKUP, PARTITION_SIZE_ENTRY_MESSAGES);
+ initLookupMethodCreator(SINGLE_ENTRY_MESSAGES);
+ }
+
+ public void testClassEpilogCallCreatesNeededPartitonLookups() {
+ List<JMethod> missingPartition0 = Arrays.asList(stringMethod, intMethod);
+ List<List<JMethod>> methodToCreatePartitionLookups = new ArrayList<>();
+ methodToCreatePartitionLookups.add(missingPartition0);
+ constantsWithLookupImplCreator.addNeededPartitionLookups(stringMethod,
+ methodToCreatePartitionLookups);
+
+ constantsWithLookupImplCreator.classEpilog();
+
+ String actual = sw.toString().trim();
+
+ assertTrue("Missing partition lookup method (getStringFromPartition0).", actual.contains(
+ "java.lang.String getStringFromPartition0(java.lang.String arg0) {"));
+ }
+
+ public void testCodeForIntIsGeneratedSameAsWithoutPartitions() throws TypeOracleException,
+ UnableToCompleteException {
+ initLookupMethodCreator(SINGLE_ENTRY_MESSAGES);
+ constantsWithLookupImplCreator.emitMethodBody(logger, intMethod, locale);
+
+ // Same generated code as in version 2.8.1
+ SourceWriter expected = new StringSourceWriter();
+ expected.println("Integer target = (Integer) cache.get(arg0);");
+ expected.println("if (target != null) {");
+ expected.indent();
+ expected.println("return target.intValue();");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("if(arg0.equals(\"intEntry\")) {");
+ expected.indent();
+ expected.println("int answer = intEntry();");
+ // Needed because the return template use '\n'
+ expected.outdent();
+ expected.println("cache.put(\"intEntry\",new Integer(answer));");
+ expected.println("return answer;");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("throw new java.util.MissingResourceException("
+ + "\"Cannot find constant '\" +arg0 + \"'; expecting a method name\", \"foo.SingleEntryMessage\", arg0);");
+ expected.outdent();
+
+ String actual = sw.toString();
+ assertEquals("Wrong source Lookup created.", expected.toString(), actual);
+ }
+
+ public void testCodeIsGeneratedSameAsWithoutPartitions() throws TypeOracleException,
+ UnableToCompleteException {
+ initLookupMethodCreator(PARTITION_SIZE_ENTRY_MESSAGES);
+ constantsWithLookupImplCreator.emitMethodBody(logger, stringMethod, locale);
+
+ // Same generated code as in version 2.8.1
+ SourceWriter expected = new StringSourceWriter();
+ expected.println("java.lang.String target = (java.lang.String) cache.get(arg0);");
+ expected.println("if (target != null) {");
+ expected.indent();
+ expected.println("return target;");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("if(arg0.equals(\"lookupMethod1\")) {");
+ expected.indent();
+ expected.print("String answer = lookupMethod1();");
+ expected.print("\n");
+ expected.print("cache.put(\"lookupMethod1\",answer);");
+ expected.print("\n");
+ expected.println("return answer;");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("if(arg0.equals(\"lookupMethod2\")) {");
+ expected.indent();
+ expected.print("String answer = lookupMethod2();");
+ expected.print("\n");
+ expected.print("cache.put(\"lookupMethod2\",answer);");
+ expected.print("\n");
+ expected.println("return answer;");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("if(arg0.equals(\"lookupMethod3\")) {");
+ expected.indent();
+ expected.print("String answer = lookupMethod3();");
+ expected.print("\n");
+ expected.print("cache.put(\"lookupMethod3\",answer);");
+ expected.print("\n");
+ expected.println("return answer;");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("throw new java.util.MissingResourceException("
+ + "\"Cannot find constant '\" +arg0 + \"'; expecting a method name\", \"foo.PartitionSizeEntryMessage\", arg0);");
+ expected.outdent();
+
+ String actual = sw.toString();
+ assertEquals("Wrong source Lookup created.", expected.toString(), actual);
+ }
+
+ public void testCreateMethodForJMethodForSingleEntry() throws UnableToCompleteException {
+ constantsWithLookupImplCreator.emitMethodBody(logger, stringMethod, locale);
+
+ assertFalse("No partition lookup should created.", sw.toString().contains(
+ "java.lang.String getString0(java.lang.String arg0) {"));
+ }
+
+ public void testCreateMethodForJMethodForSingleIntEntry() throws UnableToCompleteException {
+ constantsWithLookupImplCreator.emitMethodBody(logger, intMethod, locale);
+
+ String createdSource = sw.toString();
+ assertTrue("Lookup for intMethod not created.", createdSource.contains(
+ "int answer = intEntry();"));
+ }
+
+ public void testCreateMethodForPartitionSizeEntriesNoPartitionNeeded() throws TypeOracleException,
+ UnableToCompleteException {
+ initLookupMethodCreator(PARTITION_SIZE_ENTRY_MESSAGES);
+ constantsWithLookupImplCreator.emitMethodBody(logger, stringMethod, locale);
+
+ String actual = sw.toString();
+
+ assertFalse("Missing partition lookup method (getStringFromPartitionN).", actual.contains(
+ "getStringFromPartition"));
+ }
+
+ public void testCreatePartitionMethodName() {
+ String partitionMethodName = constantsWithLookupImplCreator.createPartitionMethodName(
+ stringMethod, 1);
+ String expectedPartitionMethodName = "getStringFromPartition1";
+ assertEquals(expectedPartitionMethodName, partitionMethodName);
+ }
+
+ public void testEmitClassWithMultiMessageEntryCreateOneAdditionalPartition()
+ throws TypeOracleException, UnableToCompleteException {
+ initLookupMethodCreator(FOUR_ENTRY_MESSAGES);
+ constantsWithLookupImplCreator.emitClass(logger, locale);
+
+ String actual = sw.toString().trim();
+
+ assertTrue("Missing partition lookup method (getStringFromPartition0).", actual.contains(
+ "java.lang.String getStringFromPartition0(java.lang.String arg0) {"));
+ }
+
+ public void testEmitMethodBodyForJMethodForMultiMessageEntryNeedOneAdditionalPartition()
+ throws TypeOracleException, UnableToCompleteException {
+ initLookupMethodCreator(FOUR_ENTRY_MESSAGES);
+ constantsWithLookupImplCreator.emitMethodBody(logger, stringMethod, locale);
+
+ String actual = sw.toString().trim();
+
+ assertTrue("Method should end with call partition lookup method.", actual.endsWith(
+ "return getStringFromPartition0(arg0);"));
+ assertFalse("Partition method should not be created", actual.contains(
+ "java.lang.String getStringFromPartition0(java.lang.String arg0) {"));
+
+ assertEquals(1, constantsWithLookupImplCreator.getNeededPartitionLookups().size());
+ }
+
+ public void testFindMethodsToCreate() throws TypeOracleException {
+ initLookupMethodCreator(SINGLE_ENTRY_MESSAGES);
+
+ JType intType = oracle.parse(int.class.getName());
+ List<JMethod> intMethods = constantsWithLookupImplCreator.findAllMethodsToCreate(intMethod,
+ intType);
+
+ JMethod expectedIntMethod = oracle.findType(SINGLE_ENTRY_MESSAGES.getTypeName()).findMethod(
+ "intEntry", new JType[0]);
+
+ assertEquals(1, intMethods.size());
+ assertEquals(expectedIntMethod, intMethods.get(0));
+ }
+
+ public void testFindMethodsToCreateWithDifferentTargetMethod() throws TypeOracleException {
+ initLookupMethodCreator(SINGLE_ENTRY_MESSAGES);
+
+ JType intType = oracle.parse(int.class.getName());
+ List<JMethod> foundMethods = constantsWithLookupImplCreator.findAllMethodsToCreate(stringMethod,
+ intType);
+
+ JMethod expectedIntMethod = oracle.findType(SINGLE_ENTRY_MESSAGES.getTypeName()).findMethod(
+ "intEntry", new JType[0]);
+
+ assertEquals(2, foundMethods.size());
+ assertTrue(foundMethods.contains(intMethod));
+ assertTrue(foundMethods.contains(expectedIntMethod));
+ }
+
+ public void testGetReturnTypeNameForPrimitveTypes() {
+ for (JPrimitiveType primitiveType : JPrimitiveType.values()) {
+ LookupMethodCreator primitiveMethodCreator = new LookupMethodCreator(null, primitiveType);
+ String returnType = primitiveMethodCreator.getReturnTypeName();
+ String expectedType = primitiveType.getQualifiedBoxedSourceName().substring("java.lang."
+ .length());
+ assertEquals("Wrong Return Type for primitve type", expectedType, returnType);
+ }
+ }
+
+ private void initLookupMethodCreator(MockJavaResource resource) throws TypeOracleException {
+ initLookupMethodCreator(resource, TEST_PARTITION_SIZE);
+ }
+
+ private void initLookupMethodCreator(MockJavaResource resource, int partitionsSize)
+ throws TypeOracleException {
+ JClassType clazz = oracle.findType(resource.getTypeName());
+ try {
+ ResourceList resourceList = mock(ResourceList.class);
+ when(resourceList.getRequiredStringExt(anyString(), anyString())).thenReturn(
+ "Required value");
+ constantsWithLookupImplCreator = new ConstantsWithLookupImplCreator(logger, sw, clazz,
+ resourceList, oracle, partitionsSize);
+
+ JType stringType = oracle.parse(String.class.getName());
+ stringMethod = oracle.findType(LOOKUP.getTypeName()).findMethod("getString", new JType[] {
+ stringType});
+ intMethod = oracle.findType(LOOKUP.getTypeName()).findMethod("getInt", new JType[] {
+ stringType});
+ } catch (UnableToCompleteException e) {
+ fail(e.getMessage());
+ }
+ }
+
+}
diff --git a/user/test/com/google/gwt/i18n/rebind/LookupMethodCreatorTest.java b/user/test/com/google/gwt/i18n/rebind/LookupMethodCreatorTest.java
new file mode 100644
index 0000000..3fb045b
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/rebind/LookupMethodCreatorTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.i18n.rebind;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.core.ext.typeinfo.TypeOracleException;
+import com.google.gwt.dev.javac.TypeOracleTestingUtils;
+import com.google.gwt.dev.javac.testing.impl.MockJavaResource;
+import com.google.gwt.dev.shell.FailErrorLogger;
+import com.google.gwt.i18n.rebind.AbstractResource.ResourceList;
+import com.google.gwt.i18n.server.GwtLocaleFactoryImpl;
+import com.google.gwt.i18n.shared.GwtLocale;
+import com.google.gwt.i18n.shared.GwtLocaleFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.google.gwt.user.rebind.StringSourceWriter;
+
+import junit.framework.TestCase;
+
+import static org.mockito.Mockito.mock;
+
+import java.util.List;
+
+/**
+ * Tests for {@link LookupMethodCreator}.
+ */
+public class LookupMethodCreatorTest extends TestCase {
+
+ private static final int TEST_PARTITION_SIZE = 3;
+
+ private static final MockJavaResource SINGLE_ENTRY_MESSAGES = new MockJavaResource(
+ "foo.SingleEntryMessage") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package foo;\n");
+ code.append("public interface SingleEntryMessage extends foo.Lookup {\n");
+ code.append(" String stringEntry();\n");
+ code.append(" int intEntry();\n");
+ code.append("}");
+ return code;
+ }
+ };
+ private static final MockJavaResource PARTITION_SIZE_ENTRY_MESSAGES = new MockJavaResource(
+ "foo.PartitionSizeEntryMessage") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package foo;\n");
+ code.append("public interface PartitionSizeEntryMessage extends foo.Lookup {\n");
+ for (int i = 1; i <= TEST_PARTITION_SIZE; i++) {
+ code.append(" String lookupMethod");
+ code.append(i);
+ code.append("();\n");
+ }
+ code.append("}");
+ return code;
+ }
+ };
+
+ private static final MockJavaResource LOOKUP = new MockJavaResource("foo.Lookup") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package foo;\n");
+ code.append("public interface Lookup {\n");
+ code.append(" String getString(String arg0);\n");
+ code.append(" int getInt(String arg0);\n");
+ code.append("}");
+ return code;
+ }
+ };
+
+ private TreeLogger logger = new FailErrorLogger();
+
+ private LookupMethodCreator stringCreator;
+ private LookupMethodCreator intCreator;
+
+ private SourceWriter sw = new StringSourceWriter();
+
+ private TypeOracle oracle;
+
+ private JMethod stringMethod;
+
+ private JMethod intMethod;
+
+ private GwtLocale locale;
+
+ @Override
+ public void setUp() throws TypeOracleException, UnableToCompleteException {
+ GwtLocaleFactory factory = new GwtLocaleFactoryImpl();
+ locale = factory.fromString("en");
+ oracle = TypeOracleTestingUtils.buildStandardTypeOracleWith(logger, SINGLE_ENTRY_MESSAGES,
+ LOOKUP, PARTITION_SIZE_ENTRY_MESSAGES);
+ initLookupMethodCreator(SINGLE_ENTRY_MESSAGES);
+ }
+
+ public void testCodeForIntIsGeneratedSameAsWithoutPartitions() throws TypeOracleException {
+ initLookupMethodCreator(SINGLE_ENTRY_MESSAGES);
+ intCreator.createMethodFor(logger, intMethod, intMethod.getName(), null, locale);
+
+ // Same generated code as in version 2.8.1
+ SourceWriter expected = new StringSourceWriter();
+ expected.println("Integer target = (Integer) cache.get(arg0);");
+ expected.println("if (target != null) {");
+ expected.indent();
+ expected.println("return target.intValue();");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("if (arg0.equals(\"intEntry\")) {");
+ expected.indent();
+ expected.println("int answer = intEntry();");
+ // Needed because the return template use '\n'
+ expected.outdent();
+ expected.println("cache.put(\"intEntry\",new Integer(answer));");
+ expected.println("return answer;");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("throw new java.util.MissingResourceException("
+ + "\"Cannot find constant '\" +arg0 + \"'; expecting a method name\", \"foo.SingleEntryMessage\", arg0);");
+ expected.outdent();
+
+ String actual = sw.toString();
+ assertEquals("Wrong source Lookup created.", expected.toString(), actual);
+ }
+
+ public void testCodeIsGeneratedSameAsWithoutPartitions() throws TypeOracleException {
+ initLookupMethodCreator(PARTITION_SIZE_ENTRY_MESSAGES);
+ stringCreator.createMethodFor(logger, stringMethod, stringMethod.getName(), null, locale);
+
+ // Same generated code as in version 2.8.1
+ SourceWriter expected = new StringSourceWriter();
+ expected.println("java.lang.String target = (java.lang.String) cache.get(arg0);");
+ expected.println("if (target != null) {");
+ expected.indent();
+ expected.println("return target;");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("if (arg0.equals(\"lookupMethod1\")) {");
+ expected.indent();
+ expected.println("return lookupMethod1();");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("if (arg0.equals(\"lookupMethod2\")) {");
+ expected.indent();
+ expected.println("return lookupMethod2();");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("if (arg0.equals(\"lookupMethod3\")) {");
+ expected.indent();
+ expected.println("return lookupMethod3();");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("throw new java.util.MissingResourceException("
+ + "\"Cannot find constant '\" +arg0 + \"'; expecting a method name\", \"foo.PartitionSizeEntryMessage\", arg0);");
+ expected.outdent();
+
+ String actual = sw.toString();
+ assertEquals("Wrong source Lookup created.", expected.toString(), actual);
+ }
+
+ public void testCreateMethodForJMethodForSingleEntry() throws TypeOracleException {
+ JType stringType = oracle.parse(String.class.getName());
+ List<JMethod> methodsToCreate = stringCreator.getConstantsWithLookupCreator()
+ .findAllMethodsToCreate(stringMethod, stringType);
+ stringCreator.createMethodFor(stringMethod, methodsToCreate, null);
+
+ assertFalse("No partition lookup should created.", sw.toString().contains(
+ "java.lang.String getString0(java.lang.String arg0) {"));
+ }
+
+ public void testCreateMethodForJMethodForSingleIntEntry() throws TypeOracleException {
+ JType intType = oracle.parse(int.class.getName());
+ List<JMethod> methodsToCreate = intCreator.getConstantsWithLookupCreator()
+ .findAllMethodsToCreate(intMethod, intType);
+ intCreator.createMethodFor(intMethod, methodsToCreate, null);
+
+ String createdSource = sw.toString();
+ assertTrue("Lookup for intMethod not created.", createdSource.contains(
+ "int answer = intEntry();"));
+ }
+
+ public void testGetReturnTypeName() {
+ String returnType = stringCreator.getReturnTypeName();
+ assertEquals("java.lang.String", returnType);
+ }
+
+ public void testGetReturnTypeNameForPrimitveTypes() {
+ for (JPrimitiveType primitiveType : JPrimitiveType.values()) {
+ LookupMethodCreator primitiveMethodCreator = new LookupMethodCreator(null, primitiveType);
+ String returnType = primitiveMethodCreator.getReturnTypeName();
+ String expectedType = primitiveType.getQualifiedBoxedSourceName().substring("java.lang."
+ .length());
+ assertEquals("Wrong Return Type for primitve type", expectedType, returnType);
+ }
+ }
+
+ public void testPrintFound() {
+ stringCreator.printFound("callTest");
+
+ String returnStatement = sw.toString();
+ assertEquals("return callTest();\n", returnStatement);
+ }
+
+ private void initLookupMethodCreator(MockJavaResource resource) throws TypeOracleException {
+ initLookupMethodCreator(resource, TEST_PARTITION_SIZE);
+ }
+
+ private void initLookupMethodCreator(MockJavaResource resource, int partitionsSize)
+ throws TypeOracleException {
+ JClassType clazz = oracle.findType(resource.getTypeName());
+ ConstantsWithLookupImplCreator mockCreator;
+ try {
+ mockCreator = new ConstantsWithLookupImplCreator(logger, sw, clazz, mock(ResourceList.class),
+ oracle, partitionsSize);
+
+ JType stringType = oracle.parse(String.class.getName());
+ stringMethod = oracle.findType(LOOKUP.getTypeName()).findMethod("getString", new JType[] {
+ stringType});
+ JType intType = oracle.parse(int.class.getName());
+ intMethod = oracle.findType(LOOKUP.getTypeName()).findMethod("getInt", new JType[] {
+ stringType});
+
+ stringCreator = new LookupMethodCreator(mockCreator, stringType);
+ intCreator = new LookupMethodCreator(mockCreator, intType) {
+ @Override
+ public void printReturnTarget() {
+ println("return target.intValue();");
+ }
+
+ @Override
+ public String returnTemplate() {
+ return "int answer = {0}();\ncache.put(\"{0}\",new Integer(answer));\nreturn answer;";
+ }
+ };
+ } catch (UnableToCompleteException e) {
+ fail(e.getMessage());
+ }
+ }
+
+}
diff --git a/user/test/com/google/gwt/user/UserJreSuite.java b/user/test/com/google/gwt/user/UserJreSuite.java
new file mode 100644
index 0000000..43c2b97
--- /dev/null
+++ b/user/test/com/google/gwt/user/UserJreSuite.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user;
+
+import com.google.gwt.user.rebind.AbstractSourceCreatorTest;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * User tests running as a regular JRE test.
+ */
+public class UserJreSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite("Non-browser tests for com.google.gwt.user");
+
+ // $JUnit-BEGIN$
+ suite.addTestSuite(AbstractSourceCreatorTest.class);
+ // $JUnit-END$
+
+ return suite;
+ }
+
+}
diff --git a/user/test/com/google/gwt/user/rebind/AbstractSourceCreatorTest.java b/user/test/com/google/gwt/user/rebind/AbstractSourceCreatorTest.java
new file mode 100644
index 0000000..95dff8d
--- /dev/null
+++ b/user/test/com/google/gwt/user/rebind/AbstractSourceCreatorTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.rebind;
+
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+
+import junit.framework.TestCase;
+
+/**
+ * Used to test the {@link AbstractSourceCreator}.
+ */
+public class AbstractSourceCreatorTest extends TestCase {
+
+ public void testGetJavaObjectTypeForPrimitveTypes() {
+ for (JPrimitiveType primitiveType : JPrimitiveType.values()) {
+ String returnType = AbstractSourceCreator.getJavaObjectTypeFor(primitiveType);
+ String expectedType = primitiveType.getQualifiedBoxedSourceName().substring("java.lang."
+ .length());
+ assertEquals("Wrong Return Type for primitve type", expectedType, returnType);
+ }
+ }
+
+}