6 package com.google.appinventor.components.scripts;
 
    8 import java.io.IOException;
 
   10 import java.util.HashSet;
 
   11 import java.util.TreeMap;
 
   12 import java.util.TreeSet;
 
   13 import java.util.LinkedHashMap;
 
   17 import javax.tools.Diagnostic;
 
   18 import javax.tools.Diagnostic.Kind;
 
   19 import javax.tools.FileObject;
 
   23   private static final String OUTPUT_FILE_NAME = 
"ComponentsTranslation.java";
 
   24   private static final String AUTOGEN_OUTPUT_FILE_NAME = 
"AutogeneratedOdeMessages.java";
 
   26   private Map<String, String> tooltips = 
new TreeMap<>();
 
   27   private Map<String, Set<String>> tooltipComponent = 
new TreeMap<>();
 
   28   private Set<String> collisionKeys = 
new HashSet<>();
 
   30   private void outputComponent(
ComponentInfo component, Set<String> outProperties,
 
   31       Set<String> outMethods, Set<String> outEvents, StringBuilder sb) {
 
   32     if (component.getExternal()) { 
 
   35     Map<String, Parameter> parameters = 
new LinkedHashMap<String, Parameter>();
 
   36     sb.append(
"\n\n/* Component: " + component.getName() + 
" */\n\n");
 
   37     sb.append(
"    map.put(\"COMPONENT-" + component.getName() + 
"\", MESSAGES." 
   38         + Character.toLowerCase(component.getName().charAt(0)) + component.getName().substring(1)
 
   39         + 
"ComponentPallette());\n\n");
 
   40     sb.append(
"    map.put(\"" + component.getName() + 
"-helpString\", MESSAGES." 
   41         + component.getName() + 
"HelpStringComponentPallette());\n\n");
 
   42     sb.append(
"\n\n/* Properties */\n\n");
 
   43     for (Property prop : component.
properties.values()) {
 
   44       String propertyName = prop.name;
 
   45       if (prop.isUserVisible()
 
   47           || prop.isDeprecated() 
 
   50         sb.append(
"    map.put(\"PROPERTY-" + propertyName + 
"\", MESSAGES." + propertyName + 
"Properties());\n");
 
   51         outProperties.add(propertyName);
 
   54     sb.append(
"\n\n/* Events */\n\n");
 
   56       String propertyName = 
event.name;
 
   61         sb.append(
"    map.put(\"EVENT-" + propertyName + 
"\", MESSAGES." + propertyName + 
"Events());\n");
 
   62         for (
Parameter parameter : event.parameters) {
 
   63           parameters.put(parameter.
name, parameter);
 
   65         outEvents.add(propertyName);
 
   68     sb.append(
"\n\n/* Methods */\n\n");
 
   70       String propertyName = method.name;
 
   71       if (method.userVisible
 
   75         sb.append(
"    map.put(\"METHOD-" + propertyName + 
"\", MESSAGES." + propertyName + 
"Methods());\n");
 
   76         for (
Parameter parameter : method.parameters) {
 
   77           parameters.put(parameter.
name, parameter);
 
   79         outMethods.add(propertyName);
 
   84     parameters.put(
"notAlreadyHandled", 
new Parameter(
"notAlreadyHandled", 
"boolean"));
 
   85     sb.append(
"\n\n/* Parameters */\n\n");
 
   86     for (
Parameter parameter : parameters.values()) {
 
   87       sb.append(
"    map.put(\"PARAM-" + parameter.name + 
"\", MESSAGES." +
 
   88           Character.toLowerCase(parameter.name.charAt(0)) + parameter.name.substring(1) +
 
   93   private void outputCategory(String category, StringBuilder sb) {
 
   95     String[] parts = category.split(
" ");
 
   96     sb.append(
"    map.put(\"CATEGORY-" + category + 
"\", MESSAGES." + parts[0].replaceAll(
"[^A-Za-z0-9]", 
"").toLowerCase());
 
   97     for (
int i = 1; i < parts.length; i++) {
 
   98       String lower = parts[i].replaceAll(
"[^A-Za-z0-9]", 
"").toLowerCase();
 
   99       sb.append(Character.toUpperCase(lower.charAt(0)));
 
  100       sb.append(lower.substring(1));
 
  102     sb.append(
"ComponentPallette());\n");
 
  105   private void outputPropertyCategory(String category, StringBuilder sb) {
 
  106     sb.append(
"    map.put(\"CATEGORY-");
 
  108     sb.append(
"\", MESSAGES.");
 
  110     sb.append(
"PropertyCategory());\n");
 
  114       Map<String, Property> outProperties, Map<String, Method> outMethods,
 
  115       Map<String, Event> outEvents, StringBuilder sb) {
 
  116     sb.append(
"  @DefaultMessage(\"");
 
  117     sb.append(component.getName());
 
  119     sb.append(
"  @Description(\"\")\n");
 
  120     sb.append(
"  String ");
 
  121     sb.append(Character.toLowerCase(component.getName().charAt(0)));
 
  122     sb.append(component.getName().substring(1));
 
  123     sb.append(
"ComponentPallette();\n\n");
 
  124     sb.append(
"  @DefaultMessage(\"");
 
  125     sb.append(sanitize(component.description));
 
  127     sb.append(
"  @Description(\"\")\n");
 
  128     sb.append(
"  String ");
 
  129     sb.append(component.getName());
 
  130     sb.append(
"HelpStringComponentPallette();\n\n");
 
  131     for (Property property : component.
properties.values()) {
 
  132       if (property.isUserVisible() || component.
designerProperties.containsKey(property.name) ||
 
  133           property.isDeprecated()) {
 
  134         outProperties.put(property.name, property);
 
  138       if (method.userVisible || method.deprecated) {
 
  139         outMethods.put(method.name, method);
 
  143       if (event.userVisible || event.deprecated) {
 
  144         outEvents.put(event.name, event);
 
  149   private void outputPropertyAutogen(Property property, StringBuilder sb) {
 
  150     sb.append(
"  @DefaultMessage(\"");
 
  151     sb.append(sanitize(property.name));
 
  153     sb.append(
"  @Description(\"\")\n");
 
  154     sb.append(
"  String ");
 
  155     sb.append(property.name);
 
  156     sb.append(
"Properties();\n\n");
 
  159   private void outputMethodAutogen(
Method method, Map<String, Parameter> outParameters, StringBuilder sb) {
 
  160     sb.append(
"  @DefaultMessage(\"");
 
  161     sb.append(sanitize(method.name));
 
  163     sb.append(
"  @Description(\"\")\n");
 
  164     sb.append(
"  String ");
 
  165     sb.append(method.name);
 
  166     sb.append(
"Methods();\n\n");
 
  168       outParameters.put(param.
name, param);
 
  172   private void outputEventAutogen(
Event event, Map<String, Parameter> outParameters, StringBuilder sb) {
 
  173     sb.append(
"  @DefaultMessage(\"");
 
  174     sb.append(sanitize(event.name));
 
  176     sb.append(
"  @Description(\"\")\n");
 
  177     sb.append(
"  String ");
 
  178     sb.append(event.name);
 
  179     sb.append(
"Events();\n\n");
 
  181       outParameters.put(param.
name, param);
 
  185   private void outputParameterAutogen(
Parameter parameter, StringBuilder sb) {
 
  186     sb.append(
"  @DefaultMessage(\"");
 
  187     sb.append(sanitize(parameter.
name));
 
  189     sb.append(
"  @Description(\"\")\n");
 
  190     sb.append(
"  String ");
 
  191     sb.append(parameter.
name);
 
  192     sb.append(
"Params();\n\n");
 
  195   private void outputCategoryAutogen(String category, StringBuilder sb) {
 
  196     String[] parts = category.split(
" ");
 
  197     sb.append(
"  @DefaultMessage(\"");
 
  200     sb.append(
"  @Description(\"\")\n");
 
  201     sb.append(
"  String ");
 
  202     sb.append(parts[0].replaceAll(
"[^A-Za-z0-9]", 
"").toLowerCase());
 
  203     for (
int i = 1; i < parts.length; i++) {
 
  204       String lower = parts[i].replaceAll(
"[^A-Za-z0-9]", 
"").toLowerCase();
 
  205       sb.append(Character.toUpperCase(lower.charAt(0)));
 
  206       sb.append(lower.substring(1));
 
  208     sb.append(
"ComponentPallette();\n\n");
 
  211   private void outputPropertyCategoryAutogen(String category, StringBuilder sb) {
 
  212     sb.append(
"  @DefaultMessage(\"");
 
  215     sb.append(
"  @Description(\"\")\n");
 
  216     sb.append(
"  String ");
 
  218     sb.append(
"PropertyCategory();\n\n");
 
  221   private String sanitize(String input) {
 
  222     return input.replaceAll(
"\r", 
"").replaceAll(
"\n", 
"").replaceAll(
"\\\\", 
"\\\\\\\\")
 
  223         .replaceAll(
"\"", 
"\\\\\"").replaceAll(
"'", 
"''").replaceAll(
"[ \t]+", 
" ").trim();
 
  226   private void storeTooltip(
ComponentInfo component, String name, String suffix,
 
  227       String description) {
 
  228     String key = name + suffix;
 
  229     String value = tooltips.get(key);
 
  230     if (collisionKeys.contains(key)) {
 
  232       key = component.getName() + 
"__" + key;
 
  233       tooltips.put(key, description);
 
  234     } 
else if (value == 
null) {
 
  236       tooltips.put(key, description);
 
  240     } 
else if (!value.equals(description)) {
 
  242       collisionKeys.add(key);
 
  243       for (String componentName : tooltipComponent.get(key)) {
 
  244         tooltips.put(componentName + 
"__" + key, value);
 
  246       key = component.getName() + 
"__" + key;
 
  247       tooltips.put(key, description);
 
  251       tooltipComponent.get(key).add(component.getName());
 
  256     for (Property property : component.
properties.values()) {
 
  257       storeTooltip(component, property.name, 
"PropertyDescriptions", property.getDescription());
 
  260       storeTooltip(component, method.name, 
"MethodDescriptions", method.description);
 
  263       storeTooltip(component, event.name, 
"EventDescriptions", event.description);
 
  268     Set<String> categories = 
new TreeSet<>();
 
  269     Map<String, Property> properties = 
new TreeMap<>();
 
  270     Map<String, Method> methods = 
new TreeMap<>();
 
  271     Map<String, Event> events = 
new TreeMap<>();
 
  272     Map<String, Parameter> parameters = 
new TreeMap<>();
 
  273     StringBuilder sb = 
new StringBuilder();
 
  274     sb.append(
"// THIS FILE IS AUTOMATICALLY GENERATED DURING COMPILATION.\n");
 
  275     sb.append(
"// DO NOT EDIT THIS FILE. ANY CHANGES WILL BE OVERWRITTEN.\n\n");
 
  276     sb.append(
"package com.google.appinventor.client;\n\n");
 
  277     sb.append(
"import com.google.gwt.i18n.client.Messages;\n\n");
 
  278     sb.append(
"public interface AutogeneratedOdeMessages extends Messages {\n");
 
  279     sb.append(
"\n  /* Components */\n");
 
  282       outputComponentAutogen(component, properties, methods, events, sb);
 
  283       computeTooltipMap(component);
 
  286     for (String key : collisionKeys) {
 
  287       tooltips.remove(key);
 
  289     sb.append(
"\n  /* Properties */\n");
 
  290     for (Map.Entry<String, Property> entry : properties.entrySet()) {
 
  291       outputPropertyAutogen(entry.getValue(), sb);
 
  293     sb.append(
"\n  /* Methods */\n");
 
  294     for (Map.Entry<String, 
Method> entry : methods.entrySet()) {
 
  295       outputMethodAutogen(entry.getValue(), parameters, sb);
 
  297     sb.append(
"\n  /* Events */\n");
 
  298     for (Map.Entry<String, 
Event> entry : events.entrySet()) {
 
  299       outputEventAutogen(entry.getValue(), parameters, sb);
 
  301     for (Map.Entry<String, String> entry : tooltips.entrySet()) {
 
  302       sb.append(
"  @DefaultMessage(\"");
 
  303       sb.append(sanitize(entry.getValue()));
 
  305       sb.append(
"  @Description(\"\")\n");
 
  306       sb.append(
"  String ");
 
  307       sb.append(entry.getKey());
 
  308       sb.append(
"();\n\n");
 
  310     sb.append(
"\n  /* Parameters */\n");
 
  311     for (Map.Entry<String, 
Parameter> entry : parameters.entrySet()) {
 
  312       outputParameterAutogen(entry.getValue(), sb);
 
  314     sb.append(
"\n  /* Component Categories */\n");
 
  315     for (String category : categories) {
 
  316       outputCategoryAutogen(category, sb);
 
  318     sb.append(
"\n  /* Property Categories */\n");
 
  319     outputPropertyCategoryAutogen(
"Appearance", sb);
 
  320     outputPropertyCategoryAutogen(
"Behavior", sb);
 
  321     outputPropertyCategoryAutogen(
"Unspecified", sb);
 
  323     FileObject src = createOutputFileObject(AUTOGEN_OUTPUT_FILE_NAME);
 
  324     Writer writer = src.openWriter();
 
  325     writer.write(sb.toString());
 
  328     messager.printMessage(Kind.NOTE, 
"Wrote file " + src.toUri());
 
  334     StringBuilder sb = 
new StringBuilder();
 
  335     sb.append(
"package com.google.appinventor.client;\n");
 
  337     sb.append(
"import java.util.HashMap;\n");
 
  338     sb.append(
"import java.util.Map;\n");
 
  340     sb.append(
"import static com.google.appinventor.client.Ode.MESSAGES;\n");
 
  342     sb.append(
"public class ComponentsTranslation {\n");
 
  343     sb.append(
"  public static Map<String, String> myMap = map();\n\n");
 
  344     sb.append(
"  private static String getName(String key) {\n");
 
  345     sb.append(
"    String value = myMap.get(key);\n");
 
  346     sb.append(
"    if (key == null) {\n");
 
  347     sb.append(
"      return \"**Missing key in ComponentsTranslations**\";\n");
 
  348     sb.append(
"    } else {\n");
 
  349     sb.append(
"      return value;\n");
 
  352     sb.append(
"  public static String getPropertyName(String key) {\n");
 
  353     sb.append(
"    String value = getName(\"PROPERTY-\" + key);\n");
 
  354     sb.append(
"    if(value == null) return key;\n");
 
  355     sb.append(
"    return value;\n");
 
  357     sb.append(
"  public static String getPropertyDescription(String key) {\n");
 
  358     sb.append(
"    String value = getName(\"PROPDESC-\" + key);\n");
 
  359     sb.append(
"    if(value == null) return key;\n");
 
  360     sb.append(
"    return value;\n");
 
  362     sb.append(
"  public static String getMethodName(String key) {\n");
 
  363     sb.append(
"    String value = getName(\"METHOD-\" + key);\n");
 
  364     sb.append(
"    if(value == null) return key;\n");
 
  365     sb.append(
"    return value;\n");
 
  368     sb.append(
"  public static String getEventName(String key) {\n");
 
  369     sb.append(
"    String value = getName(\"EVENT-\" + key);\n");
 
  370     sb.append(
"    if(value == null) return key;\n");
 
  371     sb.append(
"    return value;\n");
 
  374     sb.append(
"  public static String getComponentName(String key) {\n");
 
  375     sb.append(
"    String value = getName(\"COMPONENT-\" + key);\n");
 
  376     sb.append(
"    if(value == null) return key;\n");
 
  377     sb.append(
"    return value;\n");
 
  380     sb.append(
"  public static String getCategoryName(String key) {\n");
 
  381     sb.append(
"    String value = getName(\"CATEGORY-\" + key);\n");
 
  382     sb.append(
"    if(value == null) return key;\n");
 
  383     sb.append(
"    return value;\n");
 
  386     sb.append(
"  public static String getComponentHelpString(String key) {\n");
 
  387     sb.append(
"    String value = getName(key + \"-helpString\");\n");
 
  388     sb.append(
"    if(value == null) return key;\n");
 
  389     sb.append(
"    return value;\n");
 
  391     sb.append(
"  public static HashMap<String, String> map() {\n");
 
  392     sb.append(
"    HashMap<String, String> map = new HashMap<String, String>();\n");
 
  395     Set<String> categories = 
new TreeSet<>();
 
  396     Set<String> properties = 
new TreeSet<>();
 
  397     Set<String> methods = 
new TreeSet<>();
 
  398     Set<String> events = 
new TreeSet<>();
 
  401       outputComponent(component, properties, methods, events, sb);
 
  404     sb.append(
"\n\n    /* Descriptions */\n\n");
 
  405     for (String key : tooltips.keySet()) {
 
  406       if (key.endsWith(
"PropertyDescriptions")) {
 
  407         sb.append(
"    map.put(\"PROPDESC-");
 
  408         sb.append(key.replaceAll(
"__", 
"."));
 
  409         sb.append(
"\", MESSAGES.");
 
  412       } 
else if (key.endsWith(
"MethodDescriptions")) {
 
  413         sb.append(
"    map.put(\"METHODDESC-");
 
  414         sb.append(key.replaceAll(
"__", 
"."));
 
  415         sb.append(
"\", MESSAGES.");
 
  418       } 
else if (key.endsWith(
"EventDescriptions")) {
 
  419         sb.append(
"    map.put(\"EVENTDESC-");
 
  420         sb.append(key.replaceAll(
"__", 
"."));
 
  421         sb.append(
"\", MESSAGES.");
 
  426     sb.append(
"\n\n    /* Categories */\n\n");
 
  427     for (String category : categories) {
 
  428       outputCategory(category, sb);
 
  430     sb.append(
"  return map;\n");
 
  433     FileObject src = createOutputFileObject(OUTPUT_FILE_NAME);
 
  434     Writer writer = src.openWriter();
 
  435     writer.write(sb.toString());
 
  438     messager.printMessage(Diagnostic.Kind.NOTE, 
"Wrote file " + src.toUri());