dollar.api.types.DollarFactory.java Source code

Java tutorial

Introduction

Here is the source code for dollar.api.types.DollarFactory.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.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Range;
import com.thoughtworks.xstream.XStream;
import dollar.api.DollarException;
import dollar.api.DollarStatic;
import dollar.api.Pipeable;
import dollar.api.StateTracer;
import dollar.api.Type;
import dollar.api.Value;
import dollar.api.collections.CollectionUtil;
import dollar.api.exceptions.DollarFailureException;
import dollar.api.json.DecodeException;
import dollar.api.json.ImmutableJsonObject;
import dollar.api.json.JsonArray;
import dollar.api.json.JsonObject;
import dollar.api.json.impl.Json;
import dollar.api.monitor.DollarMonitor;
import dollar.api.script.Source;
import dollar.api.uri.URI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.stream.Stream;

public final class DollarFactory {

    /**
     * The constant DOUBLE_ZERO.
     */
    @NotNull
    public static final Value DOUBLE_ZERO = wrap(new DollarDecimal(0.0));
    /**
     * The constant FALSE.
     */
    @NotNull
    public static final Value FALSE = wrap(new DollarBoolean(false));
    @NotNull
    public static final Value INFINITY = wrap(new DollarInfinity(true));
    /**
     * The constant INTEGER_ZERO.
     */
    @NotNull
    public static final Value INTEGER_ZERO = wrap(new DollarInteger(0L));
    /**
     * The constant LOWERBOUND_KEY.
     */
    @NotNull
    public static final String LOWERBOUND_KEY = "lower";
    /**
     * The constant MILLISECOND_KEY.
     */
    @NotNull
    public static final String MILLISECOND_KEY = "millis";
    @NotNull
    public static final Value NEGATIVE_INFINITY = wrap(new DollarInfinity(false));
    /**
     * The constant VALUE_KEY.
     */
    @NotNull
    public static final String POSITIVE_KEY = "positive";
    /**
     * The constant TEXT_KEY.
     */
    @NotNull
    public static final String TEXT_KEY = "text";
    /**
     * The constant TRUE.
     */
    @NotNull
    public static final Value TRUE = wrap(new DollarBoolean(true));
    /**
     * The constant TYPE_KEY.
     */
    @NotNull
    public static final String TYPE_KEY = "$type";
    /**
     * The constant UPPERBOUND_KEY.
     */
    @NotNull
    public static final String UPPERBOUND_KEY = "upper";
    /**
     * The constant VALUE_KEY.
     */
    @NotNull
    public static final String VALUE_KEY = "value";
    /**
     * The constant VOID.
     */
    @NotNull
    public static final Value VOID = wrap(new DollarVoid());
    @NotNull
    private static final Logger log = LoggerFactory.getLogger("DollarSource");
    /**
     * The Monitor.
     */
    @NotNull
    static DollarMonitor monitor = DollarStatic.monitor();
    /**
     * The constant tracer.
     */
    @NotNull
    static StateTracer tracer = DollarStatic.tracer();

    private DollarFactory() {
    }

    /**
     * Block collection.
     *
     * @param Value the Value
     * @return the Value
     */
    @NotNull
    public static Value blockCollection(@NotNull List<Value> Value) {
        return wrap(new DollarBlockCollection(Value));
    }

    @NotNull
    private static Value create(@Nullable Object o) {
        if (o == null) {
            return wrap(new DollarVoid());
        }
        if (o instanceof Value) {
            return (Value) o;
        }
        if (o instanceof Boolean) {
            return wrap(new DollarBoolean((Boolean) o));
        }

        if (o instanceof Pipeable) {
            return wrap((Value) java.lang.reflect.Proxy.newProxyInstance(DollarStatic.class.getClassLoader(),
                    new Class<?>[] { Value.class }, new DollarLambda((Pipeable) o)));
        }
        if (o instanceof File) {
            try {
                try (FileInputStream stream = new FileInputStream((File) o)) {
                    return create(stream);
                }
            } catch (IOException e) {
                return failure(e);
            }
        }
        if (o instanceof JsonArray) {
            return wrap(new DollarList((JsonArray) o));
        }
        if (o instanceof JsonObject) {
            return wrap(new DollarMap(new ImmutableJsonObject((JsonObject) o)));
        }
        if ("JSONObject".equals(o.getClass().getSimpleName()) || (o instanceof ObjectNode)) {
            return wrap(new DollarMap(new JsonObject(o.toString())));
        }
        if ("JSONObject".equals(o.getClass().getSimpleName()) || (o instanceof ArrayNode)) {
            return wrap(new DollarList(new JsonArray(o.toString())));
        }
        if (o instanceof ImmutableMap) {
            return wrap(new DollarMap(((ImmutableMap<?, ?>) o)));
        }
        if (o instanceof Map) {
            return wrap(new DollarMap((Map) o));
        }
        if (o instanceof ImmutableList) {
            return wrap(new DollarList((ImmutableList<?>) o));
        }
        if (o instanceof List) {
            return wrap(new DollarList(ImmutableList.copyOf((List) o)));
        }
        if (o instanceof Collection) {
            return wrap(new DollarList(ImmutableList.copyOf(new ArrayList<>((Collection<?>) o))));
        }
        if (o.getClass().isArray()) {
            return wrap(new DollarList((Object[]) o));
        }
        if (o instanceof URI) {
            return wrap(new DollarURI((URI) o));
        }
        if ((o instanceof java.net.URI) || (o instanceof java.net.URL)) {
            return wrap(new DollarURI(URI.parse(o.toString())));
        }
        if (o instanceof Date) {
            return wrap(new DollarDate(((Date) o).getTime()));
        }
        if (o instanceof LocalDateTime) {
            return wrap(new DollarDate((LocalDateTime) o));
        }
        if (o instanceof Instant) {
            return wrap(new DollarDate((Instant) o));
        }
        if (o instanceof Double) {
            if (((Double) o == 0.0)) {
                return DOUBLE_ZERO;
            }
            return wrap(new DollarDecimal((Double) o));
        }
        if (o instanceof BigDecimal) {
            if ((((BigDecimal) o).doubleValue() == 0.0)) {
                return DOUBLE_ZERO;
            }
            return wrap(new DollarDecimal(((BigDecimal) o).doubleValue()));
        }
        if (o instanceof Float) {
            if (((Float) o == 0.0)) {
                return DOUBLE_ZERO;
            }
            return wrap(new DollarDecimal(((Float) o).doubleValue()));
        }
        if (o instanceof Long) {
            if (((Long) o == 0)) {
                return INTEGER_ZERO;
            }
            return wrap(new DollarInteger((Long) o));
        }
        if (o instanceof Integer) {
            if (((Integer) o == 0)) {
                return INTEGER_ZERO;
            }
            return wrap(new DollarInteger(((Integer) o).longValue()));
        }
        if (o instanceof Short) {
            if (((Short) o == 0)) {
                return INTEGER_ZERO;
            }
            return wrap(new DollarInteger(((Short) o).longValue()));
        }
        if (o instanceof Range) {
            return wrap(new DollarRange((Range) o, false));
        }
        if (o instanceof ImmutableJsonObject) {
            return wrap(new DollarMap((ImmutableJsonObject) o));
        }
        if (o instanceof InputStream) {
            try {
                return create(CollectionUtil.fromStream((InputStream) o));
            } catch (IOException e) {
                return failure(e);
            }
        }
        if (o instanceof String) {
            if (((String) o).matches("^[a-zA-Z0-9]+$")) {
                return wrap(new DollarString((String) o));

            } else if (((String) o).matches("^\\s*\\[.*")) {
                return wrap(new DollarList(new JsonArray(o.toString())));
            } else if (((String) o).matches("^\\s*\\{.*")) {
                try {
                    return wrap(new DollarMap(new JsonObject((String) o)));
                } catch (DecodeException de) {
                    log.error(de.getMessage(), de);
                    return wrap(new DollarString((String) o));
                }
            } else {
                return wrap(new DollarString((String) o));
            }
        }
        JsonObject json;
        if (o instanceof Multimap) {
            json = DollarStatic.mapToJson((Multimap) o);
        } else {
            json = Json.fromJavaObject(o);
        }
        return wrap(new DollarMap(json));
    }

    /**
     * Deserialize Value.
     *
     * @param s the s
     * @return the Value
     */
    @NotNull
    public static Value deserialize(@NotNull String s) {
        JsonObject jsonObject = new JsonObject(s);
        return fromJson(jsonObject);
    }

    /**
     * Deserialize Value.
     *
     * @param s the s
     * @return the Value
     */
    @NotNull
    public static Value deserialize64(@NotNull String s) {
        return (Value) new XStream().fromXML(new String(Base64.getDecoder().decode(s)));
    }

    /**
     * Failure Value.
     *
     * @param errorType the failure type
     * @param t         the t
     * @param quiet     to always avoid failing fast
     * @return the Value
     */
    @NotNull
    public static Value failure(@NotNull ErrorType errorType, @NotNull Exception t, boolean quiet) {
        if (DollarStatic.getConfig().failFast() && !quiet) {
            throw new DollarFailureException(t, errorType);
        } else {
            return wrap(new DollarError(errorType, t.getMessage()));
        }
    }

    /**
     * Failure Value.
     *
     * @param errorType the failure type
     * @param t         the t
     * @return the Value
     */
    @NotNull
    public static Value failure(@NotNull ErrorType errorType, @NotNull Exception t) {
        if (DollarStatic.getConfig().failFast()) {
            throw new DollarFailureException(t, errorType);
        } else {
            return wrap(new DollarError(errorType, t.getMessage()));
        }
    }

    /**
     * Failure Value.
     *
     * @param errorType the failure type
     * @param message   the message
     * @return the Value
     */
    @NotNull
    public static Value failure(@NotNull ErrorType errorType, @NotNull String message) {
        if (DollarStatic.getConfig().failFast()) {
            throw new DollarFailureException(errorType, message);
        } else {
            return wrap(new DollarError(errorType, message));
        }
    }

    /**
     * Failure Value.
     *
     * @param errorType the failure type
     * @param message   the message
     * @param quiet     the quiet
     * @return the Value
     */
    @NotNull
    public static Value failure(@NotNull ErrorType errorType, @NotNull String message, boolean quiet) {
        if (DollarStatic.getConfig().failFast() && !quiet) {
            throw new DollarFailureException(errorType, message);
        } else {
            return wrap(new DollarError(errorType, message));
        }
    }

    /**
     * Failure Value.
     *
     * @param throwable the throwable
     * @return the Value
     */
    @NotNull
    public static Value failure(@NotNull Exception throwable) {
        return failure(ErrorType.EXCEPTION, throwable, false);
    }

    /**
     * Failure with source.
     *
     * @param errorType the failure type
     * @param throwable the throwable
     * @param source    the source
     * @return the Value
     */
    @NotNull
    public static Value failureWithSource(@NotNull ErrorType errorType, @NotNull Exception throwable,
            @Nullable Source source) {
        if (source == null) {
            throw new NullPointerException();
        }
        if (DollarStatic.getConfig().failFast()) {
            final DollarFailureException dollarFailureException = new DollarFailureException(throwable, errorType);
            dollarFailureException.addSource(source);
            throw dollarFailureException;
        } else {
            return wrap(new DollarError(errorType, throwable.getMessage()));
        }
    }

    /**
     * From future.
     *
     * @param future the future
     * @return the Value
     */
    @NotNull
    public static Value fromFuture(@NotNull Future<Value> future) {
        return wrap((Value) java.lang.reflect.Proxy.newProxyInstance(DollarStatic.class.getClassLoader(),
                new Class<?>[] { Value.class }, new DollarLambda(i -> future.get(), false)));
    }

    /**
     * From stream.
     *
     * @param type    the type
     * @param rawBody the raw body
     * @return the Value
     * @throws IOException the iO exception
     */
    @NotNull
    public static Value fromIOStream(@NotNull SerializedType type, @NotNull InputStream rawBody)
            throws IOException {
        if (type == SerializedType.JSON) {
            ObjectMapper mapper = new ObjectMapper();
            final JsonNode jsonNode = mapper.readTree(rawBody);
            if (jsonNode.isArray()) {
                return create(jsonNode);
            } else if (jsonNode.isObject()) {
                return create(jsonNode);
            } else {
                throw new DollarException("Could not deserialize JSON, not array or object");
            }
        } else {
            throw new DollarException("Could not deserialize " + type);
        }
    }

    @NotNull
    private static Value fromJson(@NotNull JsonObject jsonObject) {
        final Type type;
        if (!jsonObject.containsField(TYPE_KEY)) {
            type = Type._MAP;
        } else {
            type = Type.of(jsonObject.getString(TYPE_KEY));
        }

        if (type.is(Type._VOID)) {
            return DollarStatic.$void();
        } else if (type.is(Type._INTEGER)) {
            return fromValue(jsonObject.getLong(VALUE_KEY));
        } else if (type.is(Type._BOOLEAN)) {
            return fromValue(jsonObject.getBoolean(VALUE_KEY));
        } else if (type.is(Type._DATE)) {
            return wrap(new DollarDate(Instant.parse(jsonObject.getString(TEXT_KEY))));
        } else if (type.is(Type._DECIMAL)) {
            return fromValue(jsonObject.getNumber(VALUE_KEY));
        } else if (type.is(Type._LIST)) {
            final JsonArray array = jsonObject.getArray(VALUE_KEY);
            ArrayList<Object> arrayList = new ArrayList<>();
            for (Object o : array) {
                arrayList.add(fromJson(o));
            }
            return wrap(new DollarList(ImmutableList.copyOf(arrayList)));
        } else if (type.is(Type._MAP)) {
            final JsonObject json;
            json = jsonObject;
            LinkedHashMap<String, Object> map = new LinkedHashMap<>();
            final Set<String> fieldNames = json.getFieldNames();
            for (String fieldName : fieldNames) {
                if (!fieldName.equals(TYPE_KEY)) {
                    map.put(fieldName, fromJson(json.get(fieldName)));
                }
            }
            return wrap(new DollarMap(map));
        } else if (type.is(Type._ERROR)) {
            final String errorType = jsonObject.getString("errorType");
            final String errorMessage = jsonObject.getString("errorMessage");
            return wrap(new DollarError(ErrorType.valueOf(errorType), errorMessage));
        } else if (type.is(Type._RANGE)) {
            final Value lower = fromJson(jsonObject.get(LOWERBOUND_KEY));
            final Value upper = fromJson(jsonObject.get(UPPERBOUND_KEY));
            return wrap(new DollarRange(lower, upper));
        } else if (type.is(Type._URI)) {
            return wrap(new DollarURI(URI.parse(jsonObject.getString(VALUE_KEY))));
        } else if (type.is(Type._INFINITY)) {
            return wrap(new DollarInfinity(jsonObject.getBoolean(POSITIVE_KEY)));
        } else if (type.is(Type._STRING)) {
            if (!(jsonObject.get(VALUE_KEY) instanceof String)) {
                log.error("_STRING type is not a a java String, {}", jsonObject.get(VALUE_KEY));
            }
            return wrap(new DollarString(jsonObject.getString(VALUE_KEY)));
        } else {
            throw new DollarException("Unrecognized type " + type);
        }
    }

    @NotNull
    private static Value fromJson(@Nullable Object value) {
        if (value == null) {
            return DollarStatic.$void();
        } else if (value instanceof LinkedHashMap) {
            JsonObject json = new JsonObject((Map<String, Object>) value);
            return fromJson(json);
        } else if (value instanceof ArrayList) {
            ArrayList list = (ArrayList) value;
            ArrayList<Value> result = new ArrayList<>();
            for (Object o : list) {
                result.add(fromJson(o));
            }
            return fromValue(result);

        } else if (value instanceof JsonObject) {
            if (((JsonObject) value).containsField(TYPE_KEY)) {
                return fromJson((JsonObject) value);
            } else {
                return fromValue(value);
            }
        } else if (value instanceof JsonArray) {
            return fromValue(value);
        } else if (value instanceof String) {
            return fromValue(value);
        } else if (value instanceof Number) {
            return fromValue(value);
        } else if (value instanceof Boolean) {
            return fromValue(value);
        } else if (value instanceof byte[]) {
            return fromValue(value);
        } else {
            throw new DollarException("Unrecognized type " + value.getClass() + " for " + value);
        }
    }

    /**
     * From lambda.
     *
     * @param pipeable the pipeable
     * @return the Value
     */
    @NotNull
    public static Value fromLambda(@NotNull Pipeable pipeable) {
        return fromValue(pipeable);
    }

    @NotNull
    public static Value fromList(@NotNull ImmutableList<Value> Values) {
        return wrap(new DollarList(Values));
    }

    @NotNull
    public static Value fromList(@NotNull List<Value> Values) {
        return wrap(new DollarList(ImmutableList.copyOf(Values)));
    }

    @NotNull
    public static Value fromMap(@NotNull ImmutableMap<Value, Value> entries) {
        return wrap(new DollarMap(entries));
    }

    public static Value fromPair(@NotNull Object k, @NotNull Object v) {
        return wrap(new DollarMap(ImmutableMap.of(create(k), create(v))));
    }

    @NotNull
    public static Value fromQueue(@NotNull LinkedBlockingDeque<Value> linkedBlockingDeque) {
        return wrap(new DollarQueue(ImmutableList.of(), linkedBlockingDeque));
    }

    /**
     * From range.
     *
     * @param from the from
     * @param to   the to
     * @return the Value
     */
    @NotNull
    public static Value fromRange(@NotNull Value from, @NotNull Value to) {
        return wrap(new DollarRange(from, to));
    }

    public static Value fromSet(@NotNull Set<Value> Values) {
        return wrap(new DollarList(ImmutableList.of(Values)));
    }

    public static Value fromStream(@NotNull Stream<Value> stream) {
        return wrap(new DollarSequence(stream));
    }

    public static Value fromStream(@NotNull List<Value> stream, boolean parallel) {
        return wrap(new DollarSequence(stream, parallel));
    }

    /**
     * From string value.
     *
     * @param body the body
     * @return the Value
     */
    @NotNull
    public static Value fromStringValue(@NotNull String body) {
        return wrap(new DollarString(body));
    }

    /**
     * From uRI.
     *
     * @param from the from
     * @return the Value
     */
    @NotNull
    public static Value fromURI(@NotNull Value from) {
        if (from.uri()) {
            return from;
        } else {
            return fromURI(from.$S());
        }
    }

    /**
     * From uRI.
     *
     * @param uri the uri
     * @return the Value
     */
    @NotNull
    public static Value fromURI(@NotNull String uri) {
        try {
            return wrap(new DollarURI(URI.parse(uri)));
        } catch (Exception e) {
            return DollarStatic.handleError(e, null);
        }
    }

    /**
     * From value.
     *
     * @return the Value
     */
    @NotNull
    public static Value fromValue() {
        return create(new JsonObject());
    }

    /**
     * From value.
     *
     * @param o the o
     * @return the Value
     */
    @NotNull
    public static Value fromValue(@Nullable Object o) {
        return create(o);
    }

    @NotNull
    public static Value fromYaml(@NotNull String yamlString) {
        Yaml yaml = new Yaml();
        return wrap(fromValue(yaml.load(yamlString)));
    }

    @NotNull
    public static Value fromYaml(@NotNull File yamlFile) {
        Yaml yaml = new Yaml();
        try (FileInputStream fileInputStream = new FileInputStream(yamlFile)) {
            return wrap(fromValue(yaml.load(fileInputStream)));
        } catch (IOException e) {
            return failure(e);
        }
    }

    @NotNull
    public static Value infinity(boolean positive) {
        return wrap(new DollarInfinity(positive));
    }

    @Nullable
    private static Object mapToJsonInternal(@NotNull Value value) {
        final JsonObject json = new JsonObject();
        ImmutableMap<Value, Value> map = value.toVarMap();
        final Set<Value> fieldNames = map.keySet();
        for (Value fieldName : fieldNames) {
            Value v = map.get(fieldName);
            json.putValue(fieldName.toString(), toJson(v));
        }
        return json;
    }

    @NotNull
    private static List<Throwable> mergeErrors(@NotNull ImmutableList<Throwable>[] errors) {
        List<Throwable> mergedErrors = new ArrayList<>();
        for (ImmutableList<Throwable> errorList : errors) {
            mergedErrors.addAll(errorList);
        }
        return mergedErrors;
    }

    @SafeVarargs
    @NotNull
    public static Value newNull(@NotNull Type type, ImmutableList<Throwable>... errors) {
        return new DollarNull(ImmutableList.copyOf(mergeErrors(errors)), type);
    }

    /**
     * New void.
     *
     * @return the Value
     */
    @NotNull
    public static Value newVoid() {
        return wrap(new DollarVoid());
    }

    /**
     * Serialize string.
     *
     * @param value the value
     * @return the string
     */
    @Nullable
    public static String serialize(@NotNull Value value) {
        final Object jsonObject = toJson(value.$fixDeep());
        return (jsonObject == null) ? null : jsonObject.toString();
    }

    /**
     * Serialize string.
     *
     * @param value the value
     * @return the string
     */
    @Nullable
    public static String serialize64(@NotNull Value value) {
        final String xmlObject = new XStream().toXML(value.$fixDeep());
        return Base64.getEncoder().encodeToString(xmlObject.getBytes());
    }

    /**
     * To json.
     *
     * @param value the value
     * @return the object
     */
    @Nullable
    public static Object toJson(@NotNull Value value) {
        Type i = value.$type();
        if (i.is(Type._VOID) || i.is(Type._INTEGER) || i.is(Type._BOOLEAN) || i.is(Type._DECIMAL)
                || i.is(Type._STRING)) {
            return value.toJavaObject();
        } else if (i.is(Type._DATE)) {
            final JsonObject jsonObject = new JsonObject();
            jsonObject.putString(TYPE_KEY, value.$type().name());
            jsonObject.putString(TEXT_KEY, value.$S());
            jsonObject.putNumber(MILLISECOND_KEY, (long) (value.toDouble() * 24 * 60 * 60 * 1000));
            return jsonObject;
        } else if (i.is(Type._URI)) {
            final JsonObject uriJsonObject = new JsonObject();
            uriJsonObject.putString(TYPE_KEY, value.$type().name());
            uriJsonObject.putString(VALUE_KEY, value.$S());
            return uriJsonObject;
        } else if (i.is(Type._ERROR)) {
            final JsonObject errorJsonObject = new JsonObject();
            errorJsonObject.putString(TYPE_KEY, value.$type().name());
            errorJsonObject.putValue(VALUE_KEY, value.toJsonType());
            return errorJsonObject;
        } else if (i.is(Type._INFINITY)) {
            final JsonObject infinityJsonObject = new JsonObject();
            infinityJsonObject.putString(TYPE_KEY, value.$type().name());
            infinityJsonObject.putValue(POSITIVE_KEY, value.positive());
            return infinityJsonObject;
        } else if (i.is(Type._LIST) || i.is(Type._QUEUE)) {
            final JsonArray array = new JsonArray();
            ImmutableList<Value> arrayList = value.toVarList();
            for (Value v : arrayList) {
                array.add(toJson(v));
            }

            return array;
        } else if (i.is(Type._MAP)) {
            return mapToJsonInternal(value);
        } else if (i.is(Type._RANGE)) {
            final JsonObject rangeObject = new JsonObject();
            rangeObject.putString(TYPE_KEY, value.$type().name());
            final Range<Value> range = value.toJavaObject();
            rangeObject.putValue(LOWERBOUND_KEY, toJson(range.lowerEndpoint()));
            rangeObject.putValue(UPPERBOUND_KEY, toJson(range.upperEndpoint()));
            return rangeObject;
        } else if (i.is(Type._ANY)) {
            return null;
        } else {
            return mapToJsonInternal(value);
        }
    }

    @NotNull
    private static JsonObject valueToJson(@NotNull Value value) {
        final JsonObject jsonObject = new JsonObject();
        jsonObject.putString(TYPE_KEY, value.$type().name());
        jsonObject.putValue(VALUE_KEY, value.toJavaObject());
        return jsonObject;
    }

    /**
     * Wrap Value.
     *
     * @param value the value
     * @return the Value
     */
    @NotNull
    public static Value wrap(@NotNull Value value) {
        return wrap(value, DollarStatic.monitor(), DollarStatic.tracer());
    }

    @NotNull
    private static Value wrap(@NotNull Value value, @NotNull DollarMonitor monitor, @NotNull StateTracer tracer) {
        final Value val;
        if (DollarStatic.getConfig().wrapForMonitoring()) {
            val = new DollarWrapper(value, monitor, tracer);
        } else {
            val = value;
        }
        if (DollarStatic.getConfig().wrapForGuards()) {
            return (Value) java.lang.reflect.Proxy.newProxyInstance(DollarStatic.class.getClassLoader(),
                    new Class<?>[] { Value.class }, new DollarGuard(val));
        } else {
            return val;
        }

    }
}