package ru.ulstu.odin.service; import com.fasterxml.jackson.annotation.JsonIgnore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import ru.ulstu.core.error.OdinException; import ru.ulstu.odin.model.OdinBooleanField; import ru.ulstu.odin.model.OdinCollectionField; import ru.ulstu.odin.model.OdinDateField; import ru.ulstu.odin.model.OdinDto; import ru.ulstu.odin.model.OdinField; import ru.ulstu.odin.model.OdinMetadata; import ru.ulstu.odin.model.OdinNumericField; import ru.ulstu.odin.model.OdinObjectField; import ru.ulstu.odin.model.OdinStringField; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @Service public class OdinService { private final Logger log = LoggerFactory.getLogger(OdinService.class); private final Map aliases; public OdinService() { aliases = new HashMap<>(); aliases.put("boolean", OdinField.OdinFieldType.BOOLEAN); aliases.put("date", OdinField.OdinFieldType.DATE); aliases.put("instant", OdinField.OdinFieldType.DATE); aliases.put("localdate", OdinField.OdinFieldType.DATE); aliases.put("localtime", OdinField.OdinFieldType.DATE); aliases.put("localdatetime", OdinField.OdinFieldType.DATE); aliases.put("int", OdinField.OdinFieldType.NUMERIC); aliases.put("integer", OdinField.OdinFieldType.NUMERIC); aliases.put("double", OdinField.OdinFieldType.NUMERIC); aliases.put("float", OdinField.OdinFieldType.NUMERIC); aliases.put("long", OdinField.OdinFieldType.NUMERIC); aliases.put("string", OdinField.OdinFieldType.STRING); aliases.put("collection", OdinField.OdinFieldType.COLLECTION); aliases.put("object", OdinField.OdinFieldType.OBJECT); } private OdinField.OdinFieldType getFieldTypeByTypeAlias(String fieldType) { return Optional.ofNullable(aliases.get(fieldType)).orElse(OdinField.OdinFieldType.UNKNOWN); } private String getTypeAlias(String fieldType) { return fieldType.replaceAll(".*\\.", "").toLowerCase().trim(); } private OdinField.OdinFieldType getFieldType(Field field) { final String typeAlias; if (Collection.class.isAssignableFrom(field.getType())) { typeAlias = "collection"; } else if (OdinDto.class.isAssignableFrom(field.getType())) { typeAlias = "object"; } else { typeAlias = getTypeAlias(field.getGenericType().getTypeName()); } return getFieldTypeByTypeAlias(typeAlias); } private List getDtoMetaModel(Class dtoClass) { return Arrays.stream(dtoClass.getDeclaredFields()) .filter(field -> !field.isAnnotationPresent(JsonIgnore.class) && !Modifier.isStatic(field.getModifiers())) .map(field -> { OdinField.OdinFieldType fieldType = getFieldType(field); switch (fieldType) { case BOOLEAN: return new OdinBooleanField(field); case DATE: return new OdinDateField(field); case NUMERIC: return new OdinNumericField(field); case STRING: return new OdinStringField(field); case COLLECTION: return new OdinCollectionField(field); case OBJECT: return new OdinObjectField(field); default: log.debug("Unknown type {}. Skip field {} of DTO {}.", field.getGenericType().getTypeName(), field.getName(), dtoClass.getSimpleName()); } return null; }) .filter(Objects::nonNull) .collect(Collectors.toList()); } public OdinMetadata getListModel(Class listDtoClass) { if (listDtoClass == null) { throw new OdinException("List DTO class is null"); } return new OdinMetadata(OdinDto.class.isAssignableFrom(listDtoClass), getDtoMetaModel(listDtoClass)); } public OdinMetadata getElementModel(Class elementDtoClass) { return elementDtoClass == null ? null : new OdinMetadata(true, getDtoMetaModel(elementDtoClass)); } }