private void implementSetter( TreeLogger logger, MethodBuffer mb, JMethod method, ModelMagic manifest, HasModelFields models, Annotation[] annos) { JPrimitiveType primitive = method.getReturnType().isPrimitive(); boolean isVoid = primitive == JPrimitiveType.VOID; boolean isFluent = ModelGeneratorGwt.isFluent(method); String name = ModelGeneratorGwt.fieldName(method, manifest); ModelField field = models.getOrMakeField(name); field.addSetter( X_Source.binaryToSource(method.getReturnType().getQualifiedBinaryName()), name, ModelGeneratorGwt.toTypes(method.getParameterTypes())) .fluent = isFluent; if (!isFluent && !isVoid) { mb.println("var value = getProperty(\"" + name + "\");"); } JParameter[] params = method.getParameters(); if (params.length != 1) throw new NotConfiguredCorrectly( "A setter method, " + method.getJsniSignature() + " must have exactly one parameter"); mb.println("setProperty(\"" + name + "\", A);"); if (!isVoid) mb.println("return " + (isFluent ? "this;" : "value;")); }
Collection<JMethod> extractMethods( TreeLogger logger, SourceBuilder<ModelMagic> sb, GeneratorContext ctx, JClassType type) throws UnableToCompleteException { assert type.isInterface() != null; ModelMagic models = sb.getPayload(); Map<String, JMethod> uniqueMethods = new LinkedHashMap<String, JMethod>(); JMethod[] existing = type.getInheritableMethods(); Set<? extends JClassType> hierarchy = type.getFlattenedSupertypeHierarchy(); if (ModelGeneratorGwt.allAbstract(existing)) { // still an interface; grab our root type. existing = models.getRootType(logger, ctx).getInheritableMethods(); } for (JMethod method : existing) { if (!method.isAbstract()) uniqueMethods.put(xapi.dev.model.ModelGeneratorGwt.toSignature(method), method); } boolean debug = logger.isLoggable(Type.DEBUG); for (JClassType next : hierarchy) { if (next.isInterface() != null) { sb.getClassBuffer().addInterfaces(next.getQualifiedSourceName()); for (JMethod method : next.getMethods()) { String sig = ModelGeneratorGwt.toSignature(method); if (!uniqueMethods.containsKey(sig)) uniqueMethods.put(sig, method); } } else { for (JMethod method : next.getMethods()) { String sig = ModelGeneratorGwt.toSignature(method); if (uniqueMethods.containsKey(sig)) { if (debug) logger.log( Type.WARN, "Found multiple model methods for " + type.getName() + "::" + sig + "; preferring method from " + uniqueMethods.get(sig).getEnclosingType().getName()); } else { uniqueMethods.put(sig, method); } } } } return uniqueMethods.values(); }
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;"); } }
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."; } }
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); }