EntityWrapper.java

package au.com.mountainpass.hyperstate.core.entities;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.hateoas.Identifiable;
import org.springframework.http.HttpMethod;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.google.common.collect.ImmutableSet;

import au.com.mountainpass.hyperstate.core.Action;
import au.com.mountainpass.hyperstate.core.EntityRelationship;
import au.com.mountainpass.hyperstate.core.EntityRepository;
import au.com.mountainpass.hyperstate.core.JavaAction;
import au.com.mountainpass.hyperstate.core.JavaLink;
import au.com.mountainpass.hyperstate.core.Link;
import au.com.mountainpass.hyperstate.core.NavigationalRelationship;
import au.com.mountainpass.hyperstate.core.Relationship;

@JsonPropertyOrder({ "class", "properties", "entities", "actions", "links",
        "title" })
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
public class EntityWrapper<T> extends Entity implements Identifiable<String> {
    private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    private static final int PAGE_SIZE = 10;

    private Map<String, Action<?>> actions = new HashMap<>();

    private Collection<EntityRelationship> entities = null;

    @JsonProperty("links")
    private Set<NavigationalRelationship> navigationalRelationships = new HashSet<>();

    T properties;

    private String path;

    private EntityRepository repository;

    protected EntityWrapper(@JsonProperty("properties") T properties) {
        this.properties = properties;
    }

    protected EntityWrapper(String path, T properties, String label,
            String... natures) {
        super(label, natures);
        this.properties = properties;
        this.path = path;
        add(new NavigationalRelationship(new JavaLink(this),
                Relationship.SELF));
        Method[] methods = this.getClass().getMethods();
        for (Method method : methods) {
            HttpMethod httpMethod = JavaAction.determineMethodNature(method);
            if (httpMethod != null) {
                switch (httpMethod) {
                case DELETE:
                    actions.put(method.getName(),
                            new JavaAction<Void>(this, method));
                    break;
                case POST:
                    actions.put(method.getName(),
                            new JavaAction<CreatedEntity>(this, method));
                    break;
                case PUT:
                    actions.put(method.getName(),
                            new JavaAction<UpdatedEntity>(this, method));
                case GET:
                    actions.put(method.getName(),
                            new JavaAction<EntityWrapper<?>>(this, method));
                default:
                }
            }
        }
        // getNatures().add(this.getClass().getSimpleName());

        // Label titleAnnotation = properties.getClass()
        // .getAnnotation(Label.class);
        // if (titleAnnotation != null) {
        // setTitle(titleAnnotation.value(), args);
        // }
    }

    public EntityWrapper(EntityWrapper<T> src) {
        super(src);
        this.properties = src.properties;
        this.path = src.path;
        this.actions = src.actions;
        this.entities = src.entities;
        this.navigationalRelationships = src.navigationalRelationships;
    }

    public void add(NavigationalRelationship navigationalRelationship) {
        navigationalRelationships.add(navigationalRelationship);
    }

    public Action<?> getAction(String identifier) {
        return actions.get(identifier);
    }

    @JsonProperty("actions")
    public ImmutableSet<Action<?>> getActions() {
        return ImmutableSet.copyOf(actions.values());
    }

    public CompletableFuture<Collection<EntityRelationship>> getEntities() {
        return getEntities(0);
    }

    @JsonProperty("entities")
    private Collection<EntityRelationship> getEntitiesAndJoin()
            throws IllegalAccessException, IllegalArgumentException,
            InvocationTargetException, URISyntaxException {
        return getEntities().join();
    }

    public void setRepository(EntityRepository repository) {
        this.repository = repository;
    }

    public CompletableFuture<Collection<EntityRelationship>> getEntities(
            int page) {
        if (entities != null) {
            return CompletableFuture.supplyAsync(() -> entities);
        }
        if (repository != null) {
            return repository.findChildren(this).thenApplyAsync(results -> {
                return results.skip(page * PAGE_SIZE).limit(PAGE_SIZE)
                        .collect(Collectors.toList());
            });
        }
        final List<EntityRelationship> rval = new ArrayList<>();
        return CompletableFuture.supplyAsync(() -> rval);
    }

    public Link getLink(String rel) {
        Optional<NavigationalRelationship> link = getLinks().stream()
                .filter(l -> l.hasNature(rel)).findAny();
        if (link.isPresent()) {
            return link.get().getLink();
        } else {
            return null;
        }
    }

    public ImmutableSet<NavigationalRelationship> getLinks() {
        return ImmutableSet.copyOf(navigationalRelationships);
    }

    public T getProperties() {
        return properties;
    }

    @Override
    public <K, L extends EntityWrapper<K>> L reload(Class<L> type) {
        return (L) this;
    }

    @Override
    public <K, L extends EntityWrapper<K>> L resolve(Class<L> type) {
        return (L) this;
    }

    @Override
    public <K, L extends EntityWrapper<K>> L resolve(
            ParameterizedTypeReference<L> type) {
        return (L) this;
    }

    public void setActions(Action<?>[] actions) {
        for (Action<?> action : actions) {
            this.actions.put(action.getIdentifier(), action);
        }

    }

    // @Autowired
    // public void setApplicationContext(ApplicationContext context) {
    // this.context = context;
    // AutowiredAnnotationBeanPostProcessor bpp = new
    // AutowiredAnnotationBeanPostProcessor();
    // bpp.setBeanFactory(context.getAutowireCapableBeanFactory());
    // bpp.processInjection(properties);
    // }

    public void setEntities(
            Collection<EntityRelationship> entityRelationships) {
        this.entities = entityRelationships;
    }

    @Override
    public LinkedEntity toLinkedEntity() {
        LinkedEntity linkedEntity = new LinkedEntity(getLink(Relationship.SELF),
                getLabel(), getNatures());
        return linkedEntity;
    }

    // public <L extends EntityWrapper<T>> L refresh() {
    // return (L) getLink(Relationship.SELF).resolve(this.getClass());
    // }

    @Override
    @JsonIgnore
    public URI getAddress() {
        return getLink(Relationship.SELF).getAddress();
    }

    @Override
    @JsonIgnore
    public String getId() {
        return this.path;
    }

}