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

com.github.rschmitt.dynamicobject.internal.DynamicObjectInstance Maven / Gradle / Ivy

There is a newer version: 1.7.3
Show newest version
package com.github.rschmitt.dynamicobject.internal;

import com.github.rschmitt.dynamicobject.DynamicObject;

import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import clojure.lang.AFn;
import clojure.lang.Associative;
import clojure.lang.IHashEq;
import clojure.lang.IMapEntry;
import clojure.lang.IObj;
import clojure.lang.IPersistentCollection;
import clojure.lang.IPersistentMap;
import clojure.lang.ISeq;
import clojure.lang.MapEquivalence;
import clojure.lang.Seqable;

import static java.lang.String.format;

public abstract class DynamicObjectInstance> extends AFn implements Map, IPersistentMap, IObj, MapEquivalence, IHashEq, DynamicObjectPrintHook, CustomValidationHook {
    private static final Object Default = new Object();
    private static final Object Null = new Object();

    private final Map map;
    private final Class type;
    private final ConcurrentHashMap valueCache = new ConcurrentHashMap();

    public DynamicObjectInstance(Map map, Class type) {
        this.map = map;
        this.type = type;
    }

    public Map getMap() {
        return map;
    }

    public Class getType() {
        return type;
    }

    @Override
    public String toString() {
        return DynamicObject.serialize(this);
    }

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

    @Override
    public boolean equals(Object other) {
        if (other == this) return true;
        if (other == null) return false;

        if (other instanceof DynamicObject)
            return map.equals(((DynamicObject) other).getMap());
        else
            return other.equals(map);
    }

    public void prettyPrint() {
        ClojureStuff.Pprint.invoke(this);
    }

    public String toFormattedString() {
        Writer w = new StringWriter();
        ClojureStuff.Pprint.invoke(this, w);
        return w.toString();
    }

    public D merge(D other) {
        AFn ignoreNulls = new AFn() {
            public Object invoke(Object arg1, Object arg2) {
                return (arg2 == null) ? arg1 : arg2;
            }
        };
        Map mergedMap = (Map) ClojureStuff.MergeWith.invoke(ignoreNulls, map, other.getMap());
        return DynamicObject.wrap(mergedMap, type);
    }

    public D intersect(D arg) {
        return diff(arg, 2);
    }

    public D subtract(D arg) {
        return diff(arg, 0);
    }

    private D diff(D arg, int idx) {
        Object array = ClojureStuff.Diff.invoke(map, arg.getMap());
        Object union = ClojureStuff.Nth.invoke(array, idx);
        if (union == null) union = ClojureStuff.EmptyMap;
        return DynamicObject.wrap((Map) union, type);
    }

    public D convertAndAssoc(Object key, Object value) {
        return (D) assoc(key, Conversions.javaToClojure(value));
    }

    @Override
    public IPersistentMap assoc(Object key, Object value) {
        return (DynamicObjectInstance) DynamicObject.wrap((Map) ClojureStuff.Assoc.invoke(map, key, value), type);
    }

    public D assocMeta(Object key, Object value) {
        return DynamicObject.wrap((Map) ClojureStuff.VaryMeta.invoke(map, ClojureStuff.Assoc, key, value), type);
    }

    public Object getMetadataFor(Object key) {
        Object meta = ClojureStuff.Meta.invoke(map);
        return ClojureStuff.Get.invoke(meta, key);
    }

    public Object invokeGetter(Object key, boolean isRequired, Type genericReturnType) {
        Object value = getAndCacheValueFor(key, genericReturnType);
        if (value == null && isRequired)
            throw new NullPointerException(format("Required field %s was null", key.toString()));
        return value;
    }

    @SuppressWarnings("unchecked")
    public Object getAndCacheValueFor(Object key, Type genericReturnType) {
        Object cachedValue = valueCache.getOrDefault(key, Default);
        if (cachedValue == Null) return null;
        if (cachedValue != Default) return cachedValue;
        Object value = getValueFor(key, genericReturnType);
        if (value == null)
            valueCache.putIfAbsent(key, Null);
        else
            valueCache.putIfAbsent(key, value);
        return value;
    }

    public Object getValueFor(Object key, Type genericReturnType) {
        Object val = ClojureStuff.Get.invoke(map, key);
        return Conversions.clojureToJava(val, genericReturnType);
    }

    public Object $$noop() {
        return this;
    }

    public Object $$validate() {
        Validation.validateInstance(this);
        return $$customValidate();
    }

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

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

    @Override
    public boolean containsKey(Object key) {
        return map.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return map.containsValue(value);
    }

    @Override
    public Object get(Object key) {
        return map.get(key);
    }

    @Override
    public Object put(Object key, Object value) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Object remove(Object key) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void putAll(Map m) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void clear() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Set keySet() {
        return map.keySet();
    }

    @Override
    public Collection values() {
        return map.values();
    }

    @Override
    public Set entrySet() {
        return map.entrySet();
    }

    @Override
    public IMapEntry entryAt(Object key) {
        return ((Associative) map).entryAt(key);
    }

    @Override
    public Object valAt(Object key) {
        return ((Associative) map).valAt(key);
    }

    @Override
    public Object valAt(Object key, Object notFound) {
        return ((Associative) map).valAt(key, notFound);
    }

    @Override
    public int count() {
        return ((IPersistentCollection) map).count();
    }

    @Override
    public IPersistentCollection cons(Object o) {
        Map newMap = (Map) ((IPersistentCollection) map).cons(o);
        return (DynamicObjectInstance) DynamicObject.wrap(newMap, type);
    }

    @Override
    public IPersistentCollection empty() {
        return (DynamicObjectInstance) DynamicObject.wrap(ClojureStuff.EmptyMap, type);
    }

    @Override
    public boolean equiv(Object o) {
        return ((IPersistentCollection) map).equiv(o);
    }

    @Override
    public ISeq seq() {
        return ((Seqable) map).seq();
    }

    @Override
    public IPersistentMap assocEx(Object key, Object val) {
        Object newMap = ((IPersistentMap) map).assocEx(key, val);
        return (DynamicObjectInstance) DynamicObject.wrap((Map) newMap, type);
    }

    @Override
    public IPersistentMap without(Object key) {
        Object newMap = ((IPersistentMap) map).without(key);
        return (DynamicObjectInstance) DynamicObject.wrap((Map) newMap, type);
    }

    @Override
    public IPersistentMap meta() {
        return (IPersistentMap) ClojureStuff.Meta.invoke(map);
    }

    @Override
    public IObj withMeta(IPersistentMap meta) {
        Object newMap = ClojureStuff.VaryMeta.invoke(map, meta);
        return (DynamicObjectInstance) DynamicObject.wrap((Map) newMap, type);
    }

    @Override
    public int hasheq() {
        return ((IHashEq) map).hasheq();
    }

    @Override
    public Object invoke(Object arg1) {
        return valAt(arg1);
    }

    @Override
    public Object invoke(Object arg1, Object notFound) {
        return valAt(arg1, notFound);
    }

    @Override
    public Iterator iterator() {
        return ((IPersistentMap) map).iterator();
    }

    public Iterator valIterator() throws ReflectiveOperationException {
        Class aClass = map.getClass();
        return (Iterator) aClass.getMethod("valIterator").invoke(map);
    }

    public Iterator keyIterator() throws ReflectiveOperationException {
        Class aClass = map.getClass();
        return (Iterator) aClass.getMethod("keyIterator").invoke(map);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy