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

functionalj.ref.Ref Maven / Gradle / Ivy

// ============================================================================
// Copyright (c) 2017-2019 Nawapunth Manusitthipol (NawaMan - http://nawaman.net).
// ----------------------------------------------------------------------------
// MIT License
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// ============================================================================
package functionalj.ref;

import static java.util.Objects.requireNonNull;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

import functionalj.function.Func0;
import functionalj.function.Func1;
import functionalj.list.FuncList;
import functionalj.result.Result;
import lombok.val;

public abstract class Ref {
    
    private static final ThreadLocal refEntry = ThreadLocal.withInitial(()->new Entry(null, null));
    
    public static  Ref to(Class dataClass) {
        return new RefTo(dataClass);
    }
    
    public static  RefBuilder of(Class dataClass) {
        return new RefBuilder(dataClass);
    }
    
    public static  Ref ofValue(D value) {
        @SuppressWarnings("unchecked")
        val dataClass = (Class)value.getClass();
        val result    = Result.valueOf(value);
        val ref       = new RefOf.FromResult(dataClass, result, null);
        return ref;
    }
    
    public static  Ref dictactedTo(D value) {
        val ref = ofValue(value);
        return ref.dictate();
    }
    
    final Class    dataClass;
    final Supplier whenAbsentSupplier;
    
    Ref(Class dataClass, Supplier whenAbsentSupplier) {
        this.dataClass          = requireNonNull(dataClass);
        this.whenAbsentSupplier = whenAbsentSupplier;
    }
    
    abstract Result findResult();
    
    Result findOverrideResult() {
        val entry    = refEntry.get();
        val supplier = entry.findSupplier(this);
        if (supplier != null) {
            val result = Result.of(supplier);
            return result;
        }
        
        return null;
    }
    
    public DATA get() {
        return getResult().get();
    }
    
    public final Class getDataType() {
        return dataClass;
    }
    final Supplier getElseSupplier() {
        return whenAbsentSupplier;
    }
    
    public final Result getResult() {
        val override = findOverrideResult();
        if (override != null) {
            if (override.isPresent() || (whenAbsentSupplier == null))
                return override;
            if (!override.isPresent() && (whenAbsentSupplier != null)) {
                val elseValue = whenAbsentSupplier.get();
                if (elseValue != null)
                     return Result.valueOf(elseValue);
                else return Result.ofNotExist();
            }
        }
        
        val result = findResult();
        if (result != null) {
            if (result.isPresent() || (whenAbsentSupplier == null))
                return result;
            if (!result.isPresent() && (whenAbsentSupplier != null)) {
                val elseValue = Result.from(whenAbsentSupplier);
                if (elseValue.isPresent())
                    return elseValue;
               else return Result.ofNotExist();
            }
        }
        
        if (whenAbsentSupplier == null)
            return Result.ofNotExist();
        
        val elseValue = whenAbsentSupplier.get();
        if (elseValue == null)
            return Result.ofNotExist();
        
        return Result.valueOf(elseValue);
    }
    
    public final Result asResult() {
        return getResult();
    }
    
    public final Optional asOptional() {
        return getResult().toOptional();
    }
    
    public final Func0 valueSupplier() {
        return ()->{
            val value = value();
            return value;
        };
    }
    
    public final DATA value() {
        val result = getResult();
        val value  = result.value();
        return value;
    }
    public final DATA orElse(DATA elseValue) {
        return getResult().orElse(elseValue);
    }
    public final DATA orGet(Supplier elseValue) {
        return getResult().orElseGet(elseValue);
    }
    public final DATA orElseGet(Supplier elseValue) {
        return getResult().orElseGet(elseValue);
    }
    public final  Func0 then(Func1 mapper) {
        return this
                .valueSupplier()
                .then(mapper);
    }
    
    // Map to ...
    
    public final  Ref map(Class targetClass, Func1 mapper) {
        return Ref.of(targetClass).defaultFrom(()->{
            val result = getResult();
            val target = result.map(mapper);
            return target.get();
        });
    }
    
    //-- Overriding --
    
    // TODO - These methods should not be for DictatedRef ... but I don't know how to gracefully takecare of this.
    public final Substitution butWith(DATA value) {
        return new Substitution.Value(this, false, value);
    }
    public final Substitution butFrom(Func0 supplier) {
        return new Substitution.Supplier(this, false, supplier);
    }
    
    abstract Ref whenAbsent(Func0 whenAbsent);
    // These else method has no effect once the Ref become DictatedRef
    // They also has no effect for RefTo.
    public Ref whenAbsentUse(DATA defaultValue) {
        return whenAbsent(WhenAbsent.Use(defaultValue));
    }
    public Ref whenAbsentGet(Supplier defaultSupplier) {
        return whenAbsent(WhenAbsent.Get(defaultSupplier));
    }
    public Ref whenAbsentUseDefault() {
        return whenAbsentUseDefaultOrGet(null);
    }
    public Ref whenAbsentUseDefaultOrGet(Supplier manualDefault) {
        Func0 useDefault = WhenAbsent.UseDefault(getDataType());
        if (manualDefault != null)
            useDefault = useDefault.whenAbsentGet(manualDefault);
        return whenAbsent(useDefault);
    }
    public DictatedRef dictate() {
        return new DictatedRef(this);
    }
    
    public RetainedRef.Builder retained() {
        return new RetainedRef.Builder(this, true);
    }
    
    //== Overriability ==
    
    @SuppressWarnings("rawtypes")
    private static class Entry {
        
        private final Entry        parent;
        private final Substitution substitution;
        
        Entry(Entry parent, Substitution substitution) {
            this.parent       = parent;
            this.substitution = substitution;
        }
        
        @SuppressWarnings("unchecked")
        public  Func0 findSupplier(Ref ref) {
            if (ref == null)
                return null;
            
            if (substitution != null) {
                if (ref.equals(substitution.ref())) {
                    return substitution.supplier();
                }
            }
            
            if (parent == null)
                return null;
            
            return parent.findSupplier(ref);
        }
        
        @Override
        public String toString() {
            return "Entry [parent=" + parent + ", substitution=" + substitution + "]";
        }
        
    }
    
    static final  
            V runWith(List> substitutions, ComputeBody action) throws E {
        val map = refEntry.get();
        try {
            if (substitutions != null) {
                Entry current = map;
                for (val substitution : substitutions) {
                    if (substitution == null)
                        continue;
                    if (substitution.ref() instanceof DictatedRef)
                        continue;
                    
                    current = new Entry(current, substitution);
                }
                refEntry.set(current);
            }
            
            return action.compute();
        } finally {
            refEntry.set(map);
        }
    }
    
    static final  
            void runWith(List> substitutions, RunBody action) throws E {
        val map = refEntry.get();
        try {
            if (substitutions != null) {
                Entry currentEntry = map;
                for (val substitution : substitutions) {
                    if (substitution == null)
                        continue;
                    if (substitution.ref() instanceof DictatedRef)
                        continue;
                    
                    val newEntry = new Entry(currentEntry, substitution);
                    refEntry.set(newEntry);
                    currentEntry = newEntry;
                }
            }
            
            action.run();
        } finally {
            refEntry.set(map);
        }
    }
    
    public static final FuncList> getCurrentRefs() {
        val set = new HashSet>();
        Entry entry = refEntry.get();
        while ((entry != null) && (entry.substitution != null)) {
            @SuppressWarnings("rawtypes")
            val ref = entry.substitution.ref();
            set.add(ref);
            
            entry = entry.parent;
        }
        return FuncList.from(set);
    }
    
    static final FuncList> getSubstitutions() {
        val map = new HashMap, Substitution>();
        Entry entry = refEntry.get();
        while ((entry != null) && (entry.substitution != null)) {
            @SuppressWarnings("rawtypes")
            val ref = entry.substitution.ref();
            map.putIfAbsent(ref, entry.substitution);
            
            entry = entry.parent;
        }
        return FuncList.from(map.values());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy