dollar.api.types.DollarMap.java Source code

Java tutorial

Introduction

Here is the source code for dollar.api.types.DollarMap.java

Source

/*
 *    Copyright (c) 2014-2017 Neil Ellis
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *          http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package dollar.api.types;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import dollar.api.DollarStatic;
import dollar.api.Pipeable;
import dollar.api.Type;
import dollar.api.Value;
import dollar.api.exceptions.DollarFailureException;
import dollar.api.json.ImmutableJsonObject;
import dollar.api.json.JsonObject;
import org.jetbrains.annotations.NotNull;
import org.yaml.snakeyaml.Yaml;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static dollar.api.types.DollarFactory.wrap;

public class DollarMap extends AbstractDollar {

    /**
     * Publicly accessible object containing the current state as a JsonObject, if you're working in Vert.x primarily
     * with the JsonObject type you will likely end all chained expressions with '.$'
     * <p>
     * For example: {@code eb.send("api.validate", $("key", key).$("params", request.params()).$) }
     */
    protected final @NotNull LinkedHashMap<Value, Value> map;

    /**
     * Create a $ object from a variety of different objects. At present the following are supported:<br/> <ul>
     * <li>JsonObject</li> <li>MultiMap</li> <li>Message</li> </ul>
     * <p>
     * Any other object types will be converted to a string using .toString() and will then be parsed as JSON.
     *
     * @param o the object of unknown type to be converted to a JsonObject and then wrapped by the $ class.
     */
    DollarMap(@NotNull JsonObject o) {
        super();
        map = mapToVarMap(o.toMap());
    }

    public DollarMap(@NotNull Map<?, ?> o) {
        super();
        map = mapToVarMap(o);
    }

    public DollarMap(@NotNull LinkedHashMap<Value, Value> o) {
        super();
        map = deepClone(o);
    }

    public DollarMap(@NotNull ImmutableMap<Value, Value> o) {
        super();
        map = mapToVarMap(o);
    }

    public DollarMap(@NotNull ImmutableJsonObject immutableJsonObject) {
        super();
        map = mapToVarMap(immutableJsonObject.toMap());
    }

    public DollarMap() {
        super();
        map = new LinkedHashMap<>();
    }

    @NotNull
    @Override
    public Value $abs() {
        return this;
    }

    @NotNull
    @Override
    public Value $append(@NotNull Value value) {
        final LinkedHashMap<Value, Value> newMap = new LinkedHashMap<>(toVarMap());
        newMap.put(value.$pairKey(), value.$pairValue());
        return DollarFactory.fromValue(newMap);
    }

    @NotNull
    @Override
    public Value $as(@NotNull Type type) {
        if (type.is(Type._MAP)) {
            return this;
        } else if (type.is(Type._LIST)) {
            return DollarStatic.$(toVarList());
        } else if (type.is(Type._BOOLEAN)) {
            return DollarStatic.$(!map.isEmpty());
        } else if (type.is(Type._STRING)) {
            return DollarFactory.fromStringValue(toHumanString());
        } else if (type.is(Type._VOID)) {
            return DollarStatic.$void();
        } else {
            throw new DollarFailureException(ErrorType.INVALID_CAST);
        }
    }

    @NotNull
    @Override
    public Value $containsKey(@NotNull Value value) {
        return DollarStatic.$(map.containsKey(value));
    }

    @Override
    @NotNull
    public Value $containsValue(@NotNull Value value) {
        return DollarStatic.$(map.containsValue(value));
    }

    @NotNull
    @Override
    public Value $divide(@NotNull Value rhs) {
        throw new DollarFailureException(ErrorType.INVALID_MAP_OPERATION);
    }

    @NotNull
    @Override
    public Value $get(@NotNull Value key) {
        if (key.integer()) {
            final Value mapKey = (Value) map.keySet().toArray()[key.toInteger()];
            return DollarStatic.$(mapKey, map.get(mapKey));
        } else {
            return DollarFactory.fromValue(map.get(key));
        }

    }

    @NotNull
    @Override
    public Value $has(@NotNull Value key) {
        return DollarStatic.$(map.containsKey(key));
    }

    @NotNull
    @Override
    public Value $insert(@NotNull Value value, int position) {
        final LinkedHashMap<Value, Value> newMap = new LinkedHashMap<>();
        int count = 0;
        for (Map.Entry<Value, Value> entry : newMap.entrySet()) {
            if (count == position) {
                newMap.put(value.$pairKey(), value.$pairValue());
            }
            newMap.put(entry.getKey(), entry.getValue());

        }
        newMap.putAll(toVarMap());
        return DollarFactory.fromValue(newMap);
    }

    @NotNull
    @Override
    public Value $listen(@NotNull Pipeable pipe) {
        String key = UUID.randomUUID().toString();
        $listen(pipe, key);
        return DollarStatic.$(key);
    }

    @NotNull
    @Override
    public Value $listen(@NotNull Pipeable pipe, @NotNull String key) {
        for (Value v : map.values()) {
            //Join the children to this, so if the children change
            //listeners to this get the latest value of this.
            v.$listen(i -> this, key);
        }
        return DollarStatic.$(key);
    }

    @NotNull
    @Override
    public Value $map() {
        return this;
    }

    @NotNull
    @Override
    public Value $mimeType() {
        return DollarStatic.$("application/json");
    }

    @NotNull
    @Override
    public Value $minus(@NotNull Value rhs) {
        Value rhsFix = rhs.$fixDeep();
        if (rhsFix.map()) {
            LinkedHashMap<Value, Value> copy = copyMap();
            for (Map.Entry<Value, Value> entry : rhsFix.toVarMap().entrySet()) {
                copy.remove(entry.getKey());
            }
            return wrap(new DollarMap(copy));
        } else {
            LinkedHashMap<Value, Value> copy = copyMap();
            copy.remove(rhsFix);
            return wrap(new DollarMap(copy));
        }
    }

    @NotNull
    @Override
    public Value $modulus(@NotNull Value rhs) {
        throw new DollarFailureException(ErrorType.INVALID_MAP_OPERATION);
    }

    @NotNull
    @Override
    public Value $multiply(@NotNull Value v) {
        throw new DollarFailureException(ErrorType.INVALID_MAP_OPERATION);
    }

    @NotNull
    @Override
    public Value $negate() {
        LinkedHashMap<Value, Value> result = new LinkedHashMap<>();
        final ArrayList<Map.Entry<Value, Value>> entries = new ArrayList<>(map.entrySet());
        Collections.reverse(entries);
        for (Map.Entry<Value, Value> entry : entries) {
            result.put(entry.getKey(), entry.getValue());
        }
        return DollarFactory.fromValue(result);
    }

    @NotNull
    @Override
    public Value $plus(@NotNull Value rhs) {
        Value rhsFix = rhs.$fixDeep();
        if (rhsFix.map()) {
            LinkedHashMap<Value, Value> copy = copyMap();
            copy.putAll(rhsFix.toVarMap());
            return wrap(new DollarMap(copy));
        } else if (rhsFix.string()) {
            return DollarFactory.fromValue(toHumanString() + rhsFix.toHumanString());
        } else {
            LinkedHashMap<Value, Value> copy = copyMap();
            copy.put(DollarFactory.fromValue("_" + copy.size()), rhsFix);
            return wrap(new DollarMap(copy));
        }
    }

    @NotNull
    @Override
    public Value $prepend(@NotNull Value value) {
        final LinkedHashMap<Value, Value> newMap = new LinkedHashMap<>();
        newMap.put(value.$pairKey(), value.$pairValue());
        newMap.putAll(toVarMap());
        return DollarFactory.fromValue(newMap);
    }

    @NotNull
    @Override
    public Value $remove(@NotNull Value key) {
        final LinkedHashMap<Value, Value> newMap = new LinkedHashMap<>(map);
        newMap.remove(key);
        return DollarFactory.fromValue(newMap);
    }

    @NotNull
    @Override
    public Value $removeByKey(@NotNull String key) {
        final LinkedHashMap<Value, Value> newMap = new LinkedHashMap<>(map);
        newMap.remove(DollarStatic.$(key));
        return DollarFactory.fromValue(newMap);
    }

    @NotNull
    @Override
    public Value $set(@NotNull Value key, @NotNull Object value) {
        LinkedHashMap<Value, Value> copyMap = copyMap();
        copyMap.put(key, DollarFactory.fromValue(value));
        return wrap(new DollarMap(copyMap));
    }

    @NotNull
    @Override
    public Value $size() {
        return DollarStatic.$(toJavaMap().size());
    }

    @NotNull
    @Override
    public Type $type() {
        return new Type(Type._MAP, constraintLabel());
    }

    @Override
    public boolean collection() {
        return true;
    }

    @Override
    public boolean is(@NotNull Type... types) {
        for (Type type : types) {
            if (type.is(Type._MAP)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean isBoolean() {
        return false;
    }

    @Override
    public boolean isFalse() {
        return false;
    }

    @Override
    public boolean isTrue() {
        return false;
    }

    @Override
    public boolean isVoid() {
        return false;
    }

    @Override
    public boolean neitherTrueNorFalse() {
        return true;
    }

    @NotNull
    @Override
    public int size() {
        return map.size();
    }

    @NotNull
    @Override
    public Stream<Value> stream(boolean parallel) {
        return split().values().stream();
    }

    @NotNull
    @Override
    public String toDollarScript() {
        StringBuilder builder = new StringBuilder("{");
        for (Map.Entry<Value, Value> entry : map.entrySet()) {
            builder.append(entry.getKey().toDollarScript()).append(" : ").append(entry.getValue().toDollarScript())
                    .append(",\n");
        }
        builder.append("}");
        return builder.toString();
    }

    @NotNull
    @Override
    public String toHumanString() {
        return toJsonObject().toString();
    }

    @NotNull
    @Override
    public int toInteger() {
        @NotNull
        Value result;
        throw new DollarFailureException(ErrorType.INVALID_MAP_OPERATION);
    }

    @NotNull
    @Override
    public <K extends Comparable<K>, V> ImmutableMap<K, V> toJavaMap() {
        Map<K, V> varMap = varMapToMap();
        return ImmutableMap.copyOf(varMap);
    }

    @NotNull
    @Override
    public <R> R toJavaObject() {
        return (R) varMapToMap();
    }

    @NotNull
    @Override
    public ImmutableList<Object> toList() {
        final ArrayList<Object> entries = new ArrayList<>();
        for (Map.Entry<Value, Value> entry : map.entrySet()) {
            entries.add(entry.getValue().toJavaObject());
        }
        return ImmutableList.copyOf(entries);
    }

    @NotNull
    @Override
    public Number toNumber() {
        return 0;
    }

    @Override
    public ImmutableList<String> toStrings() {
        List<String> values = new ArrayList<>();
        ImmutableMap<String, Object> map = toJavaMap();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            assert entry.getKey() instanceof String;
            values.add(entry.getKey());
            values.add(entry.getValue().toString());
        }
        return ImmutableList.copyOf(values);
    }

    @NotNull
    @Override
    public ImmutableList<Value> toVarList() {
        final List<Value> entries = map.entrySet().stream()
                .map(entry -> DollarStatic.$(entry.getKey(), entry.getValue())).collect(Collectors.toList());
        return ImmutableList.copyOf(entries);
    }

    @NotNull
    @Override
    public ImmutableMap<Value, Value> toVarMap() {

        LinkedHashMap<Value, Value> result = new LinkedHashMap<>();
        for (Map.Entry<Value, Value> entry : map.entrySet()) {
            result.put(entry.getKey(), entry.getValue().$fix(false));
        }
        return ImmutableMap.copyOf(result);
    }

    @NotNull
    @Override
    public String toYaml() {
        Yaml yaml = new Yaml();
        return yaml.dump(map);
    }

    @Override
    public boolean truthy() {
        return !map.isEmpty();
    }

    @NotNull
    @Override
    public Value $copy() {
        return wrap(new DollarMap(map));
    }

    @NotNull
    @Override
    public Value $fix(int depth, boolean parallel) {
        if (depth <= 1) {
            return this;
        } else {
            LinkedHashMap<Value, Value> result = new LinkedHashMap<>();
            if (parallel) {
                for (Map.Entry<Value, Value> entry : map.entrySet()) {
                    result.put(entry.getKey(),
                            DollarStatic.$fork(source(), entry.getValue(), in -> in.$fix(depth - 1, true)));
                }
            } else {
                for (Map.Entry<Value, Value> entry : map.entrySet()) {
                    result.put(entry.getKey(), entry.getValue().$fix(depth - 1, false));
                }
            }
            return new DollarMap(result);
        }
    }

    @Override
    public Value $notify(NotificationType type, Value newValue) {
        map.values().forEach(member -> member.$notify(NotificationType.UNARY_VALUE_CHANGE, newValue));
        return this;
    }

    @Override
    public boolean map() {
        return true;
    }

    @Override
    public boolean pair() {
        return map.size() == 1;
    }

    @Override
    public int compareTo(@NotNull Value o) {
        if (pair() && o.pair()) {
            return Comparator.<Value>naturalOrder().<Value>compare($pairKey(), o.$pairKey());
        } else {
            throw new DollarFailureException(ErrorType.INVALID_MAP_OPERATION, "Cannot compare maps");
        }
    }

    @NotNull
    private LinkedHashMap<Value, Value> copyMap() {
        return deepClone(map);
    }

    @NotNull
    private LinkedHashMap<Value, Value> deepClone(@NotNull LinkedHashMap<Value, Value> o) {
        LinkedHashMap<Value, Value> result = new LinkedHashMap<>();
        for (Map.Entry<Value, Value> entry : o.entrySet()) {
            result.put(entry.getKey(), entry.getValue());
        }
        return result;
    }

    @NotNull
    private LinkedHashMap<Value, Value> mapToVarMap(@NotNull Map<?, ?> stringObjectMap) {
        LinkedHashMap<Value, Value> result = new LinkedHashMap<>();
        for (Map.Entry<?, ?> entry : stringObjectMap.entrySet()) {
            result.put(DollarFactory.fromValue(entry.getKey()), DollarFactory.fromValue(entry.getValue()));
        }
        return result;
    }

    @NotNull
    Map<Value, Value> split() {
        return copyMap();
    }

    @NotNull
    private <K extends Comparable<K>, V> Map<K, V> varMapToMap() {
        Map<K, V> result = new LinkedHashMap<>();
        for (Map.Entry<Value, Value> entry : toVarMap().entrySet()) {
            result.put(entry.getKey().toJavaObject(), entry.getValue().toJavaObject());
        }
        return result;
    }

}