in scim-spec/scim-spec-schema/src/main/java/org/apache/directory/scim/spec/schema/Schemas.java [114:294]
private static List<Schema.Attribute> createAttributes(String urn, List<Field> fieldList, Set<String> invalidAttributes, String nameBase) throws ScimResourceInvalidException {
List<Schema.Attribute> attributeList = new ArrayList<>();
for (Field f : fieldList) {
ScimAttribute sa = f.getAnnotation(ScimAttribute.class);
log.debug("++++++++++++++++++++ Processing field " + f.getName());
if (sa == null) {
log.debug("Attribute " + f.getName() + " did not have a ScimAttribute annotation");
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.setUrn(urn);
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<String>(canonicalTypes));
}
attribute.setCaseExact(sa.caseExact());
attribute.setDescription(sa.description());
String typeName = null;
if (Collection.class.isAssignableFrom(f.getType())) {
log.debug("We have a collection");
ParameterizedType stringListType = (ParameterizedType) f.getGenericType();
Class<?> attributeContainedClass = (Class<?>) stringListType.getActualTypeArguments()[0];
typeName = attributeContainedClass.getTypeName();
attribute.setMultiValued(true);
} else if (f.getType().isArray()) {
log.debug("We have an array");
Class<?> componentType = f.getType().getComponentType();
typeName = componentType.getTypeName();
attribute.setMultiValued(true);
} else {
typeName = f.getType().toString();
attribute.setMultiValued(false);
}
// attribute.setType(sa.type());
boolean attributeIsAString = false;
log.debug("Attempting to set the attribute type, raw value = " + typeName);
switch (typeName) {
case STRING_TYPE_IDENTIFIER:
case CHARACTER_ARRAY_TYPE_IDENTIFIER:
case BIG_C_CHARACTER_ARRAY_TYPE_IDENTIFIER:
log.debug("Setting type to String");
attribute.setType(Schema.Attribute.Type.STRING);
attributeIsAString = true;
break;
case INT_TYPE_IDENTIFIER:
case INTEGER_TYPE_IDENTIFIER:
log.debug("Setting type to integer");
attribute.setType(Schema.Attribute.Type.INTEGER);
break;
case FLOAT_TYPE_IDENTIFIER:
case BIG_F_FLOAT_TYPE_IDENTIFIER:
case DOUBLE_TYPE_IDENTIFIER:
case BIG_D_DOUBLE_TYPE_IDENTIFIER:
log.debug("Setting type to decimal");
attribute.setType(Schema.Attribute.Type.DECIMAL);
break;
case BOOLEAN_TYPE_IDENTIFIER:
case BIG_B_BOOLEAN_TYPE_IDENTIFIER:
log.debug("Setting type to boolean");
attribute.setType(Schema.Attribute.Type.BOOLEAN);
break;
case BYTE_ARRAY_TYPE_IDENTIFIER:
log.debug("Setting type to binary");
attribute.setType(Schema.Attribute.Type.BINARY);
break;
case DATE_TYPE_IDENTIFIER:
case LOCAL_DATE_TIME_TYPE_IDENTIFIER:
case LOCAL_TIME_TYPE_IDENTIFER:
case LOCAL_DATE_TYPE_IDENTIFER:
log.debug("Setting type to date time");
attribute.setType(Schema.Attribute.Type.DATE_TIME);
break;
case RESOURCE_REFERENCE_TYPE_IDENTIFIER:
log.debug("Setting type to reference");
attribute.setType(Schema.Attribute.Type.REFERENCE);
break;
default:
log.debug("Setting type to complex");
attribute.setType(Schema.Attribute.Type.COMPLEX);
}
if (f.getAnnotation(ScimResourceIdReference.class) != null) {
if (attributeIsAString) {
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.setReferenceTypes(Arrays.asList(sa.referenceTypes()));
}
attribute.setRequired(sa.required());
attribute.setReturned(sa.returned());
attribute.setUniqueness(sa.uniqueness());
//if (sa.type().equals(Type.COMPLEX))
org.apache.directory.scim.spec.annotation.ScimType st = f.getType().getAnnotation(ScimType.class);
if (attribute.getType() == Schema.Attribute.Type.COMPLEX || st != null) {
Class<?> componentType;
if (!attribute.isMultiValued()) {
componentType = f.getType();
attribute.setSubAttributes(createAttributes(urn, Arrays.asList(f.getType().getDeclaredFields()), invalidAttributes, nameBase + "." + f.getName()), Schema.Attribute.AddAction.APPEND);
} 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);
List<Schema.Attribute> la = createAttributes(urn, fl, invalidAttributes, nameBase + "." + f.getName());
attribute.setSubAttributes(la, Schema.Attribute.AddAction.APPEND);
}
attributeList.add(attribute);
}
attributeList.sort(Comparator.comparing(o -> o.name));
log.debug("Returning " + attributeList.size() + " attributes");
return attributeList;
}