Package com.google.gwt.i18n.rebind

Source Code of com.google.gwt.i18n.rebind.AnnotationsResource$ArgumentInfo

/*
* 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.typeinfo.JArrayType;
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.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JRawType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.i18n.client.PluralRule;
import com.google.gwt.i18n.client.Constants.DefaultBooleanValue;
import com.google.gwt.i18n.client.Constants.DefaultDoubleValue;
import com.google.gwt.i18n.client.Constants.DefaultFloatValue;
import com.google.gwt.i18n.client.Constants.DefaultIntValue;
import com.google.gwt.i18n.client.Constants.DefaultStringArrayValue;
import com.google.gwt.i18n.client.Constants.DefaultStringMapValue;
import com.google.gwt.i18n.client.Constants.DefaultStringValue;
import com.google.gwt.i18n.client.LocalizableResource.DefaultLocale;
import com.google.gwt.i18n.client.LocalizableResource.Description;
import com.google.gwt.i18n.client.LocalizableResource.GenerateKeys;
import com.google.gwt.i18n.client.LocalizableResource.Key;
import com.google.gwt.i18n.client.LocalizableResource.Meaning;
import com.google.gwt.i18n.client.Messages.DefaultMessage;
import com.google.gwt.i18n.client.Messages.Example;
import com.google.gwt.i18n.client.Messages.Optional;
import com.google.gwt.i18n.client.Messages.PluralCount;
import com.google.gwt.i18n.client.Messages.PluralText;
import com.google.gwt.i18n.rebind.keygen.KeyGenerator;
import com.google.gwt.i18n.rebind.keygen.MethodNameKeyGenerator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
* AbstractResource implementation which looks up text annotations on classes.
*/
public class AnnotationsResource extends AbstractResource {

  /**
   * An exception indicating there was some problem with an annotation.
   *
   * A caller receiving this exception should log the human-readable message and
   * treat it as a fatal error.
   */
  public static class AnnotationsError extends Exception {

    public AnnotationsError(String msg) {
      super(msg);
    }
  }

  /**
   * Class for argument information, used for export.
   */
  public static class ArgumentInfo {
    public String example;
    public boolean isPluralCount;
    public String name;
    public boolean optional;
    public Class<? extends PluralRule> pluralRuleClass;

    public ArgumentInfo(String name) {
      this.name = name;
    }
  }

  /**
   * Class to keep annotation information about a particular method.
   */
  private static class MethodEntry {

    public ArrayList<ArgumentInfo> arguments;
    public String description;
    public String meaning;
    public Map<String, String> pluralText;
    public String text;

    public MethodEntry(String text, String meaning) {
      this.text = text;
      this.meaning = meaning;
      pluralText = new HashMap<String, String>();
      arguments = new ArrayList<ArgumentInfo>();
    }

    public ArgumentInfo addArgument(String argName) {
      ArgumentInfo argInfo = new ArgumentInfo(argName);
      arguments.add(argInfo);
      return argInfo;
    }

    public void addPluralText(String form, String text) {
      pluralText.put(form, text);
    }
  }

  /**
   * Returns the key for a given method.
   *
   * If null is returned, an error message has already been logged.
   *
   * @return null if unable to get or compute the key for this method, otherwise
   *         the key is returned
   */
  public static String getKey(TreeLogger logger, KeyGenerator keyGenerator,
      JMethod method, boolean isConstants) {
    Key key = method.getAnnotation(Key.class);
    if (key != null) {
      return key.value();
    }
    String text;
    try {
      text = getTextString(method, null, isConstants);
    } catch (AnnotationsError e) {
      return null;
    }
    String meaningString = null;
    Meaning meaning = method.getAnnotation(Meaning.class);
    if (meaning != null) {
      meaningString = meaning.value();
    }
    String keyStr = keyGenerator.generateKey(
        method.getEnclosingType().getQualifiedSourceName(), method.getName(),
        text, meaningString);
    if (keyStr == null) {
      if (text == null) {
        logger.log(
            TreeLogger.ERROR,
            "Key generator "
                + keyGenerator.getClass().getName()
                + " requires the default value be specified in an annotation for method "
                + method.getName(), null);
      } else {
        logger.log(TreeLogger.ERROR, "Key generator "
            + keyGenerator.getClass().getName()
            + " was unable to compute a key value for method "
            + method.getName(), null);
      }
    }
    return keyStr;
  }

  /**
   * Returns a suitable key generator for the specified class.
   *
   * @throws AnnotationsError
   */
  public static KeyGenerator getKeyGenerator(JClassType targetClass)
      throws AnnotationsError {
    GenerateKeys generator = targetClass.getAnnotation(GenerateKeys.class);
    if (generator != null) {
      String className = generator.value();
      try {
        Class<? extends KeyGenerator> keyGeneratorClass = Class.forName(
            className, false, KeyGenerator.class.getClassLoader()).asSubclass(
            KeyGenerator.class);
        return keyGeneratorClass.newInstance();
      } catch (InstantiationException e) {
        throw new AnnotationsError("@GenerateKeys: unable to instantiate "
            + className);
      } catch (IllegalAccessException e) {
        throw new AnnotationsError("@GenerateKeys: unable to instantiate "
            + className);
      } catch (ClassNotFoundException e) {
        throw new AnnotationsError("Invalid class specified to @GenerateKeys: "
            + className);
      }
    }
    return new MethodNameKeyGenerator();
  }

  /**
   * Return the text string from annotations for a particular method.
   *
   * @param method the method to retrieve text
   * @param map if not null, add keys for DefaultStringMapValue to this map
   * @param isConstants true if the method is in a subinterface of Constants
   * @throws AnnotationsError if the annotation usage is incorrect
   * @return the text value to use for this method, as if read from a properties
   *         file, or null if there are no annotations.
   */
  private static String getTextString(JMethod method,
      Map<String, MethodEntry> map, boolean isConstants)
      throws AnnotationsError {
    JType returnType = method.getReturnType();
    DefaultMessage defaultText = method.getAnnotation(DefaultMessage.class);
    DefaultStringValue stringValue = method.getAnnotation(DefaultStringValue.class);
    DefaultStringArrayValue stringArrayValue = method.getAnnotation(DefaultStringArrayValue.class);
    DefaultStringMapValue stringMapValue = method.getAnnotation(DefaultStringMapValue.class);
    DefaultIntValue intValue = method.getAnnotation(DefaultIntValue.class);
    DefaultFloatValue floatValue = method.getAnnotation(DefaultFloatValue.class);
    DefaultDoubleValue doubleValue = method.getAnnotation(DefaultDoubleValue.class);
    DefaultBooleanValue booleanValue = method.getAnnotation(DefaultBooleanValue.class);
    int constantsCount = 0;
    if (stringValue != null) {
      constantsCount++;
      if (!returnType.getQualifiedSourceName().equals("java.lang.String")) {
        throw new AnnotationsError(
            "@DefaultStringValue can only be used with a method returning String");
      }
    }
    if (stringArrayValue != null) {
      constantsCount++;
      JArrayType arrayType = returnType.isArray();
      if (arrayType == null
          || !arrayType.getComponentType().getQualifiedSourceName().equals(
              "java.lang.String")) {
        throw new AnnotationsError(
            "@DefaultStringArrayValue can only be used with a method returning String[]");
      }
    }
    if (stringMapValue != null) {
      constantsCount++;
      JRawType rawType = returnType.getErasedType().isRawType();
      boolean error = false;
      if (rawType == null
          || !rawType.getQualifiedSourceName().equals("java.util.Map")) {
        error = true;
      } else {
        JParameterizedType paramType = returnType.isParameterized();
        if (paramType != null) {
          JType[] args = paramType.getTypeArgs();
          if (args.length != 2
              || !args[0].getQualifiedSourceName().equals("java.lang.String")
              || !args[1].getQualifiedSourceName().equals("java.lang.String")) {
            error = true;
          }
        }
      }
      if (error) {
        throw new AnnotationsError(
            "@DefaultStringMapValue can only be used with a method "
                + "returning Map or Map<String,String>");
      }
    }
    if (intValue != null) {
      constantsCount++;
      JPrimitiveType primType = returnType.isPrimitive();
      if (primType != JPrimitiveType.INT) {
        throw new AnnotationsError(
            "@DefaultIntValue can only be used with a method returning int");
      }
    }
    if (floatValue != null) {
      constantsCount++;
      JPrimitiveType primType = returnType.isPrimitive();
      if (primType != JPrimitiveType.FLOAT) {
        throw new AnnotationsError(
            "@DefaultFloatValue can only be used with a method returning float");
      }
    }
    if (doubleValue != null) {
      constantsCount++;
      JPrimitiveType primType = returnType.isPrimitive();
      if (primType != JPrimitiveType.DOUBLE) {
        throw new AnnotationsError(
            "@DefaultDoubleValue can only be used with a method returning double");
      }
    }
    if (booleanValue != null) {
      constantsCount++;
      JPrimitiveType primType = returnType.isPrimitive();
      if (primType != JPrimitiveType.BOOLEAN) {
        throw new AnnotationsError(
            "@DefaultBooleanValue can only be used with a method returning boolean");
      }
    }
    if (!isConstants) {
      if (constantsCount > 0) {
        throw new AnnotationsError(
            "@Default*Value is not permitted on a Messages interface; see @DefaultText");
      }
      if (defaultText != null) {
        return defaultText.value();
      }
    } else {
      if (defaultText != null) {
        throw new AnnotationsError(
            "@DefaultText is not permitted on a Constants interface; see @Default*Value");
      }
      if (constantsCount > 1) {
        throw new AnnotationsError(
            "No more than one @Default*Value annotation may be used on a method");
      }
      if (stringValue != null) {
        return stringValue.value();
      } else if (intValue != null) {
        return Integer.toString(intValue.value());
      } else if (floatValue != null) {
        return Float.toString(floatValue.value());
      } else if (doubleValue != null) {
        return Double.toString(doubleValue.value());
      } else if (booleanValue != null) {
        return Boolean.toString(booleanValue.value());
      } else if (stringArrayValue != null) {
        StringBuilder buf = new StringBuilder();
        boolean firstString = true;
        for (String str : stringArrayValue.value()) {
          str = str.replace("\\", "\\\\");
          str = str.replace(",", "\\,");
          if (!firstString) {
            buf.append(',');
          } else {
            firstString = false;
          }
          buf.append(str);
        }
        return buf.toString();
      } else if (stringMapValue != null) {
        StringBuilder buf = new StringBuilder();
        boolean firstString = true;
        String[] entries = stringMapValue.value();
        if ((entries.length & 1) != 0) {
          throw new AnnotationsError(
              "Odd number of strings supplied to @DefaultStringMapValue");
        }
        for (int i = 0; i < entries.length; i += 2) {
          String key = entries[i];
          String value = entries[i + 1];

          if (map != null) {
            // add key=value part to map
            MethodEntry entry = new MethodEntry(value, null);
            map.put(key, entry);
          }

          // add the key to the master entry
          key = key.replace("\\", "\\\\");
          key = key.replace(",", "\\,");
          if (!firstString) {
            buf.append(',');
          } else {
            firstString = false;
          }
          buf.append(key);
        }
        return buf.toString();
      }
    }
    return null;
  }

  private Map<String, MethodEntry> map;

  /**
   * Create a resource that supplies data from i18n-related annotations.
   *
   * @param logger
   * @param clazz
   * @param locale
   * @param isConstants
   * @throws AnnotationsError if there is a fatal error while processing
   *           annotations
   */
  public AnnotationsResource(TreeLogger logger, JClassType clazz,
      String locale, boolean isConstants) throws AnnotationsError {
    KeyGenerator keyGenerator = getKeyGenerator(clazz);
    map = new HashMap<String, MethodEntry>();
    setPath(clazz.getQualifiedSourceName());
    DefaultLocale defLocale = clazz.getAnnotation(DefaultLocale.class);
    if (defLocale != null && !ResourceFactory.DEFAULT_TOKEN.equals(locale)
        && !locale.equalsIgnoreCase(defLocale.value())) {
      logger.log(TreeLogger.WARN, "@DefaultLocale on "
          + clazz.getQualifiedSourceName() + " doesn't match " + locale)
      return;
    }
    for (JMethod method : clazz.getMethods()) {
      String meaningString = null;
      Meaning meaning = method.getAnnotation(Meaning.class);
      if (meaning != null) {
        meaningString = meaning.value();
      }
      String textString = getTextString(method, map, isConstants);
      if (textString == null) {
        // ignore ones without some value annotation
        continue;
      }
      String key = null;
      Key keyAnnot = method.getAnnotation(Key.class);
      if (keyAnnot != null) {
        key = keyAnnot.value();
      } else {
        key = keyGenerator.generateKey(
            method.getEnclosingType().getQualifiedSourceName(),
            method.getName(), textString, meaningString);
      }
      if (key == null) {
        throw new AnnotationsError("Could not compute key for "
            + method.getEnclosingType().getQualifiedSourceName() + "."
            + method.getName());
      }
      MethodEntry entry = new MethodEntry(textString, meaningString);
      map.put(key, entry);
      Description description = method.getAnnotation(Description.class);
      if (description != null) {
        entry.description = description.value();
      }
      PluralText pluralText = method.getAnnotation(PluralText.class);
      if (pluralText != null) {
        String[] pluralForms = pluralText.value();
        if ((pluralForms.length & 1) != 0) {
          throw new AnnotationsError(
              "Odd number of strings supplied to @PluralText: must be"
              + " pairs of form names and strings");
        }
        for (int i = 0; i + 1 < pluralForms.length; i += 2) {
          entry.addPluralText(pluralForms[i], pluralForms[i + 1]);
        }
      }
      for (JParameter param : method.getParameters()) {
        ArgumentInfo argInfo = entry.addArgument(param.getName());
        Optional optional = param.getAnnotation(Optional.class);
        if (optional != null) {
          argInfo.optional = true;
        }
        PluralCount pluralCount = param.getAnnotation(PluralCount.class);
        if (pluralCount != null) {
          argInfo.isPluralCount = true;
        }
        Example example = param.getAnnotation(Example.class);
        if (example != null) {
          argInfo.example = example.value();
        }
      }
    }
  }

  @Override
  public void addToKeySet(Set<String> s) {
    s.addAll(map.keySet());
  }

  public Iterable<ArgumentInfo> argumentsIterator(String key) {
    MethodEntry entry = map.get(key);
    return entry != null ? entry.arguments : null;
  }

  public String getDescription(String key) {
    MethodEntry entry = map.get(key);
    return entry == null ? null : entry.description;
  }

  @Override
  public Collection<String> getExtensions(String key) {
    MethodEntry entry = map.get(key);
    return entry == null ? new ArrayList<String>() : entry.pluralText.keySet();
  }

  public String getMeaning(String key) {
    MethodEntry entry = map.get(key);
    return entry == null ? null : entry.meaning;
  }

  @Override
  public String getStringExt(String key, String extension) {
    MethodEntry entry = map.get(key);
    if (entry == null) {
      return null;
    }
    if (extension != null) {
      return entry.pluralText.get(extension);
    } else {
      return entry.text;
    }
  }

  @Override
  public boolean notEmpty() {
    return !map.isEmpty();
  }

  @Override
  public String toString() {
    return "Annotations from class " + getPath();
  }
}
TOP

Related Classes of com.google.gwt.i18n.rebind.AnnotationsResource$ArgumentInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.