/** * Checks type and its supertypes for {@link ExtraTypes} annotations. * * @param type the type to examine * @param addModelExtraTypes if {@code true} the contents of the {@link #extraTypes} field will be * added to the returned list. */ private List<EntityProxyModel> checkExtraTypes(JClassType type, boolean addModelExtraTypes) throws UnableToCompleteException { Set<EntityProxyModel> toReturn = new LinkedHashSet<EntityProxyModel>(); if (addModelExtraTypes && extraTypes != null) { toReturn.addAll(extraTypes); } for (JClassType toExamine : type.getFlattenedSupertypeHierarchy()) { ExtraTypes proxyExtraTypes = toExamine.getAnnotation(ExtraTypes.class); if (proxyExtraTypes != null) { for (Class<? extends BaseProxy> clazz : proxyExtraTypes.value()) { JClassType proxy = oracle.findType(clazz.getCanonicalName()); if (proxy == null) { poison( "Unknown class %s in @%s", clazz.getCanonicalName(), ExtraTypes.class.getSimpleName()); } else { toReturn.add(getEntityProxyType(proxy)); } } } } if (toReturn.isEmpty()) { return Collections.emptyList(); } return new ArrayList<EntityProxyModel>(toReturn); }
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(); }
@Override public void visitClientRpc(TreeLogger logger, JClassType type, ConnectorBundle bundle) throws UnableToCompleteException { checkGenericType(logger, type); Set<? extends JClassType> hierarchy = type.getFlattenedSupertypeHierarchy(); for (JClassType subType : hierarchy) { JMethod[] methods = subType.getMethods(); for (JMethod method : methods) { checkReturnType(logger, method); bundle.setNeedsInvoker(type, method); bundle.setNeedsParamTypes(type, method); if (method.getAnnotation(NoLayout.class) != null) { bundle.setMethodAttribute(type, method, MethodAttribute.NO_LAYOUT); } JType[] parameterTypes = method.getParameterTypes(); for (JType paramType : parameterTypes) { bundle.setNeedsSerialize(paramType); } } } }
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); }
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; }