private void emitBody(SourceWriter w) throws NotFoundException { JClassType baseClass = context_.getTypeOracle().getType("org.rstudio.core.client.js.JsObjectInjector"); JClassType c = baseType_.asParameterizationOf(baseClass.isGenericType()); JType typeToInject = c.isParameterized().getTypeArgs()[0]; w.print("public native final void injectObject("); w.print(typeToInject.getQualifiedSourceName()); w.println(" value) /*-{"); w.indent(); w.println(baseExpression_ + " = {"); w.indent(); JMethod[] methods = typeToInject.isClassOrInterface().getMethods(); for (int i = 0; i < methods.length; i++) { JMethod method = methods[i]; final JParameter[] jParameters = method.getParameters(); StringBuilder argString = new StringBuilder(); for (int j = 0; j < jParameters.length; j++) { argString.append("_").append(j); if (j < jParameters.length - 1) argString.append(", "); } w.println(method.getName() + ": function(" + argString + ") {"); w.indent(); if (!method.getReturnType().getQualifiedSourceName().equals("void")) w.print("return "); w.print("value.@"); w.print(typeToInject.getQualifiedSourceName()); w.print("::"); w.print(method.getName()); w.print("("); for (JParameter param : jParameters) w.print(param.getType().getJNISignature()); w.print(")("); w.print(argString.toString()); w.println(");"); w.outdent(); w.print("}"); w.println((i < methods.length - 1) ? "," : ""); } w.outdent(); w.println("};"); w.outdent(); w.println("}-*/;"); }
public RequestFactoryModel(TreeLogger logger, JClassType factoryType) throws UnableToCompleteException { this.logger = logger; this.factoryType = factoryType; this.oracle = factoryType.getOracle(); collectionInterface = oracle.findType(Collection.class.getCanonicalName()); entityProxyInterface = oracle.findType(EntityProxy.class.getCanonicalName()); instanceRequestInterface = oracle.findType(InstanceRequest.class.getCanonicalName()); listInterface = oracle.findType(List.class.getCanonicalName()); mapInterface = oracle.findType(Map.class.getCanonicalName()); requestContextInterface = oracle.findType(RequestContext.class.getCanonicalName()); requestFactoryInterface = oracle.findType(RequestFactory.class.getCanonicalName()); requestInterface = oracle.findType(Request.class.getCanonicalName()); setInterface = oracle.findType(Set.class.getCanonicalName()); splittableType = oracle.findType(Splittable.class.getCanonicalName()); valueProxyInterface = oracle.findType(ValueProxy.class.getCanonicalName()); extraTypes = checkExtraTypes(factoryType, false); for (JMethod method : factoryType.getOverridableMethods()) { if (method.getEnclosingType().equals(requestFactoryInterface)) { // Ignore methods defined an RequestFactory itself continue; } if (method.getParameters().length > 0) { poison("Unexpected parameter on method %s", method.getName()); continue; } JClassType contextType = method.getReturnType().isInterface(); if (contextType == null || !requestContextInterface.isAssignableFrom(contextType)) { poison( "Unexpected return type %s on method %s is not" + " an interface assignable to %s", method.getReturnType().getQualifiedSourceName(), method.getName(), requestContextInterface.getSimpleSourceName()); continue; } ContextMethod.Builder builder = new ContextMethod.Builder(); builder.setDeclaredMethod(method); buildContextMethod(builder, contextType); contextMethods.add(builder.build()); } if (poisoned) { die(poisonedMessage()); } }
private String getRestMethod(JMethod method) throws UnableToCompleteException { String restMethod = null; if (method.getAnnotation(DELETE.class) != null) { restMethod = METHOD_DELETE; } else if (method.getAnnotation(GET.class) != null) { restMethod = METHOD_GET; } else if (method.getAnnotation(HEAD.class) != null) { restMethod = METHOD_HEAD; } else if (method.getAnnotation(OPTIONS.class) != null) { restMethod = METHOD_OPTIONS; } else if (method.getAnnotation(POST.class) != null) { restMethod = METHOD_POST; } else if (method.getAnnotation(PUT.class) != null) { restMethod = METHOD_PUT; } else if (method.getAnnotation(JSONP.class) != null) { restMethod = METHOD_JSONP; } else { restMethod = method.getName(); if (!REST_METHODS.contains(restMethod)) { getLogger() .log( ERROR, "Invalid rest method. It must either have a lower case rest method name or have a javax rs method annotation: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } } return restMethod; }
Annotation[] extractAnnotations( TreeLogger logger, JClassType type, JMethod ifaceMethod, JMethod classMethod, GeneratorContext ctx) { Map<Class<?>, Annotation> unique = new LinkedHashMap<Class<?>, Annotation>(); // Prefer annotation on classes before interfaces. for (Annotation classAnno : classMethod.getAnnotations()) { unique.put(classAnno.annotationType(), classAnno); } // Transverse supertypes JClassType next = type.getSuperclass(); while (next != null) { JMethod method = next.findMethod(ifaceMethod.getName(), ifaceMethod.getParameterTypes()); if (method != null) for (Annotation classAnno : method.getAnnotations()) { unique.put(classAnno.annotationType(), classAnno); } next = next.getSuperclass(); } for (Annotation ifaceAnno : ifaceMethod.getAnnotations()) { unique.put(ifaceAnno.annotationType(), ifaceAnno); } return unique.values().toArray(new Annotation[unique.size()]); }
static String badContextReturnType( JMethod method, JClassType requestInterface, JClassType instanceRequestInterface) { return String.format( "Return type %s in method %s must be an interface assignable" + " to %s or %s", method.getReturnType(), method.getName(), requestInterface.getSimpleSourceName(), instanceRequestInterface.getSimpleSourceName()); }
/** Write out the JavaScript wrapper around a Java method. */ static void writeFunctionForMethod(FragmentGeneratorContext context, JMethod m) throws UnableToCompleteException { context.parentLogger.branch(TreeLogger.DEBUG, "Writing function() for " + m.getName(), null); if (isIdentityFunction(context, m)) { writeIdentityInvocation(context, m); } else { writeLinkageInvocation(context, m); } }
/** * @param srcWriter * @param type * @param parentVariable * @param added * @param iocContainerVariable * @param configurations */ private static void injectMethods( SourcePrinter srcWriter, JClassType type, String parentVariable, Set<String> added, String iocContainerVariable, Map<String, IocConfig<?>> configurations) { for (JMethod method : type.getMethods()) { Inject inject = method.getAnnotation(Inject.class); if (inject != null && !method.isStatic()) { String methodName = method.getName(); if (!added.contains(methodName + "()")) { added.add(methodName + "()"); JParameter[] parameters = method.getParameters(); List<String> params = new ArrayList<String>(); for (JParameter parameter : parameters) { JType parameterType = parameter.getType(); if ((parameterType.isPrimitive() != null)) { throw new IoCException( "IoC Error Method [" + methodName + "] from class [" + type.getQualifiedSourceName() + "] declares an invalid parameter. Primitive types are not allowed here"); } String variableName = "parameter_" + methodName + "_" + parameter.getName(); params.add(variableName); String injectionExpression = getParameterInjectionExpression(parameter, iocContainerVariable, configurations); srcWriter.println( parameterType.getQualifiedSourceName() + " " + variableName + " = " + injectionExpression + ";"); } srcWriter.print(parentVariable + "." + methodName + "("); boolean first = true; for (String param : params) { if (!first) { srcWriter.print(", "); } first = false; srcWriter.print(param); } srcWriter.println(");"); } } } }
private void implementAction( TreeLogger logger, ModelGenerator generator, JMethod method, ModelMagic models, Annotation[] annos) { boolean fluent = ModelGeneratorGwt.isFluent(method); JPrimitiveType primitive = method.getReturnType().isPrimitive(); if (primitive != JPrimitiveType.VOID) { if (!fluent) { // non-fluent, non-void return type is not an action // TODO change this! // implementGetter(logger, mb, method, models, annos, // method.getReturnType().getSimpleSourceName()); logger.log( Type.ERROR, "No getter for " + method.getJsniSignature() + "; " + "If your type does not use javabean getField() naming conventions, " + "then you MUST annotate a getter field with @GetterField"); } return; } MethodBuffer mb = generator.createMethod( method.getReturnType().getQualifiedSourceName(), method.getName(), ModelGeneratorGwt.typeToParameterString(method.getParameterTypes())); if (method.getName().equals("clear")) { // implement clear } if (fluent) { mb.println("return this;"); } }
public static void checkReturnType(TreeLogger logger, JMethod method) throws UnableToCompleteException { if (!method.getReturnType().getQualifiedSourceName().equals("void")) { logger.log( Type.ERROR, "The method " + method.getEnclosingType().getQualifiedSourceName() + "." + method.getName() + " returns " + method.getReturnType().getQualifiedSourceName() + " but only void is supported for methods in RPC interfaces."); throw new UnableToCompleteException(); } }
/** Simply prints a JSNI reference to the exported function. */ private static void writeIdentityInvocation(FragmentGeneratorContext context, JMethod m) throws UnableToCompleteException { SourceWriter sw = context.sw; JParameter[] parameters = m.getParameters(); sw.print("@"); sw.print(m.getEnclosingType().getQualifiedSourceName()); sw.print("::"); sw.print(m.getName()); sw.print("("); // Argument list for the Java invocation for (int i = 0; i < parameters.length; i++) { sw.print(parameters[i].getType().getJNISignature()); } sw.print(")"); }
private void implementException(TreeLogger logger, MethodBuffer mb, JMethod method) { logger.log( Type.WARN, "Unable to implement model method for " + method.getJsniSignature() + "; " + "inserting a runtime exception." + " Either annotate the method with an xapi.annotation.model, " + " or ensure it conforms to javabean naming conventions get___, set___"); mb.println("throw new RuntimeException(\""); mb.println( "Model method " + method.getEnclosingType().getSimpleSourceName() + "." + method.getName() + " not annotated correctly"); mb.println("\");"); }
@Override protected void generateWrapperMethod(JMethod method, SourcePrinter srcWriter) { if (mustDelegateToController(method)) { JType returnType = method.getReturnType().getErasedType(); srcWriter.println(method.getReadableDeclaration(false, false, false, false, true) + "{"); if (returnType != JPrimitiveType.VOID) { srcWriter.print("return "); } srcWriter.print("this._controller." + method.getName() + "("); boolean needsComma = false; for (JParameter parameter : method.getParameters()) { if (needsComma) { srcWriter.print(", "); } needsComma = true; srcWriter.print(parameter.getName()); } srcWriter.println(");"); srcWriter.println("}"); } }
/** Determines if the exported method can be used as-is. */ private static boolean isIdentityFunction(FragmentGeneratorContext context, JMethod m) throws UnableToCompleteException { TreeLogger logger = context.parentLogger.branch( TreeLogger.DEBUG, "Determining identity status of " + m.getName(), null); FragmentGeneratorOracle fgo = context.fragmentGeneratorOracle; boolean identityOnly = m.isStatic(); JParameter[] parameters = m.getParameters(); identityOnly &= context .fragmentGeneratorOracle .findFragmentGenerator(logger, context.typeOracle, m.getReturnType()) .isIdentity(); for (int i = 0; i < parameters.length && identityOnly; i++) { FragmentGenerator fragmentGenerator = fgo.findFragmentGenerator(logger, context.typeOracle, parameters[i].getType()); identityOnly &= fragmentGenerator.isIdentity(); } return identityOnly; }
private RequestQueueModel collectModel( TreeLogger logger, GeneratorContext context, JClassType toGenerate) throws UnableToCompleteException { RequestQueueModel.Builder rqBuilder = new RequestQueueModel.Builder(); TypeOracle typeOracle = context.getTypeOracle(); JClassType requestQueue = typeOracle.findType(RequestQueue.class.getName()); JClassType asyncCallback = typeOracle.findType(AsyncCallback.class.getName()); JClassType voidType = typeOracle.findType(Void.class.getName()); rqBuilder.setRequestQueueInterfaceName(toGenerate.getParameterizedQualifiedSourceName()); AsyncServiceModel.Builder serviceBuilder = new AsyncServiceModel.Builder(); for (JMethod m : toGenerate.getMethods()) { TreeLogger serviceLogger = logger.branch(Type.DEBUG, "Reading async service " + m.getReadableDeclaration()); // Skip those defined at RequestQueue if (m.getEnclosingType().equals(requestQueue)) { continue; } JClassType returnType = m.getReturnType().isClassOrInterface(); if (returnType == null) { serviceLogger.log(Type.ERROR, "Unexpected method return type " + returnType); throw new UnableToCompleteException(); } serviceBuilder.setAsyncServiceInterfaceName(returnType.getParameterizedQualifiedSourceName()); serviceBuilder.setDeclaredMethodName(m.getName()); Service serviceAnnotation = m.getAnnotation(Service.class); if (serviceAnnotation == null) { serviceLogger.log(Type.ERROR, "Missing @Service annotation"); throw new UnableToCompleteException(); } serviceBuilder.setService(serviceAnnotation.value().getName()); AsyncServiceMethodModel.Builder methodBuilder = new AsyncServiceMethodModel.Builder(); for (JMethod asyncMethod : m.getReturnType().isClassOrInterface().getMethods()) { TreeLogger methodLogger = serviceLogger.branch( Type.DEBUG, "Reading service method " + asyncMethod.getReadableDeclaration()); List<JType> types = new ArrayList<JType>(); methodBuilder.setReturnTypeName(voidType.getParameterizedQualifiedSourceName()); boolean asyncFound = false; for (JType param : asyncMethod.getParameterTypes()) { if (asyncFound) { methodLogger.log( Type.WARN, "Already passed an AsyncCallback param - is that what you meant?"); } if (param.isClassOrInterface() != null && param.isClassOrInterface().isAssignableTo(asyncCallback)) { JClassType boxedReturnType = ModelUtils.findParameterizationOf(asyncCallback, param.isClassOrInterface())[0]; methodBuilder .setHasCallback(true) .setReturnTypeName(boxedReturnType.getParameterizedQualifiedSourceName()); asyncFound = true; continue; // should be last, check for this... } types.add(param); } Set<JClassType> throwables = new HashSet<JClassType>(); Throws t = asyncMethod.getAnnotation(Throws.class); if (t != null) { for (Class<? extends Throwable> throwable : t.value()) { throwables.add(typeOracle.findType(throwable.getName())); } } methodBuilder .setMethodName(asyncMethod.getName()) .setArgTypes(types) .setThrowables(throwables); serviceBuilder.addMethod(methodBuilder.build()); } rqBuilder.addService(serviceBuilder.build()); } return rqBuilder.build(); }
/** Writes a linkage function object that will invoke the exported function. */ private static void writeLinkageInvocation(FragmentGeneratorContext context, JMethod m) throws UnableToCompleteException { TreeLogger logger = context.parentLogger.branch( TreeLogger.DEBUG, "Writing function() for " + m.getName(), null); SourceWriter sw = context.sw; JParameter[] parameters = m.getParameters(); FragmentGeneratorOracle fgo = context.fragmentGeneratorOracle; FragmentGenerator returnFragmentGenerator = fgo.findFragmentGenerator(logger, context.typeOracle, m.getReturnType()); sw.print("function("); for (int i = 0; i < parameters.length; i++) { sw.print("arg"); sw.print(String.valueOf(i)); if (i < parameters.length - 1) { sw.print(", "); } } sw.println(") {"); sw.indent(); if (returnFragmentGenerator.isIdentity()) { sw.print("return "); } else { sw.print("var javaReturn = "); } // Don't need to reference the instance on a static method if (!m.isStatic()) { sw.print(context.parameterName); sw.print("."); } sw.print("@"); sw.print(m.getEnclosingType().getQualifiedSourceName()); sw.print("::"); sw.print(m.getName()); sw.print("("); // Argument list for the Java invocation for (int i = 0; i < parameters.length; i++) { sw.print(parameters[i].getType().getJNISignature()); } sw.println(")("); // Indent the parameters, each on its own like to improve readability sw.indent(); sw.indent(); for (int i = 0; i < parameters.length; i++) { // Create a sub-context to generate the wrap/unwrap logic JType returnType = parameters[i].getType(); FragmentGeneratorContext subParams = new FragmentGeneratorContext(context); subParams.returnType = returnType; subParams.parameterName = "arg" + i; FragmentGenerator fragmentGenerator = fgo.findFragmentGenerator(logger, context.typeOracle, returnType); if (fragmentGenerator == null) { logger.log( TreeLogger.ERROR, "No fragment generator for " + returnType.getQualifiedSourceName(), null); throw new UnableToCompleteException(); } fragmentGenerator.fromJS(subParams); if (i < parameters.length - 1) { sw.println(", "); } } sw.outdent(); sw.outdent(); sw.println(");"); if (!returnFragmentGenerator.isIdentity()) { FragmentGeneratorContext returnContext = new FragmentGeneratorContext(context); returnContext.parameterName = "javaReturn"; returnContext.returnType = m.getReturnType(); sw.print("return "); returnFragmentGenerator.toJS(returnContext); sw.println(";"); } sw.outdent(); sw.print("}"); }
private void grabAnnotations( TreeLogger logger, ModelMagic models, HasModelFields fields, JMethod method, Annotation[] annos, JClassType type) { GetterFor getter = method.getAnnotation(GetterFor.class); ClientToServer c2s = method.getAnnotation(ClientToServer.class); ServerToClient s2c = method.getAnnotation(ServerToClient.class); Persistent persist = method.getAnnotation(Persistent.class); Serializable serial = method.getAnnotation(Serializable.class); Named name = method.getAnnotation(Named.class); for (Annotation annotation : annos) { if (getter == null && annotation.annotationType() == GetterFor.class) getter = (GetterFor) annotation; else if (serial == null && annotation.annotationType() == Serializable.class) serial = (Serializable) annotation; else if (persist == null && annotation.annotationType() == Persistent.class) persist = (Persistent) annotation; else if (c2s == null && annotation.annotationType() == ClientToServer.class) c2s = (ClientToServer) annotation; else if (s2c == null && annotation.annotationType() == ServerToClient.class) s2c = (ServerToClient) annotation; else if (name == null && annotation.annotationType() == Named.class) name = (Named) annotation; } String fieldName; if (name == null) { fieldName = ModelGeneratorGwt.fieldName(method, models); } else { fieldName = name.value(); if (X_Runtime.isDebug()) { logger.log( Type.TRACE, "Named method " + method.getJsniSignature() + " " + fieldName + ", from @Named attribute. Heuristic name: " + ModelGeneratorGwt.fieldName(method, models)); } } if ("".equals(fieldName)) fieldName = method.getName(); ModelField field = fields.getOrMakeField(fieldName); if (field.getPersistent() == null) { field.setPersistent(persist); } else { assert persist == null || (persist.patchable() == field.getPersistent().patchable()) : "Model annotation mismatch! Field " + field.getName() + " of type " + type.getQualifiedSourceName() + " contained multiple @Persistent annotations which did not match. " + "You may have to override an annotated supertype method with the correct " + "@Persistent annotation."; } if (field.getSerializable() == null) { field.setSerializable(serial); } else { // assert serial == null || // ( // this block is all assert, so it will compile out of production. // serial.clientToServer() == field.getSerializable().clientToServer() && // serial.serverToClient() == field.getSerializable().serverToClient() && // serial.obfuscated() == field.getSerializable().obfuscated() // ) : "Model annotation mismatch! Field "+field.getName()+" contained " + // "multiple @Serializable annotations which did not match. You may " + // "have to override an annotated supertype method with the correct " + // "@Serializable annotation."; } if (field.getServerToClient() == null) { field.setServerToClient(s2c); } else { assert s2c == null || s2c.enabled() == field.getServerToClient().enabled() : "Model annotation mismatch! Field " + field.getName() + " was marked as " + "both " + "serverToClient" + " enabled and disabled. Please correct this ambiguity;" + " your model is now undeterministic and may break unexpectedly."; } if (field.getClientToServer() == null) { field.setClientToServer(c2s); } else { assert c2s == null || c2s.enabled() == field.getClientToServer().enabled() : "Model annotation mismatch! Field " + field.getName() + " was marked as " + "both " + "clientToServer" + " enabled and disabled. Please correct this ambiguity;" + " your model is now undeterministic and may break unexpectedly."; } }
static String noSettersAllowed(JMethod found) { return String.format("Optional setters not allowed here: ", found.getName()); }
public void build( TreeLogger logger, SourceBuilder<ModelMagic> builder, GeneratorContext ctx, JClassType type) throws UnableToCompleteException { ModelMagic models = builder.getPayload(); ModelGenerator generator = new ModelGenerator(builder); // Step one; determine if we already have a concrete type or not. // TODO if JClassType is final, we need to wrap it using a delegate model. JClassType concrete; // ClassBuffer cb = builder.getClassBuffer(); JClassType root = models.getRootType(logger, ctx); if (type.isInterface() == null && type != root) { concrete = type; generator.setSuperClass(type.getQualifiedSourceName()); } else { // We have an interface on our hands; search for an existing or // buildable model. // search for a supertype to inherit. Anything that extends Model, // does not implement any method we do implement, preferred type // being the one that implements the most interfaces possible final JClassType model; try { model = ctx.getTypeOracle().getType(Model.class.getName()); } catch (NotFoundException e) { logger.log( Type.ERROR, "Cannot load " + Model.class.getName() + "; " + "make sure you have xapi-gwt-model:sources.jar on classpath."); throw new UnableToCompleteException(); } concrete = model; for (JClassType supertype : concrete.getFlattenedSupertypeHierarchy()) { // Only interfaces explicitly extending Model become concrete. if (ModelGeneratorGwt.canBeSupertype(type, supertype)) { // prefer the concrete type with the most methods in common. concrete = supertype; } } if (concrete == null || concrete == model) { concrete = models.getRootType(logger, ctx); generator.setSuperClass(concrete.getQualifiedSourceName()); } else { // We have to make sure this concrete supertype is created. if (!models.hasModel(concrete.getQualifiedSourceName())) { // Concrete type is not cached. Build it now. RebindResult result = ModelGeneratorGwt.execImpl(logger, ctx, concrete.getQualifiedSourceName()); generator.setSuperClass(result.getResultTypeName()); } } } // This will probably become jsni, if we can avoid jso interface sickness... generator.createFactory(type.getQualifiedSourceName()); HasModelFields fieldMap = new HasModelFields(); fieldMap.setDefaultSerializable(type.getAnnotation(Serializable.class)); for (JMethod method : methods.keySet()) { if (!toGenerate.contains(ModelGeneratorGwt.toSignature(method))) { logger.log( Type.TRACE, "Skipping method defined in supertype: " + method.getJsniSignature()); continue; } Annotation[] annos = methods.get(method); String methodName = method.getName(); String returnType = method.getReturnType().getQualifiedSourceName(); String params = ModelGeneratorGwt.typeToParameterString(method.getParameterTypes()); // TODO: check imports if we are safe to use simple name. returnType = method.getReturnType().getSimpleSourceName(); IsType returns = X_Source.binaryToSource(method.getReturnType().getQualifiedBinaryName()); IsType[] parameters = ModelGeneratorGwt.toTypes(method.getParameterTypes()); GetterFor getter = method.getAnnotation(GetterFor.class); if (getter != null) { String name = getter.value(); if (name.length() == 0) { name = ModelUtil.stripGetter(method.getName()); } ModelField field = fieldMap.getOrMakeField(name); field.setType(returnType); assert parameters.length == 0 : "A getter method cannot have parameters. " + "Generated code requires using getter methods without args. You provided " + method.getJsniSignature(); grabAnnotations(logger, models, fieldMap, method, annos, type); field.addGetter(returns, methodName); continue; } SetterFor setter = method.getAnnotation(SetterFor.class); if (setter != null) { String name = setter.value(); if (name.length() == 0) name = ModelUtil.stripSetter(method.getName()); grabAnnotations(logger, models, fieldMap, method, annos, type); continue; } if (method.getAnnotation(DeleterFor.class) != null) { implementAction(logger, generator, method, models, annos); continue; } // No annotation. We have to guess the type. boolean isVoid = method.getReturnType().isPrimitive() == JPrimitiveType.VOID; boolean isGetter = methodName.startsWith("get") || methodName.startsWith("is") || methodName.startsWith("has"); boolean isSetter, isAction; if (isGetter) { assert !isVoid : "Cannot have a void return type with method name " + methodName + "; getter prefixes get(), is() and has() must return a type."; isSetter = false; isAction = false; } else { isSetter = methodName.startsWith("set") || methodName.startsWith("add") || methodName.startsWith("put") || methodName.startsWith("rem") || methodName.startsWith("remove"); if (isSetter) { isAction = false; } else { isAction = true; } } if (isVoid) { // definitely a setter / action method. if (isSetter) { MethodBuffer mb = generator.createMethod(returnType, methodName, params); implementSetter(logger, mb, method, models, fieldMap, annos); } else if (isAction) { implementAction(logger, generator, method, models, annos); } else { MethodBuffer mb = generator.createMethod(returnType, methodName, params); implementException(logger, mb, method); } } else { if (isGetter) { String name = ModelUtil.stripGetter(method.getName()); ModelField field = fieldMap.getOrMakeField(name); field.setType(returnType); field.addGetter(returns, methodName); grabAnnotations(logger, models, fieldMap, method, annos, type); } else if (isSetter) { MethodBuffer mb = generator.createMethod(returnType, methodName, params); implementSetter(logger, mb, method, models, fieldMap, annos); } else if (isAction) { implementAction(logger, generator, method, models, annos); } else { MethodBuffer mb = generator.createMethod(returnType, methodName, params); implementException(logger, mb, method); } } } generator.generateModel( X_Source.toType(builder.getPackage(), builder.getClassBuffer().getSimpleName()), fieldMap); }
@Override public String generate(TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException { // make sure it is an interface TypeOracle oracle = context.getTypeOracle(); propertyAccessInterface = oracle.findType(Name.getSourceNameForClass(PropertyAccess.class)); modelKeyProviderInterface = oracle.findType(Name.getSourceNameForClass(ModelKeyProvider.class)); valueProviderInterface = oracle.findType(Name.getSourceNameForClass(ValueProvider.class)); labelProviderInterface = oracle.findType(Name.getSourceNameForClass(LabelProvider.class)); JClassType toGenerate = oracle.findType(typeName).isInterface(); if (toGenerate == null) { logger.log(TreeLogger.ERROR, typeName + " is not an interface type"); throw new UnableToCompleteException(); } if (!toGenerate.isAssignableTo(propertyAccessInterface)) { logger.log(Type.ERROR, "This isn't a PropertyAccess subtype..."); throw new UnableToCompleteException(); } // Get the name of the new type String packageName = toGenerate.getPackage().getName(); String simpleSourceName = toGenerate.getName().replace('.', '_') + "Impl"; PrintWriter pw = context.tryCreate(logger, packageName, simpleSourceName); if (pw == null) { return packageName + "." + simpleSourceName; } // start making the class, with basic imports ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory(packageName, simpleSourceName); factory.addImplementedInterface(typeName); SourceWriter sw = factory.createSourceWriter(context, pw); // for each method, for (JMethod m : toGenerate.getOverridableMethods()) { TreeLogger l = logger.branch(Type.DEBUG, "Building method: " + m.getReadableDeclaration()); // no support for params at this time if (m.getParameters().length != 0) { l.log(Type.ERROR, "Method " + m.toString() + " must not have parameters."); throw new UnableToCompleteException(); } // ask for the types that provide the property data JClassType ret = m.getReturnType().isClassOrInterface(); final AbstractCreator c; if (ret.isAssignableTo(valueProviderInterface)) { c = new ValueProviderCreator(context, l, m); } else if (ret.isAssignableTo(modelKeyProviderInterface)) { c = new ModelKeyProviderCreator(context, l, m); } else if (ret.isAssignableTo(labelProviderInterface)) { c = new LabelProviderCreator(context, l, m); } else { logger.log(Type.ERROR, "Method uses a return type that cannot be generated"); throw new UnableToCompleteException(); } c.create(); // build the method // public ValueProvider<T, V> name() { return NameValueProvider.instance; // } sw.println("public %1$s %2$s() {", m.getReturnType().getQualifiedSourceName(), m.getName()); sw.indentln("return %1$s;", c.getInstanceExpression()); sw.println("}"); } sw.commit(logger); return factory.getCreatedClassName(); }
private void writeMethodImpl(JMethod method) throws UnableToCompleteException { boolean returnRequest = false; if (method.getReturnType() != JPrimitiveType.VOID) { if (!method.getReturnType().getQualifiedSourceName().equals(Request.class.getName()) && !method .getReturnType() .getQualifiedSourceName() .equals(JsonpRequest.class.getName())) { getLogger() .log( ERROR, "Invalid rest method. Method must have void, Request or JsonpRequest return types: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } else { returnRequest = true; } } Json jsonAnnotation = source.getAnnotation(Json.class); final Style classStyle = jsonAnnotation != null ? jsonAnnotation.style() : Style.DEFAULT; Options classOptions = source.getAnnotation(Options.class); Options options = method.getAnnotation(Options.class); p(method.getReadableDeclaration(false, false, false, false, true) + " {").i(1); { String restMethod = getRestMethod(method); LinkedList<JParameter> args = new LinkedList<JParameter>(Arrays.asList(method.getParameters())); // the last arg should be the callback. if (args.isEmpty()) { getLogger() .log( ERROR, "Invalid rest method. Method must declare at least a callback argument: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } JParameter callbackArg = args.removeLast(); JClassType callbackType = callbackArg.getType().isClassOrInterface(); JClassType methodCallbackType = METHOD_CALLBACK_TYPE; if (callbackType == null || !callbackType.isAssignableTo(methodCallbackType)) { getLogger() .log( ERROR, "Invalid rest method. Last argument must be a " + methodCallbackType.getName() + " type: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } JClassType resultType = getCallbackTypeGenericClass(callbackType); String pathExpression = null; Path pathAnnotation = method.getAnnotation(Path.class); if (pathAnnotation != null) { pathExpression = wrap(pathAnnotation.value()); } JParameter contentArg = null; HashMap<String, JParameter> queryParams = new HashMap<String, JParameter>(); HashMap<String, JParameter> formParams = new HashMap<String, JParameter>(); HashMap<String, JParameter> headerParams = new HashMap<String, JParameter>(); for (JParameter arg : args) { PathParam paramPath = arg.getAnnotation(PathParam.class); if (paramPath != null) { if (pathExpression == null) { getLogger() .log( ERROR, "Invalid rest method. Invalid @PathParam annotation. Method is missing the @Path annotation: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } pathExpression = pathExpression(pathExpression, arg, paramPath); // .replaceAll(Pattern.quote("{" + paramPath.value() + "}"), // "\"+com.google.gwt.http.client.URL.encodePathSegment(" + toStringExpression(arg) + // ")+\""); if (arg.getAnnotation(Attribute.class) != null) { // allow part of the arg-object participate in as PathParam and the object goes over the // wire contentArg = arg; } continue; } QueryParam queryParam = arg.getAnnotation(QueryParam.class); if (queryParam != null) { queryParams.put(queryParam.value(), arg); continue; } FormParam formParam = arg.getAnnotation(FormParam.class); if (formParam != null) { formParams.put(formParam.value(), arg); continue; } HeaderParam headerParam = arg.getAnnotation(HeaderParam.class); if (headerParam != null) { headerParams.put(headerParam.value(), arg); continue; } if (!formParams.isEmpty()) { getLogger() .log( ERROR, "You can not have both @FormParam parameters and a content parameter: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } if (contentArg != null) { getLogger() .log( ERROR, "Invalid rest method. Only one content parameter is supported: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } contentArg = arg; } String acceptTypeBuiltIn = null; if (callbackType.equals(TEXT_CALLBACK_TYPE)) { acceptTypeBuiltIn = "CONTENT_TYPE_TEXT"; } else if (callbackType.equals(JSON_CALLBACK_TYPE)) { acceptTypeBuiltIn = "CONTENT_TYPE_JSON"; } else if (callbackType.isAssignableTo(OVERLAY_CALLBACK_TYPE)) { acceptTypeBuiltIn = "CONTENT_TYPE_JSON"; } else if (callbackType.equals(XML_CALLBACK_TYPE)) { acceptTypeBuiltIn = "CONTENT_TYPE_XML"; } p("final " + METHOD_CLASS + " __method ="); p("getResource()"); if (pathExpression != null) { p(".resolve(" + pathExpression + ")"); } for (Map.Entry<String, JParameter> entry : queryParams.entrySet()) { String expr = entry.getValue().getName(); JClassType type = entry.getValue().getType().isClassOrInterface(); if (type != null && isQueryParamListType(type)) { p( ".addQueryParams(" + wrap(entry.getKey()) + ", " + toIteratedStringExpression(entry.getValue()) + ")"); } else { p( ".addQueryParam(" + wrap(entry.getKey()) + ", " + toStringExpression(entry.getValue().getType(), expr) + ")"); } } // example: .get() p("." + restMethod + "();"); // Handle JSONP specific configuration... JSONP jsonpAnnotation = method.getAnnotation(JSONP.class); final boolean isJsonp = restMethod.equals(METHOD_JSONP) && jsonpAnnotation != null; if (isJsonp) { if (returnRequest && !method .getReturnType() .getQualifiedSourceName() .equals(JsonpRequest.class.getName())) { getLogger() .log( ERROR, "Invalid rest method. JSONP method must have void or JsonpRequest return types: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } if (jsonpAnnotation.callbackParam().length() > 0) { p( "((" + JSONP_METHOD_CLASS + ")__method).callbackParam(" + wrap(jsonpAnnotation.callbackParam()) + ");"); } if (jsonpAnnotation.failureCallbackParam().length() > 0) { p( "((" + JSONP_METHOD_CLASS + ")__method).failureCallbackParam(" + wrap(jsonpAnnotation.failureCallbackParam()) + ");"); } } else { if (returnRequest && !method.getReturnType().getQualifiedSourceName().equals(Request.class.getName())) { getLogger() .log( ERROR, "Invalid rest method. Non JSONP method must have void or Request return types: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } } // configure the dispatcher if (options != null && options.dispatcher() != Dispatcher.class) { // use the dispatcher configured for the method. p("__method.setDispatcher(" + options.dispatcher().getName() + ".INSTANCE);"); } else { // use the default dispatcher configured for the service.. p("__method.setDispatcher(this.dispatcher);"); } // configure the expected statuses.. if (options != null && options.expect().length != 0) { // Using method level defined expected status p("__method.expect(" + join(options.expect(), ", ") + ");"); } else if (classOptions != null && classOptions.expect().length != 0) { // Using class level defined expected status p("__method.expect(" + join(classOptions.expect(), ", ") + ");"); } // configure the timeout if (options != null && options.timeout() >= 0) { // Using method level defined value p("__method.timeout(" + options.timeout() + ");"); } else if (classOptions != null && classOptions.timeout() >= 0) { // Using class level defined value p("__method.timeout(" + classOptions.timeout() + ");"); } if (jsonpAnnotation == null) { Produces producesAnnotation = findAnnotationOnMethodOrEnclosingType(method, Produces.class); if (producesAnnotation != null) { p( "__method.header(" + RESOURCE_CLASS + ".HEADER_ACCEPT, " + wrap(producesAnnotation.value()[0]) + ");"); } else { // set the default accept header.... if (acceptTypeBuiltIn != null) { p( "__method.header(" + RESOURCE_CLASS + ".HEADER_ACCEPT, " + RESOURCE_CLASS + "." + acceptTypeBuiltIn + ");"); } else { p( "__method.header(" + RESOURCE_CLASS + ".HEADER_ACCEPT, " + RESOURCE_CLASS + ".CONTENT_TYPE_JSON);"); } } Consumes consumesAnnotation = findAnnotationOnMethodOrEnclosingType(method, Consumes.class); if (consumesAnnotation != null) { p( "__method.header(" + RESOURCE_CLASS + ".HEADER_CONTENT_TYPE, " + wrap(consumesAnnotation.value()[0]) + ");"); } // and set the explicit headers now (could override the accept header) for (Map.Entry<String, JParameter> entry : headerParams.entrySet()) { String expr = entry.getValue().getName(); p( "__method.header(" + wrap(entry.getKey()) + ", " + toStringExpression(entry.getValue().getType(), expr) + ");"); } } if (!formParams.isEmpty()) { p(FORM_POST_CONTENT_CLASS + " __formPostContent = new " + FORM_POST_CONTENT_CLASS + "();"); for (Map.Entry<String, JParameter> entry : formParams.entrySet()) { p( "__formPostContent.addParameter(" + wrap(entry.getKey()) + ", " + toFormStringExpression(entry.getValue(), classStyle) + ");"); } p("__method.form(__formPostContent.getTextContent());"); } if (contentArg != null) { if (contentArg.getType() == STRING_TYPE) { p("__method.text(" + contentArg.getName() + ");"); } else if (contentArg.getType() == JSON_VALUE_TYPE) { p("__method.json(" + contentArg.getName() + ");"); } else if (contentArg.getType().isClass() != null && isOverlayArrayType(contentArg.getType().isClass())) { p("__method.json(new " + JSON_ARRAY_CLASS + "(" + contentArg.getName() + "));"); } else if (contentArg.getType().isClass() != null && contentArg.getType().isClass().isAssignableTo(OVERLAY_VALUE_TYPE)) { p("__method.json(new " + JSON_OBJECT_CLASS + "(" + contentArg.getName() + "));"); } else if (contentArg.getType() == DOCUMENT_TYPE) { p("__method.xml(" + contentArg.getName() + ");"); } else { JClassType contentClass = contentArg.getType().isClass(); if (contentClass == null) { contentClass = contentArg.getType().isClassOrInterface(); if (!locator.isCollectionType(contentClass)) { getLogger().log(ERROR, "Content argument must be a class."); throw new UnableToCompleteException(); } } jsonAnnotation = contentArg.getAnnotation(Json.class); Style style = jsonAnnotation != null ? jsonAnnotation.style() : classStyle; // example: // .json(Listings$_Generated_JsonEncoder_$.INSTANCE.encode(arg0) // ) p( "__method.json(" + locator.encodeExpression(contentClass, contentArg.getName(), style) + ");"); } } List<AnnotationResolver> annotationResolvers = getAnnotationResolvers(context, getLogger()); getLogger() .log( TreeLogger.DEBUG, "found " + annotationResolvers.size() + " additional AnnotationResolvers"); for (AnnotationResolver a : annotationResolvers) { getLogger() .log( TreeLogger.DEBUG, "(" + a.getClass().getName() + ") resolve `" + source.getName() + "#" + method.getName() + "ยด ..."); final Map<String, String[]> addDataParams = a.resolveAnnotation(getLogger(), source, method, restMethod); if (addDataParams != null) { for (String s : addDataParams.keySet()) { final StringBuilder sb = new StringBuilder(); final List<String> classList = Arrays.asList(addDataParams.get(s)); sb.append("["); for (int i = 0; i < classList.size(); ++i) { sb.append("\\\"").append(classList.get(i)).append("\\\""); if ((i + 1) < classList.size()) { sb.append(","); } } sb.append("]"); getLogger() .log(TreeLogger.DEBUG, "add call with (\"" + s + "\", \"" + sb.toString() + "\")"); p("__method.addData(\"" + s + "\", \"" + sb.toString() + "\");"); } } } if (acceptTypeBuiltIn != null) { // TODO: shouldn't we also have a cach in here? p(returnRequest(returnRequest, isJsonp) + "__method.send(" + callbackArg.getName() + ");"); } else if (isJsonp) { p(returnRequest(returnRequest, isJsonp) + "((" + JSONP_METHOD_CLASS + ")__method).send(new " + ABSTRACT_ASYNC_CALLBACK_CLASS + "<" + resultType.getParameterizedQualifiedSourceName() + ">((" + JSONP_METHOD_CLASS + ")__method, " + callbackArg.getName() + ") {") .i(1); { p("protected " + resultType.getParameterizedQualifiedSourceName() + " parseResult(" + JSON_VALUE_CLASS + " result) throws Exception {") .i(1); { if (resultType.getParameterizedQualifiedSourceName().equals("java.lang.Void")) { p("return (java.lang.Void) null;"); } else { p("try {").i(1); { if (resultType.isAssignableTo(locator.LIST_TYPE)) { p("result = new " + JSON_ARRAY_CLASS + "(result.getJavaScriptObject());"); } jsonAnnotation = method.getAnnotation(Json.class); Style style = jsonAnnotation != null ? jsonAnnotation.style() : classStyle; p("return " + locator.decodeExpression(resultType, "result", style) + ";"); } i(-1).p("} catch (Throwable __e) {").i(1); { p( "throw new " + RESPONSE_FORMAT_EXCEPTION_CLASS + "(\"Response was NOT a valid JSON document\", __e);"); } i(-1).p("}"); } } i(-1).p("}"); } i(-1).p("});"); } else { p("try {").i(1); { p(returnRequest(returnRequest, isJsonp) + "__method.send(new " + ABSTRACT_REQUEST_CALLBACK_CLASS + "<" + resultType.getParameterizedQualifiedSourceName() + ">(__method, " + callbackArg.getName() + ") {") .i(1); { p("protected " + resultType.getParameterizedQualifiedSourceName() + " parseResult() throws Exception {") .i(1); { if (resultType.getParameterizedQualifiedSourceName().equals("java.lang.Void")) { p("return (java.lang.Void) null;"); } else { p("try {").i(1); { jsonAnnotation = method.getAnnotation(Json.class); Style style = jsonAnnotation != null ? jsonAnnotation.style() : classStyle; p( "return " + locator.decodeExpression( resultType, JSON_PARSER_CLASS + ".parse(__method.getResponse().getText())", style) + ";"); } i(-1).p("} catch (Throwable __e) {").i(1); { p( "throw new " + RESPONSE_FORMAT_EXCEPTION_CLASS + "(\"Response was NOT a valid JSON document\", __e);"); } i(-1).p("}"); } } i(-1).p("}"); } i(-1).p("});"); } i(-1).p("} catch (" + REQUEST_EXCEPTION_CLASS + " __e) {").i(1); { p(callbackArg.getName() + ".onFailure(__method,__e);"); if (returnRequest) { p("return null;"); } } i(-1).p("}"); } } i(-1).p("}"); }
private EntityProxyModel getEntityProxyType(JClassType entityProxyType) throws UnableToCompleteException { entityProxyType = ModelUtils.ensureBaseType(entityProxyType); EntityProxyModel toReturn = peers.get(entityProxyType); if (toReturn == null) { EntityProxyModel.Builder inProgress = peerBuilders.get(entityProxyType); if (inProgress != null) { toReturn = inProgress.peek(); } } if (toReturn == null) { EntityProxyModel.Builder builder = new EntityProxyModel.Builder(); peerBuilders.put(entityProxyType, builder); // Validate possible super-proxy types first for (JClassType supertype : entityProxyType.getFlattenedSupertypeHierarchy()) { List<EntityProxyModel> superTypes = new ArrayList<EntityProxyModel>(); if (supertype != entityProxyType && shouldAttemptProxyValidation(supertype)) { superTypes.add(getEntityProxyType(supertype)); } builder.setSuperProxyTypes(superTypes); } builder.setQualifiedBinaryName(ModelUtils.getQualifiedBaseBinaryName(entityProxyType)); builder.setQualifiedSourceName(ModelUtils.getQualifiedBaseSourceName(entityProxyType)); if (entityProxyInterface.isAssignableFrom(entityProxyType)) { builder.setType(Type.ENTITY); } else if (valueProxyInterface.isAssignableFrom(entityProxyType)) { builder.setType(Type.VALUE); } else { poison( "The type %s is not assignable to either %s or %s", entityProxyInterface.getQualifiedSourceName(), valueProxyInterface.getQualifiedSourceName()); // Cannot continue, since knowing the behavior is crucial die(poisonedMessage()); } // Get the server domain object type ProxyFor proxyFor = entityProxyType.getAnnotation(ProxyFor.class); ProxyForName proxyForName = entityProxyType.getAnnotation(ProxyForName.class); JsonRpcProxy jsonRpcProxy = entityProxyType.getAnnotation(JsonRpcProxy.class); if (proxyFor == null && proxyForName == null && jsonRpcProxy == null) { poison( "The %s type does not have a @%s, @%s, or @%s annotation", entityProxyType.getQualifiedSourceName(), ProxyFor.class.getSimpleName(), ProxyForName.class.getSimpleName(), JsonRpcProxy.class.getSimpleName()); } // Look at the methods declared on the EntityProxy List<RequestMethod> requestMethods = new ArrayList<RequestMethod>(); Map<String, JMethod> duplicatePropertyGetters = new HashMap<String, JMethod>(); for (JMethod method : entityProxyType.getInheritableMethods()) { if (method.getEnclosingType().equals(entityProxyInterface)) { // Ignore methods on EntityProxy continue; } RequestMethod.Builder methodBuilder = new RequestMethod.Builder(); methodBuilder.setDeclarationMethod(entityProxyType, method); JType transportedType; String name = method.getName(); if (JBeanMethod.GET.matches(method)) { transportedType = method.getReturnType(); String propertyName = JBeanMethod.GET.inferName(method); JMethod previouslySeen = duplicatePropertyGetters.get(propertyName); if (previouslySeen == null) { duplicatePropertyGetters.put(propertyName, method); } else { poison( "Duplicate accessors for property %s: %s() and %s()", propertyName, previouslySeen.getName(), method.getName()); } } else if (JBeanMethod.SET.matches(method) || JBeanMethod.SET_BUILDER.matches(method)) { transportedType = method.getParameters()[0].getType(); } else if (name.equals("stableId") && method.getParameters().length == 0) { // Ignore any overload of stableId continue; } else { poison("The method %s is neither a getter nor a setter", method.getReadableDeclaration()); continue; } validateTransportableType(methodBuilder, transportedType, false); RequestMethod requestMethod = methodBuilder.build(); requestMethods.add(requestMethod); } builder .setExtraTypes(checkExtraTypes(entityProxyType, false)) .setRequestMethods(requestMethods); toReturn = builder.build(); peers.put(entityProxyType, toReturn); peerBuilders.remove(entityProxyType); } return toReturn; }
/** * Generates the client's asynchronous proxy method. * * @param serializableTypeOracle the type oracle */ protected void generateProxyMethod( SourceWriter w, SerializableTypeOracle serializableTypeOracle, TypeOracle typeOracle, JMethod syncMethod, JMethod asyncMethod) { w.println(); // Write the method signature JType asyncReturnType = asyncMethod.getReturnType().getErasedType(); w.print("public "); w.print(asyncReturnType.getQualifiedSourceName()); w.print(" "); w.print(asyncMethod.getName() + "("); boolean needsComma = false; NameFactory nameFactory = new NameFactory(); JParameter[] asyncParams = asyncMethod.getParameters(); for (int i = 0; i < asyncParams.length; ++i) { JParameter param = asyncParams[i]; if (needsComma) { w.print(", "); } else { needsComma = true; } /* * Ignoring the AsyncCallback parameter, if any method requires a call to * SerializationStreamWriter.writeObject we need a try catch block */ JType paramType = param.getType(); paramType = paramType.getErasedType(); w.print(paramType.getQualifiedSourceName()); w.print(" "); String paramName = param.getName(); nameFactory.addName(paramName); w.print(paramName); } w.println(") {"); w.indent(); String helperName = nameFactory.createName("helper"); String helperClassName = RemoteServiceProxy.ServiceHelper.class.getCanonicalName(); w.println( "%s %s = new %s(\"%s\", \"%s\");", helperClassName, helperName, helperClassName, getProxySimpleName(), syncMethod.getName()); w.println("try {"); w.indent(); // Write the parameter count followed by the parameter values JParameter[] syncParams = syncMethod.getParameters(); String streamWriterName = nameFactory.createName("streamWriter"); w.println( "%s %s = %s.start(REMOTE_SERVICE_INTERFACE_NAME, %s);", SerializationStreamWriter.class.getSimpleName(), streamWriterName, helperName, syncParams.length); for (JParameter param : syncParams) { JType paramType = param.getType().getErasedType(); String typeNameExpression = computeTypeNameExpression(paramType); assert typeNameExpression != null : "Could not compute a type name for " + paramType.getQualifiedSourceName(); w.println(streamWriterName + ".writeString(" + typeNameExpression + ");"); } // Encode all of the arguments to the asynchronous method, but exclude the // last argument which is the callback instance. // for (int i = 0; i < asyncParams.length - 1; ++i) { JParameter asyncParam = asyncParams[i]; w.print(streamWriterName + "."); w.print(Shared.getStreamWriteMethodNameFor(asyncParam.getType())); w.println("(" + asyncParam.getName() + ");"); } /* * Depending on the return type for the async method, return a * RequestBuilder, a Request, or nothing at all. */ JParameter callbackParam = asyncParams[asyncParams.length - 1]; JType returnType = syncMethod.getReturnType(); String callbackName = callbackParam.getName(); if (asyncReturnType == JPrimitiveType.VOID) { w.println( "%s.finish(%s, ResponseReader.%s);", helperName, callbackName, getResponseReaderFor(returnType).name()); } else if (asyncReturnType.getQualifiedSourceName().equals(RequestBuilder.class.getName())) { w.println( "return %s.finishForRequestBuilder(%s, ResponseReader.%s);", helperName, callbackName, getResponseReaderFor(returnType).name()); } else if (asyncReturnType.getQualifiedSourceName().equals(Request.class.getName())) { w.println( "return %s.finish(%s, ResponseReader.%s);", helperName, callbackName, getResponseReaderFor(returnType).name()); } else { // This method should have been caught by RemoteServiceAsyncValidator throw new RuntimeException( "Unhandled return type " + asyncReturnType.getQualifiedSourceName()); } w.outdent(); w.print("} catch (SerializationException "); String exceptionName = nameFactory.createName("ex"); w.println(exceptionName + ") {"); w.indent(); if (!asyncReturnType.getQualifiedSourceName().equals(RequestBuilder.class.getName())) { /* * If the method returns void or Request, signal the serialization error * immediately. If the method returns RequestBuilder, the error will be * signaled whenever RequestBuilder.send() is invoked. */ w.println(callbackName + ".onFailure(" + exceptionName + ");"); } if (asyncReturnType.getQualifiedSourceName().equals(RequestBuilder.class.getName())) { w.println( "return new " + FailingRequestBuilder.class.getName() + "(" + exceptionName + ", " + callbackName + ");"); } else if (asyncReturnType.getQualifiedSourceName().equals(Request.class.getName())) { w.println("return new " + FailedRequest.class.getName() + "();"); } else { assert asyncReturnType == JPrimitiveType.VOID; } w.outdent(); w.println("}"); w.outdent(); w.println("}"); }
private void createFindMethod(SourceWriter sourceWriter) { // We create more than one findMethod as the body of one method would be too large. This is the // int that we // increment to add to the name // i.e. findMethod0() int methodNum = 0; // This int keeps track of how many methods are generated. When it gets to 200 we create a new // findMethodX() // and chain it to the previous. int methodCount = 0; sourceWriter.println( "private GwtBindingMethod findOrCreateMethod(String obj, String methodName){ "); sourceWriter.indent(); sourceWriter.println("GwtBindingMethod newMethod;"); // dummy first condition, rest are "else if". Keeps us from having conditional logic. sourceWriter.println("if(false){ }"); for (JClassType type : implementingTypes) { // close last method, chain it to a new one. if (methodCount > 200) { sourceWriter.println("return findOrCreateMethod" + (methodNum) + "(obj, methodName);"); sourceWriter.println("}"); sourceWriter.println( "private GwtBindingMethod findOrCreateMethod" + (methodNum++) + "(String obj, String methodName){ "); sourceWriter.println("GwtBindingMethod newMethod;"); // dummy first condition, rest are "else if". Keeps us from having conditional logic. sourceWriter.println("if(false){ }"); methodCount = 0; } String keyRoot = generateTypeKey(type); // if(type.isAbstract()){ // System.out.println("abstract"); // continue; // } // determine if there are any methods that are bindable before writing out conditional for // class JClassType loopType = type; boolean hasBindableMethods = false; JClassType eventSourceType = null; try { eventSourceType = typeOracle.getType("org.pentaho.ui.xul.XulEventSource"); } catch (NotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } // CHECKSTYLE IGNORE Indentation FOR NEXT 1 LINES outer: while (loopType.getSuperclass() != null && loopType.getSimpleSourceName().equals("Object") == false && loopType.isAssignableTo(eventSourceType)) { for (JMethod m : loopType.getMethods()) { if (m.isPublic() && m.getAnnotation(Bindable.class) != null) { hasBindableMethods = true; break outer; } } loopType = loopType.getSuperclass(); } if (hasBindableMethods == false) { continue; } sourceWriter.println("else if(obj.equals(\"" + type.getQualifiedSourceName() + "\")){ "); try { loopType = type; sourceWriter.indent(); // Loop over class heirarchy and generate methods for every object that is declared a // XulEventSource while (loopType.getSuperclass() != null && loopType.getSimpleSourceName().equals("Object") == false && loopType.isAssignableTo(eventSourceType)) { String superName = generateTypeKey(loopType); boolean first = true; for (JMethod m : loopType.getMethods()) { methodCount++; if (!m.isPublic() || m.getAnnotation(Bindable.class) == null) { continue; } sourceWriter.println( (first ? "" : "else ") + "if(methodName.equals(\"" + m.getName() + "\")){ "); if (first) { first = false; } sourceWriter.indent(); String methodName = m.getName(); // check to see if we've already processed this classes' method. Point to that class // instead. if (generatedMethods.contains((superName + "_" + methodName).toLowerCase()) && type != loopType) { sourceWriter.println("return findOrCreateMethod(\"" + superName + "\", methodName);"); } else { // See if it's already been created and cached. If so, return that. String keyName = (keyRoot + "_" + methodName).toLowerCase(); sourceWriter.println( "GwtBindingMethod found = wrappedTypes.get(\"" + keyName + "\");"); sourceWriter.println("if(found != null){"); sourceWriter.indent(); sourceWriter.println("return found;"); sourceWriter.outdent(); sourceWriter.println("} else {"); sourceWriter.indent(); // Not cached, create a new instance and put it in the cache. sourceWriter.println("newMethod = new GwtBindingMethod(){"); sourceWriter.println( "public Object invoke(Object obj, Object[] args) throws XulException { "); sourceWriter.indent(); sourceWriter.println("try{"); sourceWriter.println( loopType.getQualifiedSourceName() + " target = (" + loopType.getQualifiedSourceName() + ") obj;"); JParameter[] params = m.getParameters(); String argList = ""; int pos = 0; for (JParameter param : params) { if (pos > 0) { argList += ", "; } argList += "(" + getTypeName(param.getType()) + ") args[" + pos + "]"; pos++; } if (isVoidReturn(m.getReturnType())) { sourceWriter.println("target." + methodName + "(" + argList + ");"); sourceWriter.println("return null;"); } else { sourceWriter.println( "return " + boxReturnType(m) + " target." + methodName + "(" + argList + ");"); } sourceWriter.println( "}catch(Exception e){ e.printStackTrace(); throw new XulException(\"error with " + type.getQualifiedSourceName() + "\"+e.getMessage());}"); sourceWriter.println("}"); sourceWriter.outdent(); sourceWriter.println("};"); // Add it to the HashMap cache as type and decendant type if available. sourceWriter.println("wrappedTypes.put((\"" + keyName + "\"), newMethod);"); if (keyRoot.equals(superName) == false) { sourceWriter.println("wrappedTypes.put((\"" + keyName + "\"), newMethod);"); } generatedMethods.add((keyRoot + "_" + methodName).toLowerCase()); generatedMethods.add((superName + "_" + methodName).toLowerCase()); sourceWriter.println("return newMethod;"); sourceWriter.outdent(); sourceWriter.println("}"); } sourceWriter.outdent(); sourceWriter.println("}"); } // go up a level in the heirarchy and check again. loopType = loopType.getSuperclass(); } sourceWriter.outdent(); sourceWriter.println("}"); } catch (Exception e) { // record to logger that Map generation threw an exception logger.log(TreeLogger.ERROR, "PropertyMap ERROR!!!", e); } } sourceWriter.outdent(); // This is the end of the line, if not found return null. sourceWriter.println("return null;"); sourceWriter.println("}"); }