package de.escalon.hypermedia; import de.escalon.hypermedia.affordance.DataType; import org.springframework.hateoas.Resource; import org.springframework.hateoas.ResourceSupport; import org.springframework.hateoas.Resources; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.util.*; /** * Created by Dietrich on 07.05.2016. */ public class ResourceTraversal { static final Set<String> FILTER_RESOURCE_SUPPORT = new HashSet<String>(Arrays.asList("class", "links", "id")); static class NullValue { } public static final NullValue NULL_VALUE = new NullValue(); public void traverseResource(ResourceSupportVisitor visitor, Object object) { Set<String> filtered = FILTER_RESOURCE_SUPPORT; if (object == null) { return; } try { // TODO: move all returns to else branch of property descriptor handling if (object instanceof Resource) { Resource<?> resource = (Resource<?>) object; if(!visitor.visitLinks(resource.getLinks())) { return; } traverseResource(visitor, resource.getContent()); return; } else if (object instanceof Resources) { Resources<?> resources = (Resources<?>) object; if(!visitor.visitLinks(resources.getLinks())) { return; } traverseResource(visitor, resources.getContent()); return; } else if (object instanceof ResourceSupport) { ResourceSupport resource = (ResourceSupport) object; if(!visitor.visitLinks(resource.getLinks())) { return; } // wrap object attributes below to avoid endless loop } else if (object instanceof Collection) { Collection<?> collection = (Collection<?>) object; if(!visitor.visitEnterCollection(collection)) { return; } for (Object item : collection) { traverseResource(visitor, item); } if(!visitor.visitLeaveCollection(collection)) { return; } return; } if (object instanceof Map) { Map<?, ?> map = (Map<?, ?>) object; for (Map.Entry<?, ?> entry : map.entrySet()) { String key = entry.getKey() .toString(); Object content = entry.getValue(); Object value = getContentAsScalarValue(content); // UberNode entryNode = new UberNode(); // objectNode.addData(entryNode); // entryNode.setName(key); // if (value != null) { // entryNode.setValue(value); // } else { // traverseResource(visitor, content); // } Class<?> type = content != null ? content.getClass() : null; if(!visitor.visitEnterProperty(key, type, value)) { return; } if (value != null) { // for each scalar property of a simple bean, add valuepair nodes to data if(!visitor.visitProperty(key, type, value)) { return; } } else { traverseResource(visitor, content); } if(!visitor.visitLeaveProperty(key, type, value)) { return; } } } else { Map<String, PropertyDescriptor> propertyDescriptors = PropertyUtils.getPropertyDescriptors(object); for (PropertyDescriptor propertyDescriptor : propertyDescriptors.values()) { String name = propertyDescriptor.getName(); if (filtered.contains(name)) { continue; } // UberNode propertyNode = new UberNode(); Object content = propertyDescriptor.getReadMethod() .invoke(object); // // if (isEmptyCollectionOrMap(content, propertyDescriptor.getPropertyType())) { // continue; // } // Object value = getContentAsScalarValue(content); // propertyNode.setName(name); // objectNode.addData(propertyNode); Class<?> propertyType = propertyDescriptor.getPropertyType(); if(!visitor.visitEnterProperty(name, propertyType, value)) { return; } if (value != null) { // for each scalar property of a simple bean, add valuepair nodes to data if(!visitor.visitProperty(name, propertyType, value)) { return; } } else { traverseResource(visitor, content); } if(!visitor.visitLeaveProperty(name, propertyType, value)) { return; } } Field[] fields = object.getClass() .getFields(); for (Field field : fields) { String name = field.getName(); if (!propertyDescriptors.containsKey(name)) { Object content = field.get(object); Class<?> type = field.getType(); // if (isEmptyCollectionOrMap(content, type)) { // continue; // } // UberNode propertyNode = new UberNode(); // Object value = getContentAsScalarValue(content); // propertyNode.setName(name); // objectNode.addData(propertyNode); // if (value != null) { // // for each scalar property of a simple bean, add valuepair nodes to data // propertyNode.setValue(value); // } else { // toUberData(propertyNode, content); // } if(!visitor.visitEnterProperty(name, type, value)) { return; } if (value != null) { // for each scalar property of a simple bean, add valuepair nodes to data if(!visitor.visitProperty(name, type, value)) { return; } } else { traverseResource(visitor, content); } if(!visitor.visitLeaveProperty(name, type, value)) { return; } } } } } catch (Exception ex) { throw new RuntimeException("failed to transform object " + object, ex); } } private static Object getContentAsScalarValue(Object content) { final Object value; if (content == null) { value = ResourceTraversal.NULL_VALUE; } else if (DataType.isSingleValueType(content.getClass())) { value = content.toString(); } else { value = null; } return value; } }