All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.yawp.repository.IdRef Maven / Gradle / Ivy

There is a newer version: 2.08alpha
Show newest version
package io.yawp.repository;

import io.yawp.commons.http.HttpVerb;
import io.yawp.repository.actions.ActionKey;
import io.yawp.repository.models.ObjectModel;
import io.yawp.repository.query.QueryBuilder;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import static io.yawp.repository.Yawp.yawp;

public class IdRef implements Comparable>, Serializable {

    private static final long serialVersionUID = 2203539386465263956L;

    private Repository r;

    private Class clazz;

    private ObjectModel model;

    private Long id;

    private String name;

    private IdRef parentId;

    protected IdRef(Repository r, Class clazz, Long id) {
        this.r = r;
        this.clazz = clazz;
        this.id = id;
        this.model = new ObjectModel(clazz);
    }

    public IdRef(Repository r, Class clazz, String name) {
        this.r = r;
        this.clazz = clazz;
        this.name = name;
        this.model = new ObjectModel(clazz);
    }

    public void setParentId(IdRef parentId) {
        this.parentId = parentId;
    }

    public T fetch() {
        return fetch(clazz);
    }

    public  TT fetch(Class childClazz) {
        return r.query(childClazz).fetch(this);
    }

    public  TT child(Class childClazz) {
        QueryBuilder q = r.query(childClazz).from(this);
        return q.only();
    }

    public Long asLong() {
        return id;
    }

    public String asString() {
        return name;
    }

    public Repository getRepository() {
        return r;
    }

    public Class getClazz() {
        return clazz;
    }

    public Class getParentClazz() {
        return model.getParentClazz();
    }

    public ObjectModel getModel() {
        return new ObjectModel(clazz);
    }

    @SuppressWarnings("unchecked")
    public  IdRef getParentId() {
        return (IdRef) parentId;
    }

    @SuppressWarnings("unchecked")
    public  IdRef getAncestorId(int ancestor) {
        IdRef ancestorId = this;
        for (int i = 0; i <= ancestor; i++) {
            ancestorId = ancestorId.getParentId();
        }
        return (IdRef) ancestorId;
    }

    public  IdRef createChildId(Class clazz, Long id) {
        IdRef idRef = new IdRef(r, clazz, id);
        idRef.setParentId(this);
        return idRef;
    }

    public  IdRef createChildId(Class clazz, String name) {
        IdRef idRef = new IdRef(r, clazz, name);
        idRef.setParentId(this);
        return idRef;
    }

    public static  IdRef create(Repository r, Class clazz, Long id) {
        return new IdRef(r, clazz, id);
    }

    public static  IdRef create(Repository r, Class clazz, String name) {
        return new IdRef(r, clazz, name);
    }

    public static  List> create(Repository r, Class clazz, Long... ids) {
        List> idRefs = new ArrayList>();
        for (int i = 0; i < ids.length; i++) {
            idRefs.add(create(r, clazz, ids[i]));
        }
        return idRefs;
    }

    @SuppressWarnings("unchecked")
    public static  IdRef parse(Repository r, HttpVerb verb, String path) {
        if (StringUtils.isBlank(path)) {
            return null;
        }

        String[] parts = path.substring(1).split("/");

        if (parts.length < 2) {
            return null;
        }

        IdRef lastIdRef = null;

        for (int i = 0; i < parts.length; i += 2) {
            if (isActionOrCollection(r, verb, parts, i)) {
                break;
            }

            String endpointPath = "/" + parts[i];

            IdRef currentIdRef = null;

            if (!isString(parts[i + 1])) {
                Long asLong = Long.valueOf(parts[i + 1]);
                currentIdRef = (IdRef) create(r, getIdRefClazz(r, endpointPath), asLong);
            } else {
                String asString = parts[i + 1];
                currentIdRef = (IdRef) create(r, getIdRefClazz(r, endpointPath), asString);
            }

            currentIdRef.setParentId(lastIdRef);
            lastIdRef = currentIdRef;

            validateParentId(currentIdRef, path);
        }

        return lastIdRef;
    }

    public static IdRef parse(Repository r, String id) {
        return parse(r, HttpVerb.GET, id);
    }

    public static  IdRef parse(Class clazz, Repository r, String idString) {
        return parse(r, HttpVerb.GET, idString);
    }

    public static  List> parse(Class clazz, Repository r, List idsString) {
        ArrayList> ids = new ArrayList>();
        for (String idString : idsString) {
            ids.add(parse(clazz, r, idString));
        }
        return ids;
    }

    private static void validateParentId(IdRef id, String path) {
        Class parentClazz = id.getParentClazz();
        if (parentClazz == null) {
            return;
        }

        if (id.getParentId() == null) {
            throw new RuntimeException("Invalid parent structure for id: " + path);
        }

        if (!parentClazz.equals(id.getParentId().getClazz())) {
            throw new RuntimeException("Invalid parent structure for id: " + path);
        }
    }

    private static Class getIdRefClazz(Repository r, String endpointPath) {
        return r.getFeatures().getByPath(endpointPath).getClazz();
    }

    private static boolean isActionOrCollection(Repository r, HttpVerb verb, String[] parts, int i) {
        if (i < parts.length - 2) {
            return false;
        }

        if (i == parts.length - 1) {
            return true;
        }

        if (!isString(parts[parts.length - 1])) {
            return false;
        }

        String endpointPath = "/" + parts[parts.length - 2];
        String possibleAction = parts[parts.length - 1];

        ActionKey actionKey = new ActionKey(verb, possibleAction, true);
        return r.getFeatures().hasCustomAction(endpointPath, actionKey);
    }

    private static boolean isString(String s) {
        try {
            Long.valueOf(s);
            return false;
        } catch (NumberFormatException e) {
            return true;
        }
    }

    public void delete() {
        r.destroy(this);
    }

    public Object getSimpleValue() {
        if (id != null) {
            return id;
        }
        return name;
    }

    public String getName() {
        return name;
    }

    public Long getId() {
        return id;
    }

    public boolean isShuffled() {
        return model.isIdShuffled();
    }

    public boolean isAncestorId(IdRef childId) {
        IdRef currentId = childId;
        while (currentId.getParentId() != null) {
            if (currentId.getParentId().getClazz().equals(this.getClazz())) {
                return this.equals(currentId.getParentId());
            }
            currentId = currentId.getParentId();
        }
        return false;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((clazz == null) ? 0 : clazz.hashCode());
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + ((parentId == null) ? 0 : parentId.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        IdRef other = (IdRef) obj;
        if (clazz == null) {
            if (other.clazz != null)
                return false;
        } else if (!clazz.equals(other.clazz))
            return false;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (parentId == null) {
            if (other.parentId != null)
                return false;
        } else if (!parentId.equals(other.parentId))
            return false;
        return true;
    }

    @Override
    public int compareTo(IdRef o) {
        if (id != null && o.id != null) {
            return id.compareTo(o.id);
        }
        return idOrNameAsString().compareTo(o.idOrNameAsString());
    }

    private String idOrNameAsString() {
        return id != null ? String.valueOf(id) : name;
    }

    @Override
    public String toString() {
        return getUri();
    }

    public String getUri() {
        StringBuilder sb = new StringBuilder();
        if (parentId != null) {
            sb.append(parentId.toString());
        }
        sb.append(r.getFeatures().getByClazz(clazz).getEndpointPath());
        sb.append("/");
        sb.append(id != null ? id : name);
        return sb.toString();
    }

    // Serializable
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeObject(getUri());
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        String uri = (String) in.readObject();

        IdRef idRef = (IdRef) parse(yawp(), uri);
        this.r = idRef.r;
        this.clazz = idRef.clazz;
        this.model = idRef.model;
        this.id = idRef.id;
        this.name = idRef.name;
        this.parentId = idRef.parentId;
    }

    private void readObjectNoData() throws ObjectStreamException {

    }
}