/* * Copyright 2011 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.typeinfo.JArrayType; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JEnumConstant; import com.google.gwt.core.ext.typeinfo.JEnumType; import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.JParameter; import com.google.gwt.core.ext.typeinfo.JParameterizedType; 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.i18n.rebind.AbstractResource.ResourceEntry; import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.i18n.server.AbstractMessage; import com.google.gwt.i18n.server.AbstractParameter; import com.google.gwt.i18n.server.MessageInterface; import com.google.gwt.i18n.server.MessageTranslation; import com.google.gwt.i18n.server.Parameter; import com.google.gwt.i18n.server.Type; import com.google.gwt.i18n.shared.GwtLocale; import com.google.gwt.i18n.shared.GwtLocaleFactory; import com.google.gwt.safehtml.shared.SafeHtml; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; /** * An implementation of the {@link com.google.gwt.i18n.server.Message} API on * top of type oracle. */ public class TypeOracleMessage extends AbstractMessage { /** * An implementation of {@link Parameter} on top of type oracle. */ public class TypeOracleParameter extends AbstractParameter { private final JParameter param; public TypeOracleParameter(int index, JParameter param) { super(getLocaleFactory(), index, mapJTypeToType(param.getType())); this.param = param; } @Override public <A extends Annotation> A getAnnotation(Class<A> annotClass) { return param.getAnnotation(annotClass); } @Override public String getName() { return param.getName(); } @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotClass) { return param.isAnnotationPresent(annotClass); } } /** * A single translated message obtained from an {@link AbstractResource}. */ private class ResourceMessageTranslation implements MessageTranslation { private final ResourceEntry resourceEntry; private final GwtLocale matchedLocale; public ResourceMessageTranslation(ResourceEntry resourceEntry, GwtLocale matchedLocale) { this.resourceEntry = resourceEntry; this.matchedLocale = matchedLocale; } public Iterable<AlternateFormMapping> getAllMessageForms() { List<AlternateFormMapping> mapping = new ArrayList<AlternateFormMapping>(); // add the default form if (!isStringMap()) { mapping.add(new AlternateFormMapping(defaultForms(), getDefaultMessage())); } // add supplied forms int numSelectors = getSelectorParameterIndices().length; for (String joinedForms : resourceEntry.getForms()) { addMapping(mapping, numSelectors, joinedForms, resourceEntry.getForm(joinedForms)); } // sort into lexicographic order and return Collections.sort(mapping); return mapping; } public String getDefaultMessage() { return resourceEntry.getForm(null); } public GwtLocale getMatchedLocale() { return matchedLocale; } @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append("ResourceMT: ").append(resourceEntry); return buf.toString(); } } private final JMethod method; private final TypeOracle oracle; private List<Parameter> parameters; private final ResourceList resources; public TypeOracleMessage(TypeOracle oracle, GwtLocaleFactory localeFactory, MessageInterface msgIntf, JMethod method, ResourceList resources) { super(localeFactory, msgIntf); this.method = method; this.oracle = oracle; this.resources = resources; init(); } @Override public <A extends Annotation> A getAnnotation(Class<A> annotClass) { A annot = method.getAnnotation(annotClass); if (annot != null) { return annot; } return method.getEnclosingType().findAnnotationInTypeHierarchy(annotClass); } @Override public String getMethodName() { return method.getName(); } @Override public List<Parameter> getParameters() { if (parameters == null) { ensureParameters(); } return parameters; } @Override public Type getReturnType() { JType returnType = method.getReturnType(); return mapJTypeToType(returnType); } @Override public MessageTranslation getTranslation(GwtLocale locale) { /* * TODO(jat): Note that we don't actually follow the contract here, since * the ResourceList we were supplied with has already been filtered * according to the locale we should be called with, for the limited use * case today of generating translation output files. This will have to be * updated when this is used for generating code as well. */ ResourceEntry entry = null; if (resources != null) { entry = resources.getEntry(getKey()); } if (entry != null) { return new ResourceMessageTranslation(entry, resources.findLeastDerivedLocale(null, locale)); } return this; } @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotClass) { return method.isAnnotationPresent(annotClass); } public boolean isVarArgs() { return method.isVarArgs(); } protected Type mapJTypeToType(JType type) { JPrimitiveType primType = type.isPrimitive(); if (primType != null) { if (primType == JPrimitiveType.BOOLEAN) { return Type.BOOLEAN; } if (primType == JPrimitiveType.BYTE) { return Type.BYTE; } if (primType == JPrimitiveType.CHAR) { return Type.CHAR; } if (primType == JPrimitiveType.DOUBLE) { return Type.DOUBLE; } if (primType == JPrimitiveType.FLOAT) { return Type.FLOAT; } if (primType == JPrimitiveType.INT) { return Type.INT; } if (primType == JPrimitiveType.LONG) { return Type.LONG; } if (primType == JPrimitiveType.SHORT) { return Type.SHORT; } throw new RuntimeException("Unexpected primitive type " + primType); } JArrayType arrayType = type.isArray(); if (arrayType != null) { JType componentType = arrayType.getComponentType(); return new Type.ArrayType(componentType.getQualifiedSourceName() + "[]", mapJTypeToType(componentType)); } JEnumType enumType = type.isEnum(); String qualSourceName = type.getQualifiedSourceName(); if (enumType != null) { JEnumConstant[] enumConstants = enumType.getEnumConstants(); int n = enumConstants.length; String[] names = new String[n]; for (int i = 0; i < n; ++i) { names[i] = enumConstants[i].getName(); } return new Type.EnumType(qualSourceName, names); } String stringQualifiedName = String.class.getCanonicalName(); if (stringQualifiedName.equals(qualSourceName)) { return Type.STRING; } if (SafeHtml.class.getCanonicalName().equals(qualSourceName)) { return Type.SAFEHTML; } JClassType date = oracle.findType(Date.class.getCanonicalName()); JClassType classType = type.isClassOrInterface(); if (date != null && classType != null && date.isAssignableFrom(classType)) { return Type.DATE; } String listQualifiedName = List.class.getCanonicalName(); JClassType list = oracle.findType(listQualifiedName); JParameterizedType parameterizedType = type.isParameterized(); if (list != null && classType != null && list.isAssignableFrom(classType)) { if (parameterizedType == null) { // raw List usage return new Type.ListType(listQualifiedName + "<Object>", Type.OBJECT); } JType componentType = parameterizedType.getTypeArgs()[0]; return new Type.ListType(listQualifiedName + "<" + componentType.getQualifiedSourceName() + ">", mapJTypeToType(componentType)); } String mapQualifiedName = Map.class.getCanonicalName(); JClassType map = oracle.findType(mapQualifiedName); if (map != null && classType != null && map.isAssignableFrom(classType) && parameterizedType != null) { JClassType[] typeArgs = parameterizedType.getTypeArgs(); if (typeArgs.length == 2 && stringQualifiedName.equals(typeArgs[0].getQualifiedSourceName()) && stringQualifiedName.equals(typeArgs[1].getQualifiedSourceName())) { return Type.STRING_MAP; } } return new Type(qualSourceName); } private void ensureParameters() { parameters = new ArrayList<Parameter>(); int i = 0; for (JParameter parameter : method.getParameters()) { parameters.add(new TypeOracleParameter(i++, parameter)); } } }