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;
}