mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-11-19 09:05:26 +01:00
DataService implementation from another branch
This commit is contained in:
parent
fc543e165c
commit
24b6669c7d
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Player Analytics (Plan).
|
||||||
|
*
|
||||||
|
* Plan is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Plan is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.djrapitops.plan;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for sourcing, mapping and consuming data.
|
||||||
|
* <p>
|
||||||
|
* The service is in charge of two data flows:
|
||||||
|
* - push, given to consumers
|
||||||
|
* - pull, obtained from sources
|
||||||
|
* <p>
|
||||||
|
* The mappers facilitate a one way type transformation if needed.
|
||||||
|
* <p>
|
||||||
|
* The interface is very abstract about how data is obtained,
|
||||||
|
* but here are my general ideas of where the abstraction is coming from.
|
||||||
|
* - push cause one or multiple consumers to modify stored data
|
||||||
|
* - pull cause one or multiple suppliers to fetch stored data
|
||||||
|
* - mappers are stateless type transformers in memory
|
||||||
|
* <p>
|
||||||
|
* Example use-case:
|
||||||
|
* - PlayerJoinEvent -> mapped to a generic event
|
||||||
|
* - that generic event is then consumed and mapped until the data is in a database.
|
||||||
|
* <p>
|
||||||
|
* - Some kind of data is wanted to place on a web page
|
||||||
|
* - It is requested and the suppliers and mappers give the wanted type of data.
|
||||||
|
* <p>
|
||||||
|
* Further research needed:
|
||||||
|
* - Can this limited type system represent types of data that need parameters
|
||||||
|
* (such as differentiate between two servers data)
|
||||||
|
*/
|
||||||
|
public interface DataService {
|
||||||
|
|
||||||
|
<M> DataService push(Class<M> type, M data);
|
||||||
|
|
||||||
|
<S> Optional<S> pull(Class<S> type);
|
||||||
|
|
||||||
|
<S, P> Optional<S> pull(Class<S> type, P parameter);
|
||||||
|
|
||||||
|
<A, B> B mapTo(Class<B> toType, A from);
|
||||||
|
|
||||||
|
default <S, P> Optional<S> pull(Class<S> type, Class<P> parameterType) {
|
||||||
|
return pull(type, () -> pull(parameterType).orElse(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
default <S, P> Optional<S> pull(Class<S> type, Supplier<P> parameter) {
|
||||||
|
return pull(type, parameter.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
<A, B> DataService registerMapper(Class<A> typeA, Class<B> typeB, Function<A, B> mapper);
|
||||||
|
|
||||||
|
<M> DataService registerConsumer(Class<M> type, Consumer<M> consumer);
|
||||||
|
|
||||||
|
<S> DataService registerSupplier(Class<S> type, Supplier<S> supplier);
|
||||||
|
|
||||||
|
<P, S> DataService registerSupplier(Class<S> type, Class<P> parameterType, Function<P, S> supplierWithParameter);
|
||||||
|
|
||||||
|
}
|
180
Plan/common/src/main/java/com/djrapitops/plan/DataSvc.java
Normal file
180
Plan/common/src/main/java/com/djrapitops/plan/DataSvc.java
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Player Analytics (Plan).
|
||||||
|
*
|
||||||
|
* Plan is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Plan is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.djrapitops.plan;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class DataSvc implements DataService {
|
||||||
|
|
||||||
|
private final MultiHashMap<Class, Mapper> mappers;
|
||||||
|
private final MultiHashMap<Class, Mapper> mappersReverse;
|
||||||
|
private final Map<Class, Supplier> suppliers;
|
||||||
|
private final Map<ClassPair, Function> suppliersWithParameter;
|
||||||
|
private final MultiHashMap<Class, Consumer> consumers;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DataSvc() {
|
||||||
|
mappers = new MultiHashMap<>();
|
||||||
|
mappersReverse = new MultiHashMap<>();
|
||||||
|
suppliers = new ConcurrentHashMap<>();
|
||||||
|
suppliersWithParameter = new ConcurrentHashMap<>();
|
||||||
|
consumers = new MultiHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A> DataService push(Class<A> type, A data) {
|
||||||
|
if (data == null) return this;
|
||||||
|
List<Mapper> mappers = this.mappers.get(type);
|
||||||
|
for (Mapper mapper : mappers) {
|
||||||
|
push(mapper.typeB, mapper.func.apply(data));
|
||||||
|
}
|
||||||
|
List<Consumer> consumers = this.consumers.get(type);
|
||||||
|
for (Consumer<A> consumer : consumers) {
|
||||||
|
consumer.accept(data);
|
||||||
|
}
|
||||||
|
if (mappers.isEmpty() && consumers.isEmpty()) {
|
||||||
|
System.out.println("WARN: Nothing consumed " + type);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A> Optional<A> pull(Class<A> type) {
|
||||||
|
Supplier present = this.suppliers.get(type);
|
||||||
|
if (present != null) return Optional.ofNullable((A) present.get());
|
||||||
|
|
||||||
|
List<Mapper> mappers = this.mappersReverse.get(type);
|
||||||
|
for (Mapper mapper : mappers) {
|
||||||
|
Optional found = pull(mapper.typeA)
|
||||||
|
.map(data -> mapper.func.apply(data));
|
||||||
|
if (found.isPresent()) return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("WARN: Nothing supplied " + type);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A, B> B mapTo(Class<B> toType, A from) {
|
||||||
|
List<Mapper> mappers = this.mappers.get(from.getClass());
|
||||||
|
for (Mapper mapper : mappers) {
|
||||||
|
if (mapper.typeB.equals(toType)) {
|
||||||
|
return toType.cast(mapper.func.apply(from));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO Figure out type mapping resolution when it needs more than one mapping
|
||||||
|
System.out.println("WARN: No mapper for " + from.getClass() + " -> " + toType);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A, B> DataService registerMapper(Class<A> typeA, Class<B> typeB, Function<A, B> mapper) {
|
||||||
|
Mapper<A, B> asWrapper = new Mapper<>(typeA, typeB, mapper);
|
||||||
|
// TODO Prevent two mappers for same 2 types with a data structure
|
||||||
|
mappers.putOne(typeA, asWrapper);
|
||||||
|
mappersReverse.putOne(typeB, asWrapper);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A> DataService registerConsumer(Class<A> type, Consumer<A> consumer) {
|
||||||
|
consumers.putOne(type, consumer);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A> DataService registerSupplier(Class<A> type, Supplier<A> supplier) {
|
||||||
|
suppliers.put(type, supplier);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <S, P> Optional<S> pull(Class<S> type, P parameter) {
|
||||||
|
if (parameter == null) return Optional.empty();
|
||||||
|
Function<P, S> function = suppliersWithParameter.get(new ClassPair<>(type, parameter.getClass()));
|
||||||
|
return function != null ? Optional.of(function.apply(parameter)) : Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <P, S> DataService registerSupplier(Class<S> type, Class<P> parameterType, Function<P, S> supplierWithParameter) {
|
||||||
|
suppliersWithParameter.put(new ClassPair<>(type, parameterType), supplierWithParameter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ClassPair<A, B> {
|
||||||
|
final Class<A> a;
|
||||||
|
final Class<B> b;
|
||||||
|
|
||||||
|
public ClassPair(Class<A> a, Class<B> b) {
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
ClassPair<?, ?> classPair = (ClassPair<?, ?>) o;
|
||||||
|
return a.equals(classPair.a) &&
|
||||||
|
b.equals(classPair.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class KeyValuePair<K, V> {
|
||||||
|
final K key;
|
||||||
|
final V value;
|
||||||
|
|
||||||
|
public KeyValuePair(K key, V value) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MultiHashMap<A, B> extends ConcurrentHashMap<A, List<B>> {
|
||||||
|
|
||||||
|
void putOne(A key, B value) {
|
||||||
|
List<B> values = getOrDefault(key, new ArrayList<>());
|
||||||
|
values.add(value);
|
||||||
|
put(key, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Mapper<A, B> {
|
||||||
|
Class<A> typeA;
|
||||||
|
Class<B> typeB;
|
||||||
|
Function<A, B> func;
|
||||||
|
|
||||||
|
public Mapper(Class<A> typeA, Class<B> typeB, Function<A, B> func) {
|
||||||
|
this.typeA = typeA;
|
||||||
|
this.typeB = typeB;
|
||||||
|
this.func = func;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.djrapitops.plan.modules;
|
package com.djrapitops.plan.modules;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.DataService;
|
||||||
|
import com.djrapitops.plan.DataSvc;
|
||||||
import com.djrapitops.plan.PlanPlugin;
|
import com.djrapitops.plan.PlanPlugin;
|
||||||
import com.djrapitops.plan.settings.config.ExtensionSettings;
|
import com.djrapitops.plan.settings.config.ExtensionSettings;
|
||||||
import com.djrapitops.plan.settings.config.PlanConfig;
|
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||||
@ -78,4 +80,10 @@ public class SystemObjectProvidingModule {
|
|||||||
return errorLogger;
|
return errorLogger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
DataService provideDataService(DataSvc dataService) {
|
||||||
|
return dataService;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user