/** * Whether the any element is a collection. * * @return Whether the any element is a collection. */ public boolean isCollectionType() { DecoratedTypeMirror accessorType; Declaration delegate = getDelegate(); if (delegate instanceof FieldDeclaration) { accessorType = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(((FieldDeclaration) delegate).getType()); } else { accessorType = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(((PropertyDeclaration) delegate).getPropertyType()); } return accessorType.isInstanceOf(Collection.class.getName()); }
private boolean isInstanceOf(TypeDefinition typeDef, String name) { for (InterfaceType interfaceType : typeDef.getSuperinterfaces()) { if (((DecoratedInterfaceType) TypeMirrorDecorator.decorate(interfaceType)) .isInstanceOf(name)) { return true; } } ClassType superclass = typeDef.getSuperclass(); if (superclass != null && ((DecoratedClassType) TypeMirrorDecorator.decorate(superclass)).isInstanceOf(name)) { return true; } return false; }
/** * Loads the specified entity parameter according to the method signature override. * * @param signatureOverride The signature override. * @return The resource entity parameter. */ protected ResourceEntityParameter loadEntityParameter(ResourceMethodSignature signatureOverride) { try { Class<?> entityType = signatureOverride.input(); if (entityType != ResourceMethodSignature.NONE.class) { AnnotationProcessorEnvironment env = net.sf.jelly.apt.Context.getCurrentEnvironment(); TypeDeclaration type = env.getTypeDeclaration(entityType.getName()); return new ResourceEntityParameter(type, env.getTypeUtils().getDeclaredType(type)); } } catch (MirroredTypeException e) { DecoratedTypeMirror typeMirror = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(e.getTypeMirror()); if (typeMirror.isDeclared()) { if (typeMirror.isInstanceOf(ResourceMethodSignature.class.getName() + ".NONE")) { return null; } else { return new ResourceEntityParameter( ((DeclaredType) typeMirror).getDeclaration(), typeMirror); } } else { throw new ValidationException( getPosition(), "Illegal input type (must be a declared type): " + typeMirror); } } return null; }
/** * Loads the explicit output payload. * * @param signatureOverride The method signature override. * @return The output payload (explicit in the signature override. */ protected ResourceRepresentationMetadata loadOutputPayload( ResourceMethodSignature signatureOverride) { DecoratedTypeMirror returnType = (DecoratedTypeMirror) getReturnType(); try { Class<?> outputType = signatureOverride.output(); if (outputType != ResourceMethodSignature.NONE.class) { AnnotationProcessorEnvironment env = net.sf.jelly.apt.Context.getCurrentEnvironment(); TypeDeclaration type = env.getTypeDeclaration(outputType.getName()); return new ResourceRepresentationMetadata( env.getTypeUtils().getDeclaredType(type), returnType.getDocValue()); } } catch (MirroredTypeException e) { DecoratedTypeMirror typeMirror = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(e.getTypeMirror()); if (typeMirror.isDeclared()) { if (typeMirror.isInstanceOf(ResourceMethodSignature.class.getName() + ".NONE")) { return null; } return new ResourceRepresentationMetadata(typeMirror, returnType.getDocValue()); } else { throw new ValidationException( getPosition(), "Illegal output type (must be a declared type): " + typeMirror); } } return null; }
public ValidationResult validateTypeDefinition(TypeDefinition typeDef) { ValidationResult result = new ValidationResult(); // heatonra: using @XmlSeeAlso for a QName enum doesn't actually include the xmlns declaration, // so there's no reason to validate it here... // heatonra: I couldn't figure out a good way to validate the use of @XmlSeeAlso for extended // types at build-time. I think we'll have to rely on unit tests to validate this. if ("".equals(typeDef.getNamespace())) { result.addError(typeDef, "Type definition should not be in the empty namespace."); } if (typeDef.getName().toLowerCase().startsWith("web")) { result.addWarning( typeDef, "You probably don't want a type definition that starts with the name 'web'. Consider renaming using the @XmlType annotation."); } Collection<Attribute> attributes = typeDef.getAttributes(); if (attributes != null && !attributes.isEmpty()) { for (Attribute attribute : attributes) { boolean isURI = ((DecoratedTypeMirror) TypeMirrorDecorator.decorate(attribute.getAccessorType())) .isInstanceOf("org.gedcomx.common.URI"); if (isURI && !KnownXmlType.ANY_URI.getQname().equals(attribute.getBaseType().getQname())) { result.addError( attribute, "Accessors of type 'org.gedcomx.common.URI' should of type xs:anyURI. Please annotate the attribute with @XmlSchemaType(name = \"anyURI\", namespace = XMLConstants.W3C_XML_SCHEMA_NS_URI)"); } if ("id".equalsIgnoreCase(attribute.getName())) { if (!attribute.isXmlID()) { result.addError(attribute, "Id attributes should be annotated as @XmlID."); } } TypeMirror accessorType = attribute.getAccessorType(); if (accessorType instanceof EnumType && ((EnumType) accessorType).getDeclaration().getAnnotation(XmlQNameEnum.class) != null) { result.addError( attribute, "Accessors should not reference QName enums directly. You probably want to annotate this accessor with @XmlTransient."); } } } Value value = typeDef.getValue(); if (value != null) { TypeMirror accessorType = value.getAccessorType(); if (accessorType instanceof EnumType && ((EnumType) accessorType).getDeclaration().getAnnotation(XmlQNameEnum.class) != null) { result.addError( value, "Accessors should not reference QName enums directly. You probably want to annotate this accessor with @XmlTransient."); } boolean isURI = ((DecoratedTypeMirror) TypeMirrorDecorator.decorate(accessorType)) .isInstanceOf("org.gedcomx.common.URI"); if (isURI && !KnownXmlType.ANY_URI.getQname().equals(value.getBaseType().getQname())) { result.addError( value, "Accessors of type 'org.gedcomx.common.URI' should of type xs:anyURI. Please annotate the value with @XmlSchemaType(name = \"anyURI\", namespace = XMLConstants.W3C_XML_SCHEMA_NS_URI)"); } } Collection<Element> elements = typeDef.getElements(); if (elements != null && !elements.isEmpty()) { for (Element element : elements) { for (Element choice : element.getChoices()) { boolean isURI = ((DecoratedTypeMirror) TypeMirrorDecorator.decorate(choice.getAccessorType())) .isInstanceOf("org.gedcomx.common.URI"); if (isURI && !KnownXmlType.ANY_URI.getQname().equals(choice.getBaseType().getQname())) { result.addError( choice, "Accessors of type 'org.gedcomx.common.URI' should of type xs:anyURI. Please annotate the element with @XmlSchemaType(name = \"anyURI\", namespace = XMLConstants.W3C_XML_SCHEMA_NS_URI)"); } if ("href".equals(choice.getName())) { result.addError( choice, "Entity links should be make with an attribute named 'href'. You probably need to apply @XmlAttribute."); } TypeMirror accessorType = choice.getAccessorType(); if (accessorType instanceof EnumType && ((EnumType) accessorType).getDeclaration().getAnnotation(XmlQNameEnum.class) != null) { result.addError( choice, "Accessors should not reference QName enums directly. You probably want to annotate this accessor with @XmlTransient."); } QName ref = choice.getRef(); String ns = ref != null ? ref.getNamespaceURI() : choice.getNamespace(); if (ns == null || "".equals(ns)) { result.addError(choice, "Choice should not reference the empty namespace."); } } if (element.isCollectionType()) { if (!element.isWrapped() && element.getName().endsWith("s")) { if (!suppressWarning(element, "gedcomx:plural_xml_name")) { result.addWarning( element, "You may want to use @XmlElement to change the name to a non-plural form."); } } else { // make sure collection types have a proper json name. String jsonMemberName = element.getJsonMemberName(); if (!jsonMemberName.endsWith("s")) { if (!suppressWarning(element, "gedcomx:non_plural_json_name")) { result.addWarning( element, "Collection element should probably have a JSON name that ends with 's'. Consider annotating it with @JsonName."); } } else if (!element.isWrapped()) { if (element.getDelegate() instanceof PropertyDeclaration) { DecoratedMethodDeclaration getter = ((PropertyDeclaration) element.getDelegate()).getGetter(); if (getter == null || getter.getAnnotation(JsonProperty.class) == null || !jsonMemberName.equals(getter.getAnnotation(JsonProperty.class).value())) { result.addWarning( element, "Collection element is annotated with @JsonName, but the getter needs to also be annotated with @JsonProperty(\"" + jsonMemberName + "\")."); } DecoratedMethodDeclaration setter = ((PropertyDeclaration) element.getDelegate()).getSetter(); if (setter == null || setter.getAnnotation(JsonProperty.class) == null || !jsonMemberName.equals(setter.getAnnotation(JsonProperty.class).value())) { result.addWarning( element, "Collection element is annotated with @JsonName, but the setter needs to also be annotated with @JsonProperty(\"" + jsonMemberName + "\")."); } } else if (element.getDelegate() instanceof FieldDeclaration) { if (element.getAnnotation(JsonProperty.class) == null || !jsonMemberName.equals(element.getAnnotation(JsonProperty.class).value())) { result.addWarning( element, "Collection element is annotated with @JsonName, but the field needs to also be annotated with @JsonProperty(\"" + jsonMemberName + "\")."); } } } } } } } AnyElement anyElement = typeDef.getAnyElement(); if (anyElement != null) { if (!isInstanceOf(typeDef, SupportsExtensionElements.class.getName())) { result.addError( anyElement, "Type definitions that supply the 'any' element must implement " + SupportsExtensionElements.class.getName() + " so the 'any' elements can be serialized to/from JSON."); } if (!"extensionElements".equals(anyElement.getSimpleName())) { if (!suppressWarning(anyElement, "gedcomx:unconventional_any_element_name")) { result.addWarning( anyElement, "The 'any' element might be better named 'extensionElements' to conform to convention."); } } JsonIgnore getterIgnore = anyElement.getDelegate() instanceof PropertyDeclaration ? ((PropertyDeclaration) anyElement.getDelegate()) .getGetter() .getAnnotation(JsonIgnore.class) : anyElement.getAnnotation(JsonIgnore.class); JsonIgnore setterIgnore = anyElement.getDelegate() instanceof PropertyDeclaration ? ((PropertyDeclaration) anyElement.getDelegate()) .getGetter() .getAnnotation(JsonIgnore.class) : getterIgnore; if (getterIgnore == null || setterIgnore == null) { String message = "Properties annotated with @XmlAnyElement should be annotated with @JsonIgnore."; if (anyElement.getDelegate() instanceof PropertyDeclaration) { message += " (On both the getter and the setter.)"; } result.addError(anyElement, message); } } if (typeDef.isHasAnyAttribute()) { if (!isInstanceOf(typeDef, SupportsExtensionAttributes.class.getName())) { result.addError( anyElement, "Type definitions that supply the 'any' attribute must implement " + SupportsExtensionAttributes.class.getName() + " so the 'any' attributes can be serialized to/from JSON."); } MemberDeclaration anyAttribute = null; for (PropertyDeclaration prop : typeDef.getProperties()) { if (prop.getAnnotation(XmlAnyAttribute.class) != null) { anyAttribute = prop; } } if (anyAttribute == null) { // must be a field. for (FieldDeclaration field : typeDef.getFields()) { if (field.getAnnotation(XmlAnyAttribute.class) != null) { anyAttribute = field; } } } if (anyAttribute != null) { if (!"extensionAttributes".equals(anyAttribute.getSimpleName())) { if (!suppressWarning(anyElement, "gedcomx:unconventional_any_attribute_name")) { result.addWarning( anyElement, "The 'any' attribute might be better named 'extensionAttributes' to conform to convention."); } } JsonIgnore getterIgnore = anyAttribute instanceof PropertyDeclaration ? ((PropertyDeclaration) anyAttribute).getGetter().getAnnotation(JsonIgnore.class) : anyAttribute.getAnnotation(JsonIgnore.class); JsonIgnore setterIgnore = anyAttribute instanceof PropertyDeclaration ? ((PropertyDeclaration) anyAttribute).getGetter().getAnnotation(JsonIgnore.class) : getterIgnore; if (getterIgnore == null || setterIgnore == null) { String message = "Properties annotated with @XmlAnyAttribute should be annotated with @JsonIgnore."; if (anyAttribute instanceof PropertyDeclaration) { message += " (On both the getter and the setter.)"; } result.addError(anyAttribute, message); } } } return result; }
public ResourceMethod(MethodDeclaration delegate, Resource parent) { super(delegate); Set<String> httpMethods = new TreeSet<String>(); Collection<AnnotationMirror> mirrors = delegate.getAnnotationMirrors(); for (AnnotationMirror mirror : mirrors) { AnnotationTypeDeclaration annotationDeclaration = mirror.getAnnotationType().getDeclaration(); HttpMethod httpMethodInfo = annotationDeclaration.getAnnotation(HttpMethod.class); if (httpMethodInfo != null) { // request method designator found. httpMethods.add(httpMethodInfo.value()); } } if (httpMethods.isEmpty()) { throw new IllegalStateException( "A resource method must specify an HTTP method by using a request method designator annotation."); } this.httpMethods = httpMethods; Set<String> consumes; Consumes consumesInfo = delegate.getAnnotation(Consumes.class); if (consumesInfo != null) { consumes = new TreeSet<String>(Arrays.asList(JAXRSUtils.value(consumesInfo))); } else { consumes = new TreeSet<String>(parent.getConsumesMime()); } this.consumesMime = consumes; Set<String> produces; Produces producesInfo = delegate.getAnnotation(Produces.class); if (producesInfo != null) { produces = new TreeSet<String>(Arrays.asList(JAXRSUtils.value(producesInfo))); } else { produces = new TreeSet<String>(parent.getProducesMime()); } this.producesMime = produces; String subpath = null; Path pathInfo = delegate.getAnnotation(Path.class); if (pathInfo != null) { subpath = pathInfo.value(); } ResourceEntityParameter entityParameter; List<ResourceEntityParameter> declaredEntityParameters = new ArrayList<ResourceEntityParameter>(); List<ResourceParameter> resourceParameters; ResourceRepresentationMetadata outputPayload; ResourceMethodSignature signatureOverride = delegate.getAnnotation(ResourceMethodSignature.class); if (signatureOverride == null) { entityParameter = null; resourceParameters = new ArrayList<ResourceParameter>(); // if we're not overriding the signature, assume we use the real method signature. for (ParameterDeclaration parameterDeclaration : getParameters()) { if (ResourceParameter.isResourceParameter(parameterDeclaration)) { resourceParameters.add(new ResourceParameter(parameterDeclaration)); } else if (ResourceParameter.isFormBeanParameter(parameterDeclaration)) { resourceParameters.addAll(ResourceParameter.getFormBeanParameters(parameterDeclaration)); } else if (parameterDeclaration.getAnnotation(Context.class) == null) { entityParameter = new ResourceEntityParameter(this, parameterDeclaration); declaredEntityParameters.add(entityParameter); } } DecoratedTypeMirror returnTypeMirror; TypeHint hintInfo = getAnnotation(TypeHint.class); if (hintInfo != null) { try { Class hint = hintInfo.value(); AnnotationProcessorEnvironment env = net.sf.jelly.apt.Context.getCurrentEnvironment(); if (TypeHint.NO_CONTENT.class.equals(hint)) { returnTypeMirror = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(env.getTypeUtils().getVoidType()); } else { String hintName = hint.getName(); if (TypeHint.NONE.class.equals(hint)) { hintName = hintInfo.qualifiedName(); } if (!"##NONE".equals(hintName)) { TypeDeclaration type = env.getTypeDeclaration(hintName); returnTypeMirror = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(env.getTypeUtils().getDeclaredType(type)); } else { returnTypeMirror = (DecoratedTypeMirror) getReturnType(); } } } catch (MirroredTypeException e) { returnTypeMirror = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(e.getTypeMirror()); } returnTypeMirror.setDocComment(((DecoratedTypeMirror) getReturnType()).getDocComment()); } else { returnTypeMirror = (DecoratedTypeMirror) getReturnType(); if (getJavaDoc().get("returnWrapped") != null) { // support jax-doclets. see http://jira.codehaus.org/browse/ENUNCIATE-690 String fqn = getJavaDoc().get("returnWrapped").get(0); AnnotationProcessorEnvironment env = net.sf.jelly.apt.Context.getCurrentEnvironment(); TypeDeclaration type = env.getTypeDeclaration(fqn); if (type != null) { returnTypeMirror = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(env.getTypeUtils().getDeclaredType(type)); } } // in the case where the return type is com.sun.jersey.api.JResponse, // we can use the type argument to get the entity type if (returnTypeMirror.isClass() && returnTypeMirror.isInstanceOf("com.sun.jersey.api.JResponse")) { DecoratedClassType jresponse = (DecoratedClassType) returnTypeMirror; if (!jresponse.getActualTypeArguments().isEmpty()) { DecoratedTypeMirror responseType = (DecoratedTypeMirror) TypeMirrorDecorator.decorate( jresponse.getActualTypeArguments().iterator().next()); if (responseType.isDeclared()) { responseType.setDocComment(returnTypeMirror.getDocComment()); returnTypeMirror = responseType; } } } } outputPayload = returnTypeMirror.isVoid() ? null : new ResourceRepresentationMetadata(returnTypeMirror); } else { entityParameter = loadEntityParameter(signatureOverride); declaredEntityParameters.add(entityParameter); resourceParameters = loadResourceParameters(signatureOverride); outputPayload = loadOutputPayload(signatureOverride); } JavaDoc.JavaDocTagList doclets = getJavaDoc() .get( "RequestHeader"); // support jax-doclets. see // http://jira.codehaus.org/browse/ENUNCIATE-690 if (doclets != null) { for (String doclet : doclets) { int firstspace = doclet.indexOf(' '); String header = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; resourceParameters.add( new ExplicitResourceParameter(this, doc, header, ResourceParameterType.HEADER)); } } ArrayList<ResponseCode> statusCodes = new ArrayList<ResponseCode>(); ArrayList<ResponseCode> warnings = new ArrayList<ResponseCode>(); StatusCodes codes = getAnnotation(StatusCodes.class); if (codes != null) { for (org.codehaus.enunciate.jaxrs.ResponseCode code : codes.value()) { ResponseCode rc = new ResponseCode(); rc.setCode(code.code()); rc.setCondition(code.condition()); statusCodes.add(rc); } } doclets = getJavaDoc() .get("HTTP"); // support jax-doclets. see http://jira.codehaus.org/browse/ENUNCIATE-690 if (doclets != null) { for (String doclet : doclets) { int firstspace = doclet.indexOf(' '); String code = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; try { ResponseCode rc = new ResponseCode(); rc.setCode(Integer.parseInt(code)); rc.setCondition(doc); statusCodes.add(rc); } catch (NumberFormatException e) { // fall through... } } } Warnings warningInfo = getAnnotation(Warnings.class); if (warningInfo != null) { for (org.codehaus.enunciate.jaxrs.ResponseCode code : warningInfo.value()) { ResponseCode rc = new ResponseCode(); rc.setCode(code.code()); rc.setCondition(code.condition()); warnings.add(rc); } } codes = parent.getAnnotation(StatusCodes.class); if (codes != null) { for (org.codehaus.enunciate.jaxrs.ResponseCode code : codes.value()) { ResponseCode rc = new ResponseCode(); rc.setCode(code.code()); rc.setCondition(code.condition()); statusCodes.add(rc); } } warningInfo = parent.getAnnotation(Warnings.class); if (warningInfo != null) { for (org.codehaus.enunciate.jaxrs.ResponseCode code : warningInfo.value()) { ResponseCode rc = new ResponseCode(); rc.setCode(code.code()); rc.setCondition(code.condition()); warnings.add(rc); } } ResponseHeaders responseHeaders = parent.getAnnotation(ResponseHeaders.class); if (responseHeaders != null) { for (ResponseHeader header : responseHeaders.value()) { this.responseHeaders.put(header.name(), header.description()); } } responseHeaders = getAnnotation(ResponseHeaders.class); if (responseHeaders != null) { for (ResponseHeader header : responseHeaders.value()) { this.responseHeaders.put(header.name(), header.description()); } } doclets = getJavaDoc() .get( "ResponseHeader"); // support jax-doclets. see // http://jira.codehaus.org/browse/ENUNCIATE-690 if (doclets != null) { for (String doclet : doclets) { int firstspace = doclet.indexOf(' '); String header = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; this.responseHeaders.put(header, doc); } } this.entityParameter = entityParameter; this.resourceParameters = resourceParameters; this.subpath = subpath; this.parent = parent; this.statusCodes = statusCodes; this.warnings = warnings; this.representationMetadata = outputPayload; this.declaredEntityParameters = declaredEntityParameters; }