Пример #1
0
  /**
   * 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());
  }
Пример #2
0
  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;
  }
Пример #3
0
  /**
   * 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;
  }
Пример #4
0
  /**
   * 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;
  }
Пример #5
0
  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;
  }
Пример #6
0
  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;
  }