Friday, October 12, 2007

Class.forName("fully.qualified.ClassName").newInstance() in GWT

In Java, when we design something to work generically like loading a plugin Class or something like that, we use the classloader to load some arbitrary class and instantiate it using the Class.newInstance() (Assuming that our Class has a Zero argument constructor) or using the constructors through the Class's getConstructor method.

This feature cannot be used in GWT, as you may all know that the client code (JavaScript) is statically generated. There is no Classloader or no Class.newInstance() or no reflection at all in Javascript. So to provide very least feature of instantiating a Class by knowing its classname, here is a tip that you may like.

We may not require all the Class types to be available for dynamic instantiation. So we are gonna mark the required Class types as Instantiable (A marker interface).
Now, we need some factory class which has a method newInstance(String className), using which we can create an instance of specified classname.



1 package name.aanand.gwt.client;

2

3
/**

4 * Platform factory to create instance of any {@link Instantiable} type.

5 * @author aanandn

6 *

7 */

8 public interface Factory {

9 Instantiable getInstance(String className);

10 }




Now we are gonna create a Class called ReflectiveFactory which is defined as follows...


1 package name.aanand.gwt.client;

2

3

4 /**

5 * Factory class for instantiating any {@link Instantiable} type by providing

6 * the fully qualified Class name.

7 * @author aanandn

8 *

9 */

10 public class ReflectiveFactory implements FactoryWrapper{

11 }


The ReflectiveFactory implements an interface called FactoryWrapper which is again a marker interface to identify the factory classes for which Wrapper implementations or Proxies have to be generated. Now we are ready with our base interfaces. And here begins how its achieved...

First have a singleton class which instantiates our Factory like this:

factory = (Factory) GWT.create(ReflectiveFactory.class);

and return this factory whenever its asked for.

Now you may wonder, how will it work without any real code in the ReflectiveFactory. So here is the explaination of how its gonna work.

  1. We are going to extend the GWT compiler to generate a wrapper or proxy for each of the Classes which implement FactoryWrapper.
  2. The generator should create a new class with name Wrapper, which implements Factory.
  3. In the case of ReflectiveFactory, first we are going to get all the Instantiable types.
  4. And finally generate the method newInstance(String className), conditionally returning one of the Reflectables based on the specified className.
To extend GWTCompiler we need to add an entry in our module XML as follows:


1 <module>
2
3 -- Inherit the core Web Toolkit stuff. -->
4 <inherits name='com.google.gwt.user.User'/>
5
6 -- Specify the app entry point class. -->
7 <entry-point class='name.aanand.gwt.client.MainEntryPoint'/>
8
9
<generate-with class="name.aanand.gwt.generator.FactoryGenerator" >
10
<when-type-assignable class="name.aanand.gwt.client.FactoryWrapper" />
11
</generate-with>
12
13
</module>
14



The FactoryGenerator class should extend the abstract class Generator and implement its method
public String generate(TreeLogger logger, GeneratorContext context, String typeName)

Simple implemenation of FactoryGenerator is given below:



1 package name.aanand.gwt.generator;

2

3 import java.io.PrintWriter;

4

5 import com.google.gwt.core.ext.Generator;

6 import com.google.gwt.core.ext.GeneratorContext;

7 import com.google.gwt.core.ext.TreeLogger;

8 import com.google.gwt.core.ext.UnableToCompleteException;

9 import com.google.gwt.core.ext.typeinfo.JClassType;

10 import com.google.gwt.core.ext.typeinfo.NotFoundException;

11 import com.google.gwt.core.ext.typeinfo.TypeOracle;

12 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;

13 import com.google.gwt.user.rebind.SourceWriter;

14

15 public class FactoryGenerator extends Generator {

16

17 public String generate(TreeLogger logger, GeneratorContext context,

18 String typeName) throws UnableToCompleteException {

19 logger.log(TreeLogger.INFO, "Generating source for " + typeName, null);

20 TypeOracle typeOracle = context.getTypeOracle();

21

22 JClassType clazz = typeOracle.findType(typeName);

23 if (clazz == null) {

24 logger.log(TreeLogger.ERROR, "Unable to find metadata for type '"

25 + typeName + "'", null);

26 throw new UnableToCompleteException();

27 }

28

29 try {

30 logger.log(TreeLogger.INFO, "Generating source for "

31 + clazz.getQualifiedSourceName(), null);

32

33 JClassType reflectableType = typeOracle

34 .getType("name.aanand.gwt.client.Instantiable");

35 SourceWriter sourceWriter = getSourceWriter(clazz, context, logger);

36 if (sourceWriter != null) {

37 sourceWriter.println("public "

38 + reflectableType.getQualifiedSourceName()

39 + " newInstance(String className) {");

40 JClassType[] types = typeOracle.getTypes();

41 int count = 0;

42 for (int i = 0; i < types.length; i++) {

43 if (types[i].isInterface() == null

44 && types[i].isAssignableTo(reflectableType)) {

45 if (count == 0) {

46 sourceWriter.println(" if(\""

47 + types[i].getQualifiedSourceName()

48 + "\".equals(className)) {"

49 + " return new "

50 + types[i].getQualifiedSourceName() + "();"

51 + "}");

52 } else {

53 sourceWriter.println(" else if(\""

54 + types[i].getQualifiedSourceName()

55 + "\".equals(className)) {"

56 + " return new "

57 + types[i].getQualifiedSourceName() + "();"

58 + "}");

59 }

60 count++;

61 }

62 }

63 sourceWriter.println("return null;");

64 sourceWriter.println("}");

65 sourceWriter.commit(logger);

66 logger.log(TreeLogger.INFO, "Done Generating source for "

67 + clazz.getName(), null);

68 return clazz.getQualifiedSourceName() + "Wrapper";

69 }

70 } catch (NotFoundException e) {

71 e.printStackTrace();

72 }

73 return null;

74

75 }

76

77 public SourceWriter getSourceWriter(JClassType classType,

78 GeneratorContext context, TreeLogger logger) {

79

80 String packageName = classType.getPackage().getName();

81 String simpleName = classType.getSimpleSourceName() + "Wrapper";

82 ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(

83 packageName, simpleName);

84 composer.addImplementedInterface("name.aanand.gwt.client.Factory");

85 PrintWriter printWriter = context.tryCreate(logger, packageName,

86 simpleName);

87 if (printWriter == null) {

88 return null;

89 } else {

90 SourceWriter sw = composer.createSourceWriter(context, printWriter);

91 return sw;

92 }

93 }

94

95 }

96




Now compile your code with GWTCompiler and start using as shown below:

Factory factory = (Factory) GWT.create(ReflectiveFactory.class);
factory.newInstance("fully.qualified.Classname");

Thats all folks!

Monday, October 08, 2007

Simple Key Mappings for VIM

These are very simple key-mappings for VIM 7.0 and above, to play with tabs (or tab pages).


 5 set nu!

 6 set wrap!

 7 color desert

 8 set guioptions+=b

 9 :map <C-N> <M>:tabnew<CR>

10 :map <S-TAB> <M>:tabnext<CR>

11 :map <C-F4> <M>:tabclose!<CR>

12 :map <C-O> <M>:browse tabnew<CR>


All are command mode shortcuts.

CTRL-N  -  creates a new tab page.
CTRL-O  -  Opens a file in a new tab page.
CTRL-F4 - Closes current tab page

SHIFT-TAB - rotates through the tab pages.

Happy Vimming!