functionalj.ref.Ref Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of functionalj-core Show documentation
Show all versions of functionalj-core Show documentation
The module for FunctionalJ Core.
// ============================================================================
// 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());
}
}