Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.phantomthief.model.builder.impl.SimpleModelBuilder Maven / Gradle / Ivy
package com.github.phantomthief.model.builder.impl;
import static com.google.common.collect.HashMultimap.create;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonMap;
import static java.util.Spliterator.IMMUTABLE;
import static java.util.Spliterator.NONNULL;
import static java.util.Spliterator.ORDERED;
import static java.util.Spliterators.spliteratorUnknownSize;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import static org.apache.commons.lang3.ClassUtils.getAllInterfaces;
import static org.apache.commons.lang3.ClassUtils.getAllSuperclasses;
import static org.apache.commons.lang3.builder.ToStringBuilder.reflectionToString;
import static org.apache.commons.lang3.builder.ToStringStyle.SHORT_PREFIX_STYLE;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.slf4j.Logger;
import com.github.phantomthief.model.builder.ModelBuilder;
import com.github.phantomthief.model.builder.context.BuildContext;
import com.github.phantomthief.model.builder.context.impl.SimpleBuildContext;
import com.github.phantomthief.model.builder.util.MergeUtils;
import com.google.common.collect.SetMultimap;
/**
* @author w.vela
*/
@SuppressWarnings("unchecked")
public class SimpleModelBuilder implements ModelBuilder {
private static final Logger logger = getLogger(SimpleModelBuilder.class);
// obj.class=>obj->(namespace,ids)
private final SetMultimap, Function>>> idExtractors = create();
// obj.class=>obj->(namespace,values)
private final SetMultimap, Function>>> valueExtractors = create();
// idNamespace=>(valueNamespace, ids->values)
private final SetMultimap, Map>>> valueBuilders = create();
// targetNamespace=>Function
private final Map>> lazyBuilders = new HashMap<>();
private final ConcurrentMap, Set>>>> cachedIdExtractors = new ConcurrentHashMap<>();
private final ConcurrentMap, Set>>>> cachedValueExtractors = new ConcurrentHashMap<>();
@Override
public void buildMulti(Iterable> sources, B buildContext) {
if (sources == null) {
return;
}
if (buildContext instanceof SimpleBuildContext) {
SimpleBuildContext simpleBuildContext = (SimpleBuildContext) buildContext;
lazyBuilders.forEach(simpleBuildContext::setupLazyNodeData);
}
Set pendingForBuilding = stream(sources).collect(toSet());
while (!pendingForBuilding.isEmpty()) {
logger.debug("building source:{}", pendingForBuilding);
// namespace->ids
Map> idsMap = new HashMap<>();
// namespace->values
Map> valuesMap = new HashMap<>();
for (Object object : pendingForBuilding) {
extract(object, buildContext, idsMap, valuesMap);
}
logger.debug("extracted data, id:{}", idsMap);
logger.debug("extracted data, values:{}", valuesMap);
valueBuild(idsMap, valuesMap, buildContext);
logger.debug("after value build:{}", valuesMap);
mergeToBuildContext(valuesMap, buildContext);
pendingForBuilding = valuesMap.values().stream().flatMap(map -> map.values().stream())
.collect(toSet());
}
}
private void mergeToBuildContext(Map> valuesMap, B buildContext) {
valuesMap.forEach(
(valueNamespace, values) -> buildContext.getData(valueNamespace).putAll(values));
}
private void valueBuild(Map> idsMap,
Map> valuesMap, B buildContext) {
idsMap.forEach((idNamespace, ids) -> valueBuilders.get(idNamespace)
.forEach(valueBuilderWrapper -> {
Object valueNamespace = valueBuilderWrapper.getKey();
BiFunction, Map> valueBuilder = valueBuilderWrapper
.getValue();
Set needToBuildIds = filterIdSetOnBuild(ids, buildContext, valuesMap,
valueNamespace);
Map values = valueBuilder.apply(buildContext, needToBuildIds);
if (values != null) {
valuesMap.merge(valueNamespace, values, MergeUtils::merge);
}
}));
}
private Set filterIdSetOnBuild(Set original, B buildContext,
Map> valuesMap, Object valueNamespace) {
Set buildContextExistIds = buildContext.getData(valueNamespace).keySet();
Set valueMapExistIds = valuesMap
.computeIfAbsent(valueNamespace, i -> new HashMap<>()).keySet();
return original.stream() //
.filter(id -> !buildContextExistIds.contains(id)) //
.filter(id -> !valueMapExistIds.contains(id)) //
.collect(toSet());
}
// return new found data.
private void extract(Object obj, B buildContext, Map> idsMap,
Map> valuesMap) {
if (obj == null) {
return;
}
cachedValueExtractors
.computeIfAbsent(obj.getClass(),
t -> getAllSuperTypes(t).stream()
.flatMap(i -> valueExtractors.get(i).stream()).collect(toSet()))
.forEach(valueExtractor -> {
KeyPair> values = valueExtractor.apply(obj);
Map filtered = filterValueMap(values, buildContext);
idsMap.merge(values.getKey(), new HashSet<>(filtered.keySet()),
MergeUtils::merge);
valuesMap.merge(values.getKey(), filtered, MergeUtils::merge);
});
cachedIdExtractors
.computeIfAbsent(obj.getClass(), t -> getAllSuperTypes(t).stream()
.flatMap(i -> idExtractors.get(i).stream()).collect(toSet()))
.forEach(idExtractor -> {
KeyPair> ids = idExtractor.apply(obj);
idsMap.merge(ids.getKey(), filterIdSet(ids, buildContext, valuesMap),
MergeUtils::merge);
});
}
private Set filterIdSet(KeyPair> keyPair, B buildContext,
Map> valuesMap) {
Set buildContextExistIds = buildContext.getData(keyPair.getKey()).keySet();
Set valueMapExistIds = valuesMap
.computeIfAbsent(keyPair.getKey(), i -> new HashMap<>()).keySet();
return keyPair.getValue().stream() //
.filter(i -> !buildContextExistIds.contains(i)) //
.filter(i -> !valueMapExistIds.contains(i)) //
.collect(toSet());
}
private Map filterValueMap(KeyPair> keyPair,
B buildContext) {
Map buildContextData = buildContext.getData(keyPair.getKey());
return keyPair.getValue().entrySet().stream()
.filter(e -> !buildContextData.containsKey(e.getKey()))
.collect(toMap(Entry::getKey, Entry::getValue));
}
/**
* use {@link #extractId} or {@link #extractValue}
*/
@Deprecated
public OnBuilder on(Class type) {
return new OnBuilder<>(type);
}
/**
* use {@link #valueFromSelf}
*/
@Deprecated
public SimpleModelBuilder self(Class type, Function idExtractor) {
SimpleModelBuilder.OnBuilder onBuilder = new OnBuilder<>(type);
return onBuilder.new ExtractingValue(i -> i).id(idExtractor).to(type);
}
/**
* use {@link #buildValue} or {@link #buildValueTo}
*/
@Deprecated
public BuildingBuilder build(Object idNamespace) {
return new BuildingBuilder(idNamespace);
}
/**
* use {@link #buildValue} or {@link #buildValueTo}
*/
@Deprecated
public SimpleModelBuilder build(Object idNamespace,
BiFunction, Map> valueBuilder) {
return build(idNamespace).by(valueBuilder).to(idNamespace);
}
/**
* use {@link #buildValue} or {@link #buildValueTo}
*/
@Deprecated
public SimpleModelBuilder build(Object idNamespace,
Function, Map> valueBuilder) {
return build(idNamespace).by(valueBuilder).to(idNamespace);
}
/**
* use {@link #lazyBuild}
*/
@SuppressWarnings("rawtypes")
@Deprecated
public SimpleModelBuilder lazy(Lazy lazy) {
lazyBuilders.put(lazy.targetNamespace(), buildContext -> (Map) ((BiFunction) lazy.builder())
.apply(buildContext, buildContext.getData(lazy.sourceNamespace()).keySet()));
return this;
}
public SimpleModelBuilder valueFromSelf(Class type, Function idExtractor) {
self(type, idExtractor);
return this;
}
public SimpleModelBuilder extractId(Class type, Function idExtractor,
Object toIdNamespace) {
on(type).id(idExtractor).to(toIdNamespace);
return this;
}
public SimpleModelBuilder extractValue(Class type,
Function valueExtractor, Function idExtractor,
Object toValueNamespace) {
on(type).value(valueExtractor).id(idExtractor).to(toValueNamespace);
return this;
}
public SimpleModelBuilder buildValue(Object idNamespace,
Function, Map> valueBuilder) {
build(idNamespace, valueBuilder);
return this;
}
public SimpleModelBuilder buildValue(Object idNamespace,
BiFunction, Map> valueBuilder) {
build(idNamespace, valueBuilder);
return this;
}
public SimpleModelBuilder buildValueTo(Object idNamespace,
Function, Map> valueBuilder, Object toValueNamespace) {
build(idNamespace).by(valueBuilder).to(toValueNamespace);
return this;
}
public SimpleModelBuilder buildValueTo(Object idNamespace,
BiFunction, Map> valueBuilder, Object toValueNamespace) {
build(idNamespace).by(valueBuilder).to(toValueNamespace);
return this;
}
public SimpleModelBuilder lazyBuild(Object sourceNamespace,
Function, Map> builder, Object targetNamespace) {
lazy(LazyBuilder.on(sourceNamespace, builder, targetNamespace));
return this;
}
public SimpleModelBuilder lazyBuild(Object sourceNamespace,
BiFunction, Map> builder, Object targetNamespace) {
lazy(LazyBuilder.on(sourceNamespace, builder, targetNamespace));
return this;
}
private Set> getAllSuperTypes(Class> iface) {
Set> classes = new HashSet<>();
classes.add(iface);
classes.addAll(getAllInterfaces(iface));
classes.addAll(getAllSuperclasses(iface));
return classes;
}
private Stream stream(Iterable iterable) {
return StreamSupport.stream(
spliteratorUnknownSize(iterable.iterator(), (NONNULL | IMMUTABLE | ORDERED)),
false);
}
// builder.onLazy(UserCache.class).fromId(ids->dao.build(ids)).to("test");
@Override
public String toString() {
return reflectionToString(this, SHORT_PREFIX_STYLE);
}
interface Lazy {
Object sourceNamespace();
Object targetNamespace();
BiFunction, ?, ?> builder();
}
private static final class KeyPair implements Entry {
private final Object key;
private final V value;
private KeyPair(Object key, V value) {
this.key = key;
this.value = value;
}
public Object getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(V value) {
throw new UnsupportedOperationException();
}
}
@Deprecated
public final class OnBuilder {
private final Class objType;
private OnBuilder(Class objType) {
this.objType = objType;
}
public ExtractingId id(Function idExtractor) {
return new ExtractingId(idExtractor);
}
public ExtractingValue value(Function valueExtractor) {
return new ExtractingValue(valueExtractor);
}
public ExtractingValue value(Function> valueExtractor,
Function idExtractor) {
return new ExtractingValue(valueExtractor).id(idExtractor);
}
public final class ExtractingValue {
private final Function valueExtractor;
private Function idExtractor;
private ExtractingValue(Function valueExtractor) {
this.valueExtractor = (Function) valueExtractor;
}
public ExtractingValue id(Function idExtractor) {
this.idExtractor = (Function) idExtractor;
return this;
}
public SimpleModelBuilder to(Object valueNamespace) {
valueExtractors.put(objType, obj -> {
Object rawValue = valueExtractor.apply((E) obj);
Map value;
if (rawValue == null) {
value = emptyMap();
} else {
if (idExtractor != null) {
if (rawValue instanceof Iterable) {
value = stream((Iterable) rawValue)
.collect(toMap(idExtractor::apply, identity()));
} else {
value = singletonMap(idExtractor.apply(rawValue), rawValue);
}
} else {
if (rawValue instanceof Map) {
value = (Map) rawValue;
} else {
logger.warn("invalid value extractor for:{}->{}", obj, rawValue);
value = emptyMap();
}
}
}
return new KeyPair<>(valueNamespace, value);
});
cachedValueExtractors.clear();
return SimpleModelBuilder.this;
}
}
public final class ExtractingId {
private final Function idExtractor;
private ExtractingId(Function idExtractor) {
this.idExtractor = idExtractor;
}
public SimpleModelBuilder to(Object idNamespace) {
idExtractors.put(objType, obj -> {
Object rawId = idExtractor.apply((E) obj);
Set ids;
if (rawId == null) {
ids = emptySet();
} else {
if (rawId instanceof Iterable) {
ids = stream((Iterable) rawId).collect(toSet());
} else {
ids = singleton(rawId);
}
}
return new KeyPair<>(idNamespace, ids);
});
cachedIdExtractors.clear();
return SimpleModelBuilder.this;
}
}
}
@Deprecated
public final class BuildingBuilder {
private final Object idNamespace;
private BuildingBuilder(Object idNamespace) {
this.idNamespace = idNamespace;
}
@SuppressWarnings("rawtypes")
public BuildingValue by(Function, Map> valueBuilder) {
return new BuildingValue<>((c, ids) -> (Map) valueBuilder.apply(ids));
}
@SuppressWarnings("rawtypes")
public BuildingValue by(BiFunction, Map> valueBuilder) {
return new BuildingValue<>((BiFunction) valueBuilder);
}
public final class BuildingValue {
private final BiFunction, Map> valueBuilderFunction;
private BuildingValue(
BiFunction, Map> valueBuilderFunction) {
this.valueBuilderFunction = valueBuilderFunction;
}
@SuppressWarnings("rawtypes")
public SimpleModelBuilder to(Object valueNamespace) {
valueBuilders.put(idNamespace, new KeyPair(valueNamespace, valueBuilderFunction));
return SimpleModelBuilder.this;
}
}
}
}