Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GWT Maven plugin: Generating non-String parameters in the Messages class

Tags:

java

maven

gwt

I have a property in my "Messages.properties" file that has an argument that uses number formatting:

my.message=File exceeds {0,number,0.0}MB.

When I run the gwt:i18n Maven goal, it generates a Messages interface based on the properties in my "Messages.properties" file (like normal):

public interface Messages extends com.google.gwt.i18n.client.Messages {
  //...

  @DefaultMessage("File exceeds {0,number,0.0}MB.")
  @Key("my.message")
  String my_message(String arg0);

  //...
}

The problem is that the method parameter is a String. When I run the application, it gives me an error because the message argument expects a number, but a String is supplied (the error message is, "Only Number subclasses may be formatted as a number").

How do I configure Maven to have it change this parameter to number (like a float or Number)? Thanks.

like image 824
Michael Avatar asked Oct 21 '11 19:10

Michael


1 Answers

It seems to me this feature is not supported by GWT I18NCreator (which is what the maven i18n goal calls). You would have to write your own Generator to do that. I have written a couple of Generators and it's not as hard as you may think. In your case, you would want to write a Generator that creates an instance of an interface similar to GWT's Messages (but you can use your own) but which has the added functionality that you want when decoding messages. The following how-to little guide may help you, as it seems it's pretty much what I did as well and it works:

http://groups.google.com/group/Google-Web-Toolkit/msg/ae249ea67c2c3435?pli=1

I found that the easiest way to write a GWT Generator is to actually write a test class with the code you would want generated in your IDE (and with the help of auto-completion, syntax-checks etc), and then past/adapt it to the writer calls like this:

writer.println("public void doSomething() { /* implement */ }");

And don't forget to tell your module (module.gwt.xml file) which interface needs to be generated, and with which class, like this:

<generate-with  class="mycompany.utils.generators.MyGenerator">
    <when-type-assignable class="mycompany.messages.MyCoolPropertiesReader" />
</generate-with>

In the Generator code, you can use Java with all its great features (not limited to GWT-translatable code) so it shouldn't be hard to implement what you want. In the client-side code, you can then just do:

public interface MyCoolPropertiesReader {
  public String getMessage(String propKey, Object... parameters);
}

public class MyClientSideClass {
  MyCoolPropertiesReader reader = GWT.create(MyCoolPropertiesReader.class);
  String msg = reader.getMessage("my.message", 10);
  // do more work
}

A test Generator that I wrote (a GWT "reflective" getter and setter, as it were) looks like this:

public class TestGenerator extends Generator {

@Override
public String generate(TreeLogger logger, GeneratorContext context,
  String typeName) throws UnableToCompleteException {
  try {
    TypeOracle oracle = context.getTypeOracle();
    JClassType requestedClass = oracle.getType(typeName);
    String packageName = requestedClass.getPackage().getName();
    String simpleClassName = requestedClass.getSimpleSourceName();
    String proxyClassName = simpleClassName + "GetterAndSetter";
    String qualifiedProxyClassName = packageName + "." + proxyClassName;
    System.out.println("Created a class called: " + qualifiedProxyClassName);

    PrintWriter printWriter = context.tryCreate(logger, packageName, className);
    if (printWriter == null) return null;
      ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(packageName, className);
      composerFactory.addImport("test.project.shared.GetterAndSetter");
      composerFactory.addImplementedInterface("GetterAndSetter<" + underlyingTypeName + ">");
      SourceWriter writer = composerFactory.createSourceWriter(context, printWriter);
      if (writer != null) {
        JField[] fields = requestedClass.getFields();
        for (JField field : fields) {
          createSetterMethodForField(typeName, writer, field);
        }
        writer.indent();
        writer.println("public void set(" + typeName + " target, String path, Object value) {");
        writer.indent();
        createIfBlockForFields(writer, fields, true);
        writer.outdent();
        writer.println("}");
        writer.println();
        writer.println("public <K> K get(" + typeName + " target, String path) {");
        writer.indent();
        createIfBlockForFields(writer, fields, false);
        writer.outdent();
        writer.println("}");
        writer.println();
        writer.outdent();
        writer.commit(logger);
      }
      return packageName + "." + proxyClassName;
    } catch(NotFoundException nfe) {
      throw new UnableToCompleteException();
    }
  }
}

I hope this helps you.

like image 110
Renato Avatar answered Nov 06 '22 03:11

Renato