I just recently discovered that you can change the key of a resource from the default "method name" method to a customly specified key.
I'm now thinking of switching the format to a "dot separated" format (which is usually used in Java projects).
I know, this can be achieved by either using the @Key("my.new.key")
annotation or by providing a custom KeyGenerator
using the @GenerateKeys
annotation.
The problem is, that I'm searching for a solution that supports my new format as well as the old format. This is needed for backwards compatibility. I release my tool to the public and they can provide their own properties files (with their custom content). This means that I'm not able to change their keys to match my new format.
I'm now looking for a solution that uses the new key (if available) and falls back to the old key if necessary.
I thought I was being clever and came up with this KeyGenerator
:
public class MyKeyGenerator implements KeyGenerator
{
private PropertyResourceBundle bundle;
@Override
public String generateKey(Message msg)
{
if(bundle == null)
{
try
{
File file = urlToResourceFile(Text.class.getName()); /* Finds the properties file */
bundle = new PropertyResourceBundle(new FileInputStream(file));
}
catch (IOException e)
{
e.printStackTrace();
}
}
String key = msg.getKey();
if(key != null)
{
if(bundle.containsKey(key))
return key;
}
return msg.getMethodName();
}
}
Which will check if the new key exists in the properties file. If so, return it, if not return the old key.
What I receive however, is this error:
Computing all possible rebind results for 'my.package.here.i18n.Text'
Rebinding my.package.here.i18n.Text
Invoking generator com.google.gwt.i18n.rebind.LocalizableGenerator
Processing interface my.package.here.i18n.Text
Generating method body for generalSuccess()
[ERROR] No resource found for key 'general.success'
com.google.gwt.i18n.rebind.AbstractResource$MissingResourceException: No resource found for key 'general.success'
at com.google.gwt.i18n.rebind.MessagesMethodCreator.createMethodFor(MessagesMethodCreator.java:1086)
at com.google.gwt.i18n.rebind.AbstractLocalizableImplCreator.delegateToCreator(AbstractLocalizableImplCreator.java:501)
at com.google.gwt.i18n.rebind.MessagesImplCreator.emitMethodBody(MessagesImplCreator.java:92)
at com.google.gwt.user.rebind.AbstractGeneratorClassCreator.genMethod(AbstractGeneratorClassCreator.java:277)
at com.google.gwt.user.rebind.AbstractGeneratorClassCreator.emitMethods(AbstractGeneratorClassCreator.java:239)
at com.google.gwt.user.rebind.AbstractGeneratorClassCreator.emitClass(AbstractGeneratorClassCreator.java:118)
at com.google.gwt.i18n.rebind.AbstractLocalizableImplCreator.generateConstantOrMessageClass(AbstractLocalizableImplCreator.java:225)
at com.google.gwt.i18n.rebind.LocalizableGenerator.generate(LocalizableGenerator.java:151)
at com.google.gwt.i18n.rebind.LocalizableGenerator.generate(LocalizableGenerator.java:124)
at com.google.gwt.core.ext.IncrementalGenerator.generateNonIncrementally(IncrementalGenerator.java:40)
at com.google.gwt.dev.javac.StandardGeneratorContext.runGeneratorIncrementally(StandardGeneratorContext.java:676)
at com.google.gwt.dev.cfg.RuleGenerateWith.realize(RuleGenerateWith.java:41)
at com.google.gwt.dev.shell.StandardRebindOracle$Rebinder.rebind(StandardRebindOracle.java:79)
at com.google.gwt.dev.shell.StandardRebindOracle.rebind(StandardRebindOracle.java:276)
at com.google.gwt.dev.shell.StandardRebindOracle.rebind(StandardRebindOracle.java:265)
at com.google.gwt.dev.DistillerRebindPermutationOracle.getAllPossibleRebindAnswers(DistillerRebindPermutationOracle.java:91)
at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.handleGwtCreate(UnifyAst.java:387)
at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.handleMagicMethodCall(UnifyAst.java:465)
at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.endVisit(UnifyAst.java:255)
at com.google.gwt.dev.jjs.ast.JMethodCall.traverse(JMethodCall.java:244)
at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:117)
at com.google.gwt.dev.jjs.ast.JCastOperation.traverse(JCastOperation.java:65)
at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:117)
at com.google.gwt.dev.jjs.ast.JDeclarationStatement.traverse(JDeclarationStatement.java:48)
at com.google.gwt.dev.jjs.ast.JModVisitor$ListContextImmutable.traverse(JModVisitor.java:170)
at com.google.gwt.dev.jjs.ast.JModVisitor.acceptWithInsertRemoveImmutable(JModVisitor.java:336)
at com.google.gwt.dev.jjs.ast.JBlock.traverse(JBlock.java:83)
at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:138)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:134)
at com.google.gwt.dev.jjs.ast.JMethodBody.traverse(JMethodBody.java:82)
at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
at com.google.gwt.dev.jjs.ast.JMethod.visitChildren(JMethod.java:449)
at com.google.gwt.dev.jjs.ast.JMethod.traverse(JMethod.java:418)
at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
at com.google.gwt.dev.jjs.impl.UnifyAst.mainLoop(UnifyAst.java:940)
at com.google.gwt.dev.jjs.impl.UnifyAst.exec(UnifyAst.java:665)
at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.precompile(JavaToJavaScriptCompiler.java:672)
at com.google.gwt.dev.jjs.JavaScriptCompiler.precompile(JavaScriptCompiler.java:34)
at com.google.gwt.dev.Precompile.precompile(Precompile.java:271)
at com.google.gwt.dev.Precompile.precompile(Precompile.java:223)
at com.google.gwt.dev.Precompile.precompile(Precompile.java:139)
at com.google.gwt.dev.Compiler.run(Compiler.java:167)
at com.google.gwt.dev.Compiler.run(Compiler.java:132)
at com.google.gwt.dev.Compiler$1.run(Compiler.java:99)
at com.google.gwt.dev.CompileTaskRunner.doRun(CompileTaskRunner.java:55)
at com.google.gwt.dev.CompileTaskRunner.runWithAppropriateLogger(CompileTaskRunner.java:50)
at com.google.gwt.dev.Compiler.main(Compiler.java:106)
Which seems to suggest, that my KeyGenerator
is either only used if @Key
isn't present or that this GWT check is performed before my KeyGenerator
is executed.
Does anyone know how to solve this? Or maybe someone knows another solution?
UPDATE
Just found this here:
@Key(String key)
Specifies the key to use in the external format for this particular method. If not supplied, it will be generated based on the
@GenerateKeys
annotation, discussed above.
Which seems to make my goal unreachable... Any other suggestions?
Your goal may not be unreachable, but it is certainly not a easy one. While GWT-compile the com.google.gwt.i18n.rebind.LocalizableGenerator is invoked and performs its I18N-magic. This involves checking the keys which is causing the stated exception. The good news is that generator are very powerful and you can roll your custom implementation. The bad news is that generators are somewhat complicate to write and not very well documented. The official documentation mentions generators just briefly.
So in a first attempt I would suggest to replace the default LocalizableGenerator with your own version. In your own version of the generator you can then suppress the exception and add your own resource loading logic (you may end up overriding some more classes...). I'm quite sure you can override what generator is called in your local module configuration (the XML file). Although I have not tried it.
If that fails, the only option I see is to run your own I18N implementation. I mean a complete one, your own annotations with custom generators and linkers to create the necessary output...