/** Examine a RequestContext subtype to populate a ContextMethod. */ private void buildContextMethod(ContextMethod.Builder contextBuilder, JClassType contextType) throws UnableToCompleteException { Service serviceAnnotation = contextType.getAnnotation(Service.class); ServiceName serviceNameAnnotation = contextType.getAnnotation(ServiceName.class); JsonRpcService jsonRpcAnnotation = contextType.getAnnotation(JsonRpcService.class); if (serviceAnnotation == null && serviceNameAnnotation == null && jsonRpcAnnotation == null) { poison( "RequestContext subtype %s is missing a @%s or @%s annotation", contextType.getQualifiedSourceName(), Service.class.getSimpleName(), JsonRpcService.class.getSimpleName()); return; } List<RequestMethod> requestMethods = new ArrayList<RequestMethod>(); for (JMethod method : contextType.getInheritableMethods()) { if (method.getEnclosingType().equals(requestContextInterface)) { // Ignore methods declared in RequestContext continue; } RequestMethod.Builder methodBuilder = new RequestMethod.Builder(); methodBuilder.setDeclarationMethod(contextType, method); if (!validateContextMethodAndSetDataType(methodBuilder, method, jsonRpcAnnotation != null)) { continue; } requestMethods.add(methodBuilder.build()); } contextBuilder .setExtraTypes(checkExtraTypes(contextType, true)) .setRequestMethods(requestMethods); }
/** Examine a RequestContext method to see if it returns a transportable type. */ private boolean validateContextMethodAndSetDataType( RequestMethod.Builder methodBuilder, JMethod method, boolean allowSetters) throws UnableToCompleteException { JClassType requestReturnType = method.getReturnType().isInterface(); JClassType invocationReturnType; if (requestReturnType == null) { // Primitive return type poison(badContextReturnType(method, requestInterface, instanceRequestInterface)); return false; } if (instanceRequestInterface.isAssignableFrom(requestReturnType)) { // Instance method invocation JClassType[] params = ModelUtils.findParameterizationOf(instanceRequestInterface, requestReturnType); methodBuilder.setInstanceType(getEntityProxyType(params[0])); invocationReturnType = params[1]; } else if (requestInterface.isAssignableFrom(requestReturnType)) { // Static method invocation JClassType[] params = ModelUtils.findParameterizationOf(requestInterface, requestReturnType); invocationReturnType = params[0]; } else { // Unhandled return type, must be something random poison(badContextReturnType(method, requestInterface, instanceRequestInterface)); return false; } // Validate the parameters boolean paramsOk = true; JParameter[] params = method.getParameters(); for (int i = 0; i < params.length; ++i) { JParameter param = params[i]; paramsOk = validateTransportableType(new RequestMethod.Builder(), param.getType(), false) && paramsOk; } // Validate any extra properties on the request type for (JMethod maybeSetter : requestReturnType.getInheritableMethods()) { if (JBeanMethod.SET.matches(maybeSetter) || JBeanMethod.SET_BUILDER.matches(maybeSetter)) { if (allowSetters) { methodBuilder.addExtraSetter(maybeSetter); } else { poison(noSettersAllowed(maybeSetter)); } } } return validateTransportableType(methodBuilder, invocationReturnType, true); }
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 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; }