blob: 0a5de8b09a0f811467b0d4fed970c82c8d6d502c [file] [log] [blame]
/*
* 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.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.JParameter;
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.i18n.rebind.AbstractResource.ResourceList;
import com.google.gwt.i18n.shared.GwtLocale;
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, 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>.
*
* @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
*/
ConstantsWithLookupImplCreator(TreeLogger logger, SourceWriter writer,
JClassType localizableClass, ResourceList resourceList, TypeOracle oracle,
int partitionsSize) throws UnableToCompleteException {
super(logger, writer, localizableClass, resourceList, oracle);
this.partitionsSize = partitionsSize;
try {
// Boolean
JType booleanType = oracle.parse(boolean.class.getName());
LookupMethodCreator booleanMethod = new LookupMethodCreator(this,
booleanType) {
@Override
public void printReturnTarget() {
println("return target.booleanValue();");
}
@Override
public String returnTemplate() {
return "boolean answer = {0}();\n"
+ "cache.put(\"{0}\",new Boolean(answer));\n"
+ "return answer;";
}
};
namesToMethodCreators.put("getBoolean", booleanMethod);
// Double
JType doubleType = oracle.parse(double.class.getName());
LookupMethodCreator doubleMethod = new LookupMethodCreator(this,
doubleType) {
@Override
public void printReturnTarget() {
println("return target.doubleValue();");
}
@Override
public String returnTemplate() {
return "double answer = {0}();\n"
+ "cache.put(\"{0}\",new Double(answer));\n"
+ "return answer;";
}
};
namesToMethodCreators.put("getDouble", doubleMethod);
// Int
JType intType = oracle.parse(int.class.getName());
LookupMethodCreator intMethod = new LookupMethodCreator(this, intType) {
@Override
public void printReturnTarget() {
println("return target.intValue();");
}
@Override
public String returnTemplate() {
return "int answer = {0}();\n"
+ "cache.put(\"{0}\",new Integer(answer));\n"
+ "return answer;";
}
};
namesToMethodCreators.put("getInt", intMethod);
// Float
JType floatType = oracle.parse(float.class.getName());
LookupMethodCreator floatMethod = new LookupMethodCreator(this, floatType) {
@Override
public String returnTemplate() {
String val = "float answer = {0}();\n"
+ "cache.put(\"{0}\", new Float(answer));\n"
+ "return answer;";
return val;
}
@Override
protected void printReturnTarget() {
println("return target.floatValue();");
}
};
namesToMethodCreators.put("getFloat", floatMethod);
// Map - use erased type for matching
JType mapType = oracle.parse(Map.class.getName()).getErasedType();
namesToMethodCreators.put("getMap",
new LookupMethodCreator(this, mapType) {
@Override
public String getReturnTypeName() {
return ConstantsMapMethodCreator.GENERIC_STRING_MAP_TYPE;
}
});
// String
JType stringType = oracle.parse(String.class.getName());
LookupMethodCreator stringMethod = new LookupMethodCreator(this,
stringType) {
@Override
public String returnTemplate() {
return "String answer = {0}();\n"
+ "cache.put(\"{0}\",answer);\n"
+ "return answer;";
}
};
namesToMethodCreators.put("getString", stringMethod);
// String Array
JType stringArray = oracle.getArrayType(stringType);
namesToMethodCreators.put("getStringArray", new LookupMethodCreator(this,
stringArray));
setNeedCache(true);
allInterfaceMethods = getAllInterfaceMethods(localizableClass);
} catch (TypeOracleException e) {
throw error(logger, e);
}
}
@Override
protected void classEpilog() {
createNeededPartitionLookups();
super.classEpilog();
}
/**
* Create the method body associated with the given method. Arguments are
* arg0...argN.
*/
@Override
protected void emitMethodBody(TreeLogger logger, JMethod method,
GwtLocale locale) throws UnableToCompleteException {
checkMethod(logger, method);
if (method.getParameters().length == 1) {
String name = method.getName();
LookupMethodCreator c = getLookupMethodCreator(name);
if (c != null) {
createMethodWithPartitionCheckFor(c, method);
return;
}
}
// fall through
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>.
*
* @param method method to check
*/
private void checkMethod(TreeLogger logger, JMethod method)
throws UnableToCompleteException {
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
if (params.length == 0) {
checkConstantMethod(logger, method);
} else {
if (params.length != 1
|| !params[0].getType().getQualifiedSourceName().equals(
"java.lang.String")) {
throw error(logger, method + " must have a single String argument.");
}
checkReturnType(logger, method);
}
} else {
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);
}
}
}
}