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

org.mwg.plugin.AbstractNode Maven / Gradle / Ivy

There is a newer version: 10
Show newest version
package org.mwg.plugin;

import org.mwg.*;
import org.mwg.struct.LongLongArrayMap;
import org.mwg.struct.LongLongArrayMapCallBack;
import org.mwg.struct.Map;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Base implementation to develop NodeFactory plugins without overriding every methods
 */
public abstract class AbstractNode implements Node {

    private final long _world;

    private final long _time;

    private final long _id;

    private final Graph _graph;

    protected final Resolver _resolver;

    public final AtomicReference _previousResolveds;

    public AbstractNode(long p_world, long p_time, long p_id, Graph p_graph, long[] currentResolution) {
        this._world = p_world;
        this._time = p_time;
        this._id = p_id;
        this._graph = p_graph;
        this._resolver = p_graph.resolver();
        this._previousResolveds = new AtomicReference();
        this._previousResolveds.set(currentResolution);
    }

    /**
     * This method should be overridden to init the object
     */
    public void init() {
        //noop
    }

    protected NodeState unphasedState() {
        return this._resolver.resolveState(this, true);
    }

    protected NodeState phasedState() {
        return this._resolver.resolveState(this, false);
    }

    protected NodeState newState(long time) {
        return this._resolver.newState(this, _world, time);
    }

    @Override
    public Graph graph() {
        return _graph;
    }

    @Override
    public long world() {
        return this._world;
    }

    @Override
    public long time() {
        return this._time;
    }

    @Override
    public long id() {
        return this._id;
    }

    @Override
    public Object get(String propertyName) {
        NodeState resolved = this._resolver.resolveState(this, true);
        if (resolved != null) {
            return resolved.get(this._resolver.stringToHash(propertyName, false));
        }
        return null;
    }

    /**
     * @native ts
     * if (typeof propertyValue === 'string' || propertyValue instanceof String) {
     * this.setProperty(propertyName, org.mwg.Type.STRING, propertyValue);
     * } else if(typeof propertyValue === 'number' || propertyValue instanceof Number) {
     * if(propertyValue % 1 != 0) {
     * this.setProperty(propertyName, org.mwg.Type.DOUBLE, propertyValue);
     * } else {
     * this.setProperty(propertyName, org.mwg.Type.LONG, propertyValue);
     * }
     * } else if(typeof propertyValue === 'boolean' || propertyValue instanceof Boolean) {
     * this.setProperty(propertyName, org.mwg.Type.BOOL, propertyValue);
     * } else if (propertyValue instanceof Int32Array) {
     * this.setProperty(propertyName, org.mwg.Type.LONG_ARRAY, propertyValue);
     * } else if (propertyValue instanceof Float64Array) {
     * this.setProperty(propertyName, org.mwg.Type.DOUBLE_ARRAY, propertyValue);
     * } else {
     * throw new Error("Invalid property type: " + propertyValue + ", please use a Type listed in org.mwg.Type");
     * }
     */
    @Override
    public final void set(String propertyName, Object propertyValue) {
        if (propertyValue instanceof String) {
            setProperty(propertyName, Type.STRING, propertyValue);
        } else if (propertyValue instanceof Double) {
            setProperty(propertyName, Type.DOUBLE, propertyValue);
        } else if (propertyValue instanceof Long) {
            setProperty(propertyName, Type.LONG, propertyValue);
        } else if (propertyValue instanceof Float) {
            setProperty(propertyName, Type.DOUBLE, (double) ((Float) propertyValue));
        } else if (propertyValue instanceof Integer) {
            setProperty(propertyName, Type.INT, propertyValue);
        } else if (propertyValue instanceof Boolean) {
            setProperty(propertyName, Type.BOOL, propertyValue);
        } else if (propertyValue instanceof int[]) {
            setProperty(propertyName, Type.INT_ARRAY, propertyValue);
        } else if (propertyValue instanceof double[]) {
            setProperty(propertyName, Type.DOUBLE_ARRAY, propertyValue);
        } else if (propertyValue instanceof long[]) {
            setProperty(propertyName, Type.LONG_ARRAY, propertyValue);
        } else {
            throw new RuntimeException("Invalid property type: " + propertyValue + ", please use a Type listed in org.mwg.Type");
        }
    }

    @Override
    public void setProperty(String propertyName, byte propertyType, Object propertyValue) {
        NodeState preciseState = this._resolver.resolveState(this, false);
        if (preciseState != null) {
            preciseState.set(this._resolver.stringToHash(propertyName, true), propertyType, propertyValue);
        } else {
            throw new RuntimeException(Constants.CACHE_MISS_ERROR);
        }
    }

    @Override
    public Map getOrCreateMap(String propertyName, byte propertyType) {
        NodeState preciseState = this._resolver.resolveState(this, false);
        if (preciseState != null) {
            return (Map) preciseState.getOrCreate(this._resolver.stringToHash(propertyName, true), propertyType);
        } else {
            throw new RuntimeException(Constants.CACHE_MISS_ERROR);
        }
    }

    @Override
    public byte type(String propertyName) {
        NodeState resolved = this._resolver.resolveState(this, true);
        if (resolved != null) {
            return resolved.getType(this._resolver.stringToHash(propertyName, false));
        }
        return -1;
    }

    @Override
    public void removeProperty(String attributeName) {
        setProperty(attributeName, Type.INT, null);
    }

    @Override
    public void rel(String relationName, final Callback callback) {
        if (callback == null) {
            return;
        }
        final NodeState resolved = this._resolver.resolveState(this, true);
        if (resolved != null) {
            final long[] flatRefs = (long[]) resolved.get(this._resolver.stringToHash(relationName, false));
            if (flatRefs == null || flatRefs.length == 0) {
                callback.on(new Node[0]);
            } else {
                final Node[] result = new Node[flatRefs.length];
                final DeferCounter counter = _graph.newCounter(flatRefs.length);
                final int[] resultIndex = new int[1];
                for (int i = 0; i < flatRefs.length; i++) {
                    this._resolver.lookup(_world, _time, flatRefs[i], new Callback() {
                        @Override
                        public void on(Node kNode) {
                            if (kNode != null) {
                                result[resultIndex[0]] = kNode;
                                resultIndex[0]++;
                            }
                            counter.count();
                        }
                    });
                }
                counter.then(new Job() {
                    @Override
                    public void run() {
                        if (resultIndex[0] == result.length) {
                            callback.on(result);
                        } else {
                            Node[] toSend = new Node[resultIndex[0]];
                            System.arraycopy(result, 0, toSend, 0, toSend.length);
                            callback.on(toSend);
                        }
                    }
                });
            }
        }
    }

    @Override
    public void add(String relationName, Node relatedNode) {
        NodeState preciseState = this._resolver.resolveState(this, false);
        long relationKey = this._resolver.stringToHash(relationName, true);
        if (preciseState != null) {
            long[] previous = (long[]) preciseState.get(relationKey);
            if (previous == null) {
                previous = new long[1];
                previous[0] = relatedNode.id();
            } else {
                long[] incArray = new long[previous.length + 1];
                System.arraycopy(previous, 0, incArray, 0, previous.length);
                incArray[previous.length] = relatedNode.id();
                previous = incArray;
            }
            preciseState.set(relationKey, Type.RELATION, previous);
        } else {
            throw new RuntimeException(Constants.CACHE_MISS_ERROR);
        }
    }

    @Override
    public void remove(String relationName, Node relatedNode) {
        NodeState preciseState = this._resolver.resolveState(this, false);
        long relationKey = this._resolver.stringToHash(relationName, false);
        if (preciseState != null) {
            long[] previous = (long[]) preciseState.get(relationKey);
            if (previous != null) {
                int indexToRemove = -1;
                for (int i = 0; i < previous.length; i++) {
                    if (previous[i] == relatedNode.id()) {
                        indexToRemove = i;
                        break;
                    }
                }
                if (indexToRemove != -1) {
                    if ((previous.length - 1) == 0) {
                        preciseState.set(relationKey, Type.RELATION, null);
                    } else {
                        long[] newArray = new long[previous.length - 1];
                        System.arraycopy(previous, 0, newArray, 0, indexToRemove);
                        System.arraycopy(previous, indexToRemove + 1, newArray, indexToRemove, previous.length - indexToRemove - 1);
                        preciseState.set(relationKey, Type.RELATION, newArray);
                    }
                }
            }
        } else {
            throw new RuntimeException(Constants.CACHE_MISS_ERROR);
        }
    }

    @Override
    public void free() {
        this._resolver.freeNode(this);
    }

    @Override
    public long timeDephasing() {
        NodeState state = this._resolver.resolveState(this, true);
        if (state != null) {
            return (this._time - state.time());
        } else {
            throw new RuntimeException(Constants.CACHE_MISS_ERROR);
        }
    }

    @Override
    public void rephase() {
        this._resolver.resolveState(this, false);
    }

    @Override
    public void timepoints(long beginningOfSearch, long endOfSearch, Callback callback) {
        this._resolver.resolveTimepoints(this, beginningOfSearch, endOfSearch, callback);
    }

    @Override
    public  void jump(long targetTime, Callback callback) {
        _resolver.lookup(_world, targetTime, _id, callback);
    }

    @Override
    public void findByQuery(final Query query, final Callback callback) {
        NodeState currentNodeState = this._resolver.resolveState(this, false);
        if (currentNodeState == null) {
            throw new RuntimeException(Constants.CACHE_MISS_ERROR);
        }
        String indexName = query.indexName();
        if (indexName == null) {
            throw new RuntimeException("Please specify indexName in query before first use!");
        }
        long queryWorld = query.world();
        if (queryWorld == Constants.NULL_LONG) {
            queryWorld = world();
        }
        long queryTime = query.time();
        if (queryTime == Constants.NULL_LONG) {
            queryTime = time();
        }
        LongLongArrayMap indexMap = (LongLongArrayMap) currentNodeState.get(this._resolver.stringToHash(indexName, false));
        if (indexMap != null) {
            final AbstractNode selfPointer = this;
            final long[] foundId = indexMap.get(query.hash());
            if (foundId == null) {
                callback.on(new org.mwg.plugin.AbstractNode[0]);
                return;
            }
            final org.mwg.Node[] resolved = new org.mwg.plugin.AbstractNode[foundId.length];
            final DeferCounter waiter = _graph.newCounter(foundId.length);
            //TODO replace by a par lookup
            final AtomicInteger nextResolvedTabIndex = new AtomicInteger(0);
            for (int i = 0; i < foundId.length; i++) {
                selfPointer._resolver.lookup(queryWorld, queryTime, foundId[i], new Callback() {
                    @Override
                    public void on(org.mwg.Node resolvedNode) {
                        if (resolvedNode != null) {
                            resolved[nextResolvedTabIndex.getAndIncrement()] = resolvedNode;
                        }
                        waiter.count();
                    }
                });
            }
            waiter.then(new Job() {
                @Override
                public void run() {
                    //select
                    Node[] resultSet = new org.mwg.plugin.AbstractNode[nextResolvedTabIndex.get()];
                    int resultSetIndex = 0;
                    for (int i = 0; i < resultSet.length; i++) {
                        org.mwg.Node resolvedNode = resolved[i];
                        NodeState resolvedState = selfPointer._resolver.resolveState(resolvedNode, true);
                        boolean exact = true;
                        for (int j = 0; j < query.attributes().length; j++) {
                            Object obj = resolvedState.get(query.attributes()[j]);
                            if (query.values()[j] == null) {
                                if (obj != null) {
                                    exact = false;
                                    break;
                                }
                            } else {
                                if (obj == null) {
                                    exact = false;
                                    break;
                                } else {
                                    if (obj instanceof long[]) {
                                        if (query.values()[j] instanceof long[]) {
                                            if (!Constants.longArrayEquals((long[]) query.values()[j], (long[]) obj)) {
                                                exact = false;
                                                break;
                                            }
                                        } else {
                                            exact = false;
                                            break;
                                        }
                                    } else {
                                        if (!Constants.equals(query.values()[j].toString(), obj.toString())) {
                                            exact = false;
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                        if (exact) {
                            resultSet[resultSetIndex] = resolvedNode;
                            resultSetIndex++;
                        }
                    }
                    if (resultSet.length == resultSetIndex) {
                        callback.on(resultSet);
                    } else {
                        Node[] trimmedResultSet = new org.mwg.plugin.AbstractNode[resultSetIndex];
                        System.arraycopy(resultSet, 0, trimmedResultSet, 0, resultSetIndex);
                        callback.on(trimmedResultSet);
                    }
                }
            });
        } else {
            callback.on(new org.mwg.plugin.AbstractNode[0]);
        }
    }

    @Override
    public void find(String indexName, String query, Callback callback) {
        Query queryObj = _graph.newQuery();
        queryObj.setWorld(world());
        queryObj.setTime(time());
        queryObj.setIndexName(indexName);
        queryObj.parse(query);
        findByQuery(queryObj, callback);
    }

    @Override
    public void findAll(final String indexName, final Callback callback) {
        NodeState currentNodeState = this._resolver.resolveState(this, false);
        if (currentNodeState == null) {
            throw new RuntimeException(Constants.CACHE_MISS_ERROR);
        }
        LongLongArrayMap indexMap = (LongLongArrayMap) currentNodeState.get(this._resolver.stringToHash(indexName, false));
        if (indexMap != null) {
            final AbstractNode selfPointer = this;
            int mapSize = (int) indexMap.size();
            final Node[] resolved = new org.mwg.plugin.AbstractNode[mapSize];
            final DeferCounter waiter = _graph.newCounter(mapSize);
            //TODO replace by a parralel lookup
            final AtomicInteger loopInteger = new AtomicInteger(0);
            indexMap.each(new LongLongArrayMapCallBack() {
                @Override
                public void on(final long hash, final long nodeId) {
                    selfPointer._resolver.lookup(world(), time(), nodeId, new Callback() {
                        @Override
                        public void on(org.mwg.Node resolvedNode) {
                            resolved[loopInteger.getAndIncrement()] = resolvedNode;
                            waiter.count();
                        }
                    });
                }
            });
            waiter.then(new Job() {
                @Override
                public void run() {
                    if (loopInteger.get() == resolved.length) {
                        callback.on(resolved);
                    } else {
                        Node[] toSend = new org.mwg.plugin.AbstractNode[loopInteger.get()];
                        System.arraycopy(resolved, 0, toSend, 0, toSend.length);
                        callback.on(toSend);
                    }
                }
            });
        } else {
            callback.on(new org.mwg.plugin.AbstractNode[0]);
        }
    }

    @Override
    public void index(String indexName, org.mwg.Node nodeToIndex, String flatKeyAttributes, Callback callback) {
        final String[] keyAttributes = flatKeyAttributes.split(Constants.QUERY_SEP + "");
        NodeState currentNodeState = this._resolver.resolveState(this, true);
        if (currentNodeState == null) {
            throw new RuntimeException(Constants.CACHE_MISS_ERROR);
        }
        LongLongArrayMap indexMap = (LongLongArrayMap) currentNodeState.getOrCreate(this._resolver.stringToHash(indexName, true), Type.LONG_TO_LONG_ARRAY_MAP);
        Query flatQuery = _graph.newQuery();
        NodeState toIndexNodeState = this._resolver.resolveState(nodeToIndex, true);
        for (int i = 0; i < keyAttributes.length; i++) {
            String attKey = keyAttributes[i];
            Object attValue = toIndexNodeState.getFromKey(attKey);
            if (attValue != null) {
                flatQuery.add(keyAttributes[i], attValue);
            } else {
                flatQuery.add(keyAttributes[i], null);
            }
        }
        //TODO AUTOMATIC UPDATE
        indexMap.put(flatQuery.hash(), nodeToIndex.id());
        if (Constants.isDefined(callback)) {
            callback.on(true);
        }
    }

    @Override
    public void unindex(String indexName, org.mwg.Node nodeToIndex, String flatKeyAttributes, Callback callback) {
        final String[] keyAttributes = flatKeyAttributes.split(Constants.QUERY_SEP + "");
        NodeState currentNodeState = this._resolver.resolveState(this, true);
        if (currentNodeState == null) {
            throw new RuntimeException(Constants.CACHE_MISS_ERROR);
        }
        LongLongArrayMap indexMap = (LongLongArrayMap) currentNodeState.get(this._resolver.stringToHash(indexName, false));
        if (indexMap != null) {
            Query flatQuery = _graph.newQuery();
            NodeState toIndexNodeState = this._resolver.resolveState(nodeToIndex, true);
            for (int i = 0; i < keyAttributes.length; i++) {
                String attKey = keyAttributes[i];
                Object attValue = toIndexNodeState.getFromKey(attKey);
                if (attValue != null) {
                    flatQuery.add(attKey, attValue.toString());
                } else {
                    flatQuery.add(attKey, null);
                }
            }
            //TODO AUTOMATIC UPDATE
            indexMap.remove(flatQuery.hash(), nodeToIndex.id());
        }
        if (Constants.isDefined(callback)) {
            callback.on(true);
        }
    }

    /**
     * @native ts
     * return isNaN(toTest);
     */
    private boolean isNaN(double toTest) {
        return Double.NaN == toTest;
    }

    @Override
    public String toString() {
        final StringBuilder builder = new StringBuilder();
        builder.append("{\"world\":");
        builder.append(world());
        builder.append(",\"time\":");
        builder.append(time());
        builder.append(",\"id\":");
        builder.append(id());
        NodeState state = this._resolver.resolveState(this, true);
        if (state != null) {
            state.each(new NodeStateCallback() {
                @Override
                public void on(long attributeKey, int elemType, Object elem) {
                    if (elem != null) {
                        switch (elemType) {
                            /** Primitive types */
                            case Type.BOOL: {
                                builder.append(",\"");
                                builder.append(_resolver.hashToString(attributeKey));
                                builder.append("\":");
                                if ((Boolean) elem) {
                                    builder.append("0");
                                } else {
                                    builder.append("1");
                                }
                                break;
                            }
                            case Type.STRING: {
                                builder.append(",\"");
                                builder.append(_resolver.hashToString(attributeKey));
                                builder.append("\":");
                                builder.append("\"");
                                builder.append(elem);
                                builder.append("\"");
                                break;
                            }
                            case Type.LONG: {
                                builder.append(",\"");
                                builder.append(_resolver.hashToString(attributeKey));
                                builder.append("\":");
                                builder.append(elem);
                                break;
                            }
                            case Type.INT: {
                                builder.append(",\"");
                                builder.append(_resolver.hashToString(attributeKey));
                                builder.append("\":");
                                builder.append(elem);
                                break;
                            }
                            case Type.DOUBLE: {
                                if (!isNaN((Double) elem)) {
                                    builder.append(",\"");
                                    builder.append(_resolver.hashToString(attributeKey));
                                    builder.append("\":");
                                    builder.append(elem);
                                }
                                break;
                            }
                            /** Array types */
                            case Type.DOUBLE_ARRAY: {
                                builder.append(",\"");
                                builder.append(_resolver.hashToString(attributeKey));
                                builder.append("\":");
                                builder.append("[");
                                double[] castedArr = (double[]) elem;
                                for (int j = 0; j < castedArr.length; j++) {
                                    if (j != 0) {
                                        builder.append(",");
                                    }
                                    builder.append(castedArr[j]);
                                }
                                builder.append("]");
                                break;
                            }
                            case Type.RELATION:
                            case Type.LONG_ARRAY: {
                                builder.append(",\"");
                                builder.append(_resolver.hashToString(attributeKey));
                                builder.append("\":");
                                builder.append("[");
                                long[] castedArr2 = (long[]) elem;
                                for (int j = 0; j < castedArr2.length; j++) {
                                    if (j != 0) {
                                        builder.append(",");
                                    }
                                    builder.append(castedArr2[j]);
                                }
                                builder.append("]");
                                break;
                            }
                            case Type.INT_ARRAY: {
                                builder.append(",\"");
                                builder.append(_resolver.hashToString(attributeKey));
                                builder.append("\":");
                                builder.append("[");
                                int[] castedArr3 = (int[]) elem;
                                for (int j = 0; j < castedArr3.length; j++) {
                                    if (j != 0) {
                                        builder.append(",");
                                    }
                                    builder.append(castedArr3[j]);
                                }
                                builder.append("]");
                                break;
                            }
                        }
                    }
                }
            });
            builder.append("}");
        }
        return builder.toString();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy