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

functionalj.ref.RetainedRef 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.Objects;
import java.util.function.BiPredicate;
import java.util.function.Predicate;

import functionalj.environments.Env;
import functionalj.function.Func0;
import functionalj.result.Result;
import lombok.val;

public class RetainedRef extends RefOf implements RetainChecker {
    
    private static final Object NONE = new Object();
    
    private final Ref     sourceRef;
    private final RetainChecker checker;
    
    private final Holder data;
    
    public RetainedRef(Ref sourceRef, RetainChecker checker, boolean isLocal) {
        super(sourceRef.getDataType());
        this.sourceRef = sourceRef;
        this.checker   = checker;
        this.data      = new Holder<>(isLocal);
        this.data.set(NONE, NONE);
    }
    
    @Override
    public boolean stillValid() {
        return checker.stillValid();
    }
    
    @Override
    protected Result findResult() {
        val oldData    = data.get();
        val noData     = Objects.equals(oldData, NONE);
        val requireNew = !stillValid();
        if (noData || requireNew) {
            val newResult = sourceRef.asResult().getResult();
            data.set(oldData, newResult);
        }
        
        @SuppressWarnings("unchecked")
        val currentData = (Result)data.get();
        return currentData;
    }
    
    final Ref whenAbsent(Func0 defaultSupplier) {
        val newSourceRef = sourceRef.whenAbsentGet(defaultSupplier);
        if (newSourceRef == sourceRef)
            return this;
        
        return new RetainedRef<>(newSourceRef, checker, data.isLocal());
    }
    
    //== Aux Class ==
    
    public static class Builder {
        
        private final Ref sourceRef;
        private final boolean   isLocal;
        
        public Builder(Ref sourceRef, boolean isLocal) {
            this.sourceRef = requireNonNull(sourceRef);
            this.isLocal   = isLocal;
        }
        
        public Builder globally() {
            if (!isLocal)
                return this;
            
            return new Builder<>(sourceRef, false);
        }
        
        public Builder locally() {
            if (isLocal)
                return this;
            
            return new Builder<>(sourceRef, true);
        }
        
        public RetainedRef forever() {
            return new RetainedRef(sourceRef, RetainChecker.forever, isLocal);
        }
        public RetainedRef never() {
            return new RetainedRef(sourceRef, RetainChecker.never, isLocal);
        }
        
        public  WhileBuilder when(Ref stateSupplier) {
            return new WhileBuilder<>(sourceRef, stateSupplier, isLocal);
        }
        public  RetainedRef when(Ref stateSupplier, BiPredicate whenStateChange) {
            val updateOnChange  = new RetainChecker.UpdateOnChanged();
            val valueSupplier   = stateSupplier.valueSupplier();
            val checker         = new SuppliedValueCheck(
                                        isLocal,
                                        valueSupplier,
                                        valueSupplier,
                                        whenStateChange,
                                        updateOnChange);
            val ref = new RetainedRef<>(sourceRef, checker, isLocal);
            return ref;
        }
        public ForPeriodBuilder withIn(long period) {
            return new ForPeriodBuilder(sourceRef, period, isLocal);
        }
        
        public Builder localThread() {
            // TODO Auto-generated method stub
            return this;
        }
    }
    
    public static class WhileBuilder {
        
        private final Ref  sourceRef;
        private final Ref stateSupplier;
        private final boolean    isLocal;
        
        public WhileBuilder(Ref sourceRef, Ref stateSupplier, boolean isLocal) {
            this.sourceRef     = requireNonNull(sourceRef);
            this.stateSupplier = requireNonNull(stateSupplier);
            this.isLocal       = isLocal;
        }
        public RetainedRef same() {
            val whenStateChange = new RetainChecker.WhenNotSame();
            val updateOnChange  = new RetainChecker.UpdateOnChanged();
            val valueSupplier   = stateSupplier.valueSupplier();
            val checker         = new SuppliedValueCheck(
                                        isLocal,
                                        valueSupplier,
                                        valueSupplier,
                                        whenStateChange,
                                        updateOnChange);
            val ref = new RetainedRef<>(sourceRef, checker, isLocal);
            return ref;
        }
        public RetainedRef equals() {
            val whenStateChange = new RetainChecker.WhenNotEqual();
            val updateOnChange  = new RetainChecker.UpdateOnChanged();
            val valueSupplier   = stateSupplier.valueSupplier();
            val checker         = new SuppliedValueCheck(
                                        isLocal,
                                        valueSupplier,
                                        valueSupplier,
                                        whenStateChange,
                                        updateOnChange);
            val ref = new RetainedRef<>(sourceRef, checker, isLocal);
            return ref;
        }
        public RetainedRef match(Predicate matcher) {
            val whenStateChange = (BiPredicate)((STATE oldState, STATE newState) -> !matcher.test(newState));
            val updateOnChange  = new RetainChecker.UpdateOnChanged();
            val valueSupplier   = stateSupplier.valueSupplier();
            val checker         = new SuppliedValueCheck(
                                        isLocal,
                                        valueSupplier,
                                        valueSupplier,
                                        whenStateChange,
                                        updateOnChange);
            val ref = new RetainedRef<>(sourceRef, checker, isLocal);
            return ref;
        }
    }
    
    public static class ForPeriodBuilder {
        private final Ref sourceRef;
        private final long      period;
        private final boolean   isLocal;
        
        public ForPeriodBuilder(Ref sourceRef, long period, boolean isLocal) {
            this.sourceRef = requireNonNull(sourceRef);
            this.period    = period;
            this.isLocal   = isLocal;
        }
        public RetainedRef milliSeconds() {
            return milliSeconds(period);
        }
        public RetainedRef seconds() {
            return milliSeconds(period * 1000L);
        }
        public RetainedRef minutes() {
            return milliSeconds(period * 60 * 1000L);
        }
        public RetainedRef hours() {
            return milliSeconds(period * 60 * 60 * 1000L);
        }
        public RetainedRef days() {
            return milliSeconds(period * 24 * 60 * 60 * 1000L);
        }
        public RetainedRef weeks() {
            return milliSeconds(period * 7 * 24 * 60 * 60 * 1000L);
        }
        private  RetainedRef milliSeconds(long milliSeconds) {
            val whenStateChange = (BiPredicate)((Long oldState, Long newState) -> {
                long oldTime = (oldState == null) ? 0 : oldState.longValue();
                long newTime = (newState == null) ? 0 : newState.longValue();
                return oldTime + milliSeconds <= newTime;
            });
            val updateOnChange = new RetainChecker.UpdateOnChanged();
            val valueSupplier  = (Func0)Env.time()::currentMilliSecond;
            val checker        = new SuppliedValueCheck(
                                        isLocal,
                                        valueSupplier,
                                        valueSupplier,
                                        whenStateChange,
                                        updateOnChange);
            val ref = new RetainedRef<>(sourceRef, checker, isLocal);
            return ref;
        }
    }
    
}