private static Set createAttributes()

in scim-spec/scim-spec-schema/src/main/java/org/apache/directory/scim/spec/schema/Schemas.java [125:265]


  private static Set<Schema.Attribute> createAttributes(String urn, Optional<String> path, List<Field> fieldList, Set<String> invalidAttributes, String nameBase) throws ScimResourceInvalidException {
    Set<Schema.Attribute> attributes = new TreeSet<>(Comparator.comparing(o -> o.name));

    for (Field f : fieldList) {
      ScimAttribute sa = f.getAnnotation(ScimAttribute.class);

      log.debug("++++++++++++++++++++ Processing field " + f.getName());
      if (sa == null) {
        log.debug("Attribute {} did not have a ScimAttribute annotation", f.getName());
        continue;
      }

      String attributeName;
      f.setAccessible(true);

      if (sa.name() == null || sa.name().isEmpty()) {
        attributeName = f.getName();
      } else {
        attributeName = sa.name();
      }

      if (f.getType().isPrimitive() && !sa.required()) {
        invalidAttributes.add(nameBase + "." + attributeName);
        continue;
      }

      //TODO - Fix this to look for the two types of canonical attributes
      Schema.Attribute attribute = new Schema.Attribute();
      attribute.setAccessor(Schema.AttributeAccessor.forField(f));
      attribute.setName(attributeName);
      attribute.setSchemaUrn(urn);
      path.ifPresentOrElse(p -> attribute.setPath(p + "." + attributeName), () -> attribute.setPath(attributeName));

      List<String> canonicalTypes = null;
      Field [] enumFields = sa.canonicalValueEnum().getFields();
      log.debug("Gathered fields of off the enum, there are {} {}", enumFields.length, sa.canonicalValueEnum().getName());

      if (enumFields.length != 0) {

        //This looks goofy, but there's always at least the default value, so it's not an empty list
        if (sa.canonicalValueList().length != 1 && !sa.canonicalValueList()[0].isEmpty()) {
          throw new ScimResourceInvalidException("You cannot set both the canonicalEnumValue and canonicalValueList attributes on the same ScimAttribute");
        }

        canonicalTypes = new ArrayList<>();

        for (Field field : enumFields) {
          XmlEnumValue[] annotation = field.getAnnotationsByType(XmlEnumValue.class);

          if (annotation.length != 0) {
            canonicalTypes.add(annotation[0].value());
          } else {
            canonicalTypes.add(field.getName());
          }
        }
      } else {
        canonicalTypes = Arrays.asList(sa.canonicalValueList());
      }

      // If we just have the default single empty string, set to null
      if (canonicalTypes.isEmpty() || (canonicalTypes.size() == 1 && canonicalTypes.get(0).isEmpty())) {
        attribute.setCanonicalValues(null);
      } else {
        attribute.setCanonicalValues(new HashSet<>(canonicalTypes));
      }

      attribute.setCaseExact(sa.caseExact());
      attribute.setDescription(sa.description());

      Class<?> typeClass;
      if (Collection.class.isAssignableFrom(f.getType())) {
        log.debug("Attribute: '{}' is a collection", attributeName);
        ParameterizedType stringListType = (ParameterizedType) f.getGenericType();
        typeClass = (Class<?>) stringListType.getActualTypeArguments()[0];
        attribute.setMultiValued(true);
      } else if (f.getType().isArray()) {
        log.debug("Attribute: '{}' is an array", attributeName);
        typeClass = f.getType().getComponentType();

        // special case for byte[]
        if (typeClass == byte.class) {
          typeClass = byte[].class;
        } else {
          attribute.setMultiValued(true);
        }
      } else {
        typeClass = f.getType();
        attribute.setMultiValued(false);
      }

      log.debug("Attempting to set the attribute type, raw value = {}", typeClass);
      Schema.Attribute.Type type = CLASS_TO_TYPE.getOrDefault(typeClass, Schema.Attribute.Type.COMPLEX);
      attribute.setType(type);

      if (f.getAnnotation(ScimResourceIdReference.class) != null) {
        if (type == Schema.Attribute.Type.STRING) {
          attribute.setScimResourceIdReference(true);
        } else {
          log.warn("Field annotated with @ScimResourceIdReference must be a string: {}", f);
        }
      }
      attribute.setMutability(sa.mutability());

      List<String> refType = Arrays.asList(sa.referenceTypes());

      // If we just have the default single empty string, set to null
      if (refType.isEmpty() || (refType.size() == 1 && refType.get(0).isEmpty())) {
        attribute.setReferenceTypes(null);
      } else {
        attribute.setType(Schema.Attribute.Type.REFERENCE);
        attribute.setReferenceTypes(Arrays.asList(sa.referenceTypes()));
      }

      attribute.setRequired(sa.required());
      attribute.setReturned(sa.returned());
      attribute.setUniqueness(sa.uniqueness());

      ScimType st = f.getType().getAnnotation(ScimType.class);

      if (attribute.getType() == Schema.Attribute.Type.COMPLEX || st != null) {
        Class<?> componentType;
        if (!attribute.isMultiValued()) {
          componentType = f.getType();
        } else if (f.getType().isArray()) {
          componentType = f.getType().getComponentType();
        } else {
          ParameterizedType stringListType = (ParameterizedType) f.getGenericType();
          componentType = (Class<?>) stringListType.getActualTypeArguments()[0];
        }

        List<Field> fl = getFieldsUpTo(componentType, Object.class);
        Set<Schema.Attribute> la = createAttributes(urn, Optional.of(attributeName), fl, invalidAttributes, nameBase + "." + f.getName());

        attribute.setSubAttributes(la, Schema.Attribute.AddAction.APPEND);
      }
      attributes.add(attribute);
    }

    log.debug("Returning {} attributes", attributes.size());
    return attributes;
  }