/* @VaadinApache2LicenseForJavaFiles@ */ package com.vaadin.terminal.gwt.widgetsetutils; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Date; import java.util.List; import com.google.gwt.core.ext.Generator; import com.google.gwt.core.ext.GeneratorContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.TreeLogger.Type; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; import com.google.gwt.user.rebind.SourceWriter; import com.vaadin.terminal.gwt.client.ServerConnector; import com.vaadin.terminal.gwt.client.ConnectorMap; import com.vaadin.terminal.gwt.client.communication.ClientRpc; import com.vaadin.terminal.gwt.client.communication.MethodInvocation; import com.vaadin.terminal.gwt.client.communication.RpcManager; /** * GWT generator that creates an implementation for {@link RpcManager} on the * client side classes for executing RPC calls received from the the server. * * @since 7.0 */ public class RpcManagerGenerator extends Generator { @Override public String generate(TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException { String packageName = null; String className = null; try { TypeOracle typeOracle = context.getTypeOracle(); // get classType and save instance variables JClassType classType = typeOracle.getType(typeName); packageName = classType.getPackage().getName(); className = classType.getSimpleSourceName() + "Impl"; // Generate class source code for SerializerMapImpl generateClass(logger, context, packageName, className); } catch (Exception e) { logger.log(TreeLogger.ERROR, "SerializerMapGenerator creation failed", e); } // return the fully qualifed name of the class generated return packageName + "." + className; } /** * Generate source code for RpcManagerImpl * * @param logger * Logger object * @param context * Generator context * @param packageName * package name for the class to generate * @param className * class name for the class to generate */ private void generateClass(TreeLogger logger, GeneratorContext context, String packageName, String className) { // get print writer that receives the source code PrintWriter printWriter = null; printWriter = context.tryCreate(logger, packageName, className); // print writer if null, source code has ALREADY been generated if (printWriter == null) { return; } logger.log(Type.INFO, "Detecting server to client RPC interface types..."); Date date = new Date(); TypeOracle typeOracle = context.getTypeOracle(); JClassType serverToClientRpcType = typeOracle.findType(ClientRpc.class .getName()); JClassType[] rpcInterfaceSubtypes = serverToClientRpcType.getSubtypes(); // init composer, set class properties, create source writer ClassSourceFileComposerFactory composer = null; composer = new ClassSourceFileComposerFactory(packageName, className); composer.addImport("com.google.gwt.core.client.GWT"); composer.addImplementedInterface(RpcManager.class.getName()); SourceWriter sourceWriter = composer.createSourceWriter(context, printWriter); sourceWriter.indent(); List rpcInterfaces = new ArrayList(); // iterate over RPC interfaces and create helper methods for each // interface for (JClassType type : rpcInterfaceSubtypes) { if (null == type.isInterface()) { // only interested in interfaces here, not implementations continue; } rpcInterfaces.add(type); // generate method to call methods of an RPC interface sourceWriter.println("private void " + getInvokeMethodName(type) + "(" + MethodInvocation.class.getName() + " invocation, " + ConnectorMap.class.getName() + " connectorMap) {"); sourceWriter.indent(); // loop over the methods of the interface and its superinterfaces // methods for (JClassType currentType : type.getFlattenedSupertypeHierarchy()) { for (JMethod method : currentType.getMethods()) { sourceWriter.println("if (\"" + method.getName() + "\".equals(invocation.getMethodName())) {"); sourceWriter.indent(); // construct parameter string with appropriate casts String paramString = ""; JType[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < parameterTypes.length; ++i) { paramString = paramString + "(" + parameterTypes[i].getQualifiedSourceName() + ") invocation.getParameters()[" + i + "]"; if (i < parameterTypes.length - 1) { paramString = paramString + ", "; } } sourceWriter .println(ServerConnector.class.getName() + " connector = connectorMap.getConnector(invocation.getConnectorId());"); sourceWriter .println("for (" + ClientRpc.class.getName() + " rpcImplementation : connector.getRpcImplementations(\"" + type.getQualifiedSourceName() + "\")) {"); sourceWriter.indent(); sourceWriter.println("((" + type.getQualifiedSourceName() + ") rpcImplementation)." + method.getName() + "(" + paramString + ");"); sourceWriter.outdent(); sourceWriter.println("}"); sourceWriter.println("return;"); sourceWriter.outdent(); sourceWriter.println("}"); } } sourceWriter.outdent(); sourceWriter.println("}"); logger.log(Type.DEBUG, "Constructed helper method for server to client RPC for " + type.getName()); } // generate top-level "switch-case" method to select the correct // previously generated method based on the RPC interface sourceWriter.println("public void applyInvocation(" + MethodInvocation.class.getName() + " invocation, " + ConnectorMap.class.getName() + " connectorMap) {"); sourceWriter.indent(); for (JClassType type : rpcInterfaces) { sourceWriter.println("if (\"" + type.getQualifiedSourceName() + "\".equals(invocation.getInterfaceName())) {"); sourceWriter.indent(); sourceWriter.println(getInvokeMethodName(type) + "(invocation, connectorMap);"); sourceWriter.println("return;"); sourceWriter.outdent(); sourceWriter.println("}"); logger.log(Type.INFO, "Configured server to client RPC for " + type.getName()); } sourceWriter.outdent(); sourceWriter.println("}"); // close generated class sourceWriter.outdent(); sourceWriter.println("}"); // commit generated class context.commit(logger, printWriter); logger.log(Type.INFO, "Done. (" + (new Date().getTime() - date.getTime()) / 1000 + "seconds)"); } private String getInvokeMethodName(JClassType type) { return "invoke" + type.getQualifiedSourceName().replaceAll("\\.", "_"); } }