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

org.hcjf.cloud.impl.objects.DistributedTree Maven / Gradle / Ivy

package org.hcjf.cloud.impl.objects;

import java.util.*;

/**
 * @author javaito
 */
public class DistributedTree implements DistributedObject {

    private final Object key;
    private final Map branches;
    private final Long lastUpdate;

    public DistributedTree(Object key) {
        this.key = key;
        this.branches = new HashMap<>();
        this.lastUpdate = System.currentTimeMillis();
    }

    public final List filter(Class... predicate) {
        List result = new ArrayList<>();
        List path = new ArrayList<>();
        return filter(path, result, predicate);
    }

    public final List filter(List path, List result, Class... predicate) {
        DistributedObject value;
        for(Object key : branches.keySet()) {
            value = branches.get(key);
            path.add(key);
            if(value instanceof DistributedTree) {
                ((DistributedTree)value).filter(path, result, predicate);
            } else if(evaluatePredicate(value.getClass(), predicate)) {
                result.add(new Entry(path.toArray(), value));
            }
            path.remove(path.size() - 1);
        }
        return result;
    }

    private boolean evaluatePredicate(Class currentClass, Class... predicate) {
        boolean result = false;
        for(Class predicateClass : predicate) {
            result |= predicateClass.isAssignableFrom(currentClass);
            if(!result) {
                break;
            }
        }
        return result;
    }

    @Override
    public final Object getKey() {
        return key;
    }

    @Override
    public final Long getLastUpdate() {
        return lastUpdate;
    }

    public final int size() {
        return branches.size();
    }

    public final boolean isEmpty() {
        return branches.isEmpty();
    }

    public final boolean containsKey(Object key) {
        return branches.containsKey(key);
    }

    public final Set keySet() {
        return branches.keySet();
    }

    public final synchronized LocalLeaf addLocalObject(Object object, List nodes, List serviceEndPoints, Long timestamp, Object... path) {
        Objects.requireNonNull(object, "Null distributed object");
        LocalLeaf result;
        createPath(0, path.length - 1, path);
        Object instance = getInstance(0, path.length - 1, path);
        if(instance instanceof DistributedTree) {
            Object key = path[path.length-1];
            result = new LocalLeaf(key);
            result.setLastUpdate(lastUpdate);
            result.setInstance(object);
            result.getNodes().addAll(nodes);
            result.getServiceEndPoints().addAll(serviceEndPoints);

            DistributedLeaf leaf = (DistributedLeaf) branches.get(key);
            if(leaf != null) {
                if(leaf.getLastUpdate() < timestamp) {
                    if(leaf instanceof LocalLeaf) {
                        result = (LocalLeaf) leaf;
                        result.setLastUpdate(timestamp);
                        result.getNodes().addAll(nodes);
                        result.getServiceEndPoints().addAll(serviceEndPoints);
                        result.setInstance(object);
                    } else {
                        ((DistributedTree) instance).branches.put(key, result);
                    }
                } else {
                    if(leaf instanceof LocalLeaf) {
                        result = (LocalLeaf) branches.get(key);
                        result.getNodes().addAll(nodes);
                        result.getServiceEndPoints().addAll(serviceEndPoints);
                    }
                }
            } else {
                ((DistributedTree) instance).branches.put(key, result);
            }
        } else {
            throw new IllegalArgumentException();
        }
        return result;
    }

    public final synchronized RemoteLeaf addRemoteObject(Object object, List nodes, List serviceEndPoints, Long timestamp, Object... path) {
        RemoteLeaf result;
        createPath(0, path.length - 1, path);
        Object instance = getInstance(0, path.length - 1, path);
        if(instance instanceof DistributedTree) {
            Object key = path[path.length-1];
            result = new RemoteLeaf(key);
            result.setLastUpdate(lastUpdate);
            result.setInstance(object);
            result.getNodes().addAll(nodes);
            result.getServiceEndPoints().addAll(serviceEndPoints);

            DistributedLeaf leaf = (DistributedLeaf) branches.get(key);
            if(leaf != null) {
                if(leaf.getLastUpdate() < timestamp) {
                    if(leaf instanceof RemoteLeaf) {
                        result = (RemoteLeaf) leaf;
                        result.setLastUpdate(timestamp);
                        result.getNodes().addAll(nodes);
                        result.getServiceEndPoints().addAll(serviceEndPoints);
                        result.setInstance(object);
                    } else {
                        ((DistributedTree) instance).branches.put(key, result);
                    }
                } else {
                    if(leaf instanceof RemoteLeaf) {
                        result = (RemoteLeaf) branches.get(key);
                        result.getNodes().addAll(nodes);
                        result.getServiceEndPoints().addAll(serviceEndPoints);
                    }
                }
            } else {
                ((DistributedTree) instance).branches.put(key, result);
            }
        } else {
            throw new IllegalArgumentException();
        }
        return result;
    }

    public final synchronized DistributedObject remove(Object... path) {
        return remove(0, path);
    }

    private DistributedObject remove(int index, Object... path) {
        DistributedObject result = null;
        if(index + 1 == path.length) {
            result = branches.remove(path[index]);
        } else {
            DistributedObject distributedObject = branches.get(path[index]);
            if(distributedObject != null && distributedObject instanceof DistributedTree) {
                result = ((DistributedTree)distributedObject).remove(index + 1, path);
            }
        }
        return result;
    }

    public final synchronized void clear(Object... path) {
        clear(0, path);
    }

    private void clear(int index, Object... path) {
        DistributedObject distributedObject = branches.get(index++);
        if(distributedObject instanceof DistributedTree) {
            if(index == path.length) {
                ((DistributedTree) distributedObject).branches.clear();
            } else {
                ((DistributedTree) distributedObject).clear(index, path);
            }
        }
    }

    @Override
    public Object getInstance() {
        return this;
    }

    public Object getInstance(Object... path) {
        return getInstance(0, path.length, path);
    }

    private Object getInstance(int index, int length, Object... path) {
        Object result = null;
        DistributedObject distributedObject = branches.get(path[index++]);
        if(distributedObject != null) {
            if (index == length) {
                result = distributedObject.getInstance();
            } else if(distributedObject instanceof DistributedTree) {
                result = ((DistributedTree)distributedObject).getInstance(index, length, path);
            }
        }
        return result;
    }

    public synchronized boolean createPath(Object... path) {
        return createPath(0, path.length, path);
    }

    private boolean createPath(int index, int length, Object... path) {
        boolean result = false;
        Object key = path[index++];
        if(!branches.containsKey(key)) {
            branches.put(key, new DistributedTree(key));
            result = true;
        }

        if(index < length) {
            result = result & ((DistributedTree)branches.get(key)).createPath(index, length, path);
        }

        return result;
    }

    public static class Entry {

        private final Object[] path;
        private final DistributedObject value;

        public Entry(Object[] path, DistributedObject value) {
            this.path = path;
            this.value = value;
        }

        public Object[] getPath() {
            return path;
        }

        public DistributedObject getValue() {
            return value;
        }
    }
}