|
@@ -0,0 +1,282 @@
|
|
|
|
|
+package net.ranides.assira.collection.prototype;
|
|
|
|
|
+
|
|
|
|
|
+import lombok.experimental.UtilityClass;
|
|
|
|
|
+import net.ranides.assira.collection.FlatCollection;
|
|
|
|
|
+import net.ranides.assira.reflection.IClass;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.Collection;
|
|
|
|
|
+import java.util.HashSet;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+import java.util.NoSuchElementException;
|
|
|
|
|
+import java.util.Optional;
|
|
|
|
|
+import java.util.Set;
|
|
|
|
|
+import java.util.function.Consumer;
|
|
|
|
|
+import java.util.function.Function;
|
|
|
|
|
+import java.util.function.Predicate;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+
|
|
|
|
|
+@UtilityClass
|
|
|
|
|
+public class AbstractPrototype {
|
|
|
|
|
+
|
|
|
|
|
+ public static abstract class DeclaredMap implements PrototypeMap {
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public PrototypeMap declared() {
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Optional<PrototypeEntry<?>> entry(String key) {
|
|
|
|
|
+ if(has(key)) {
|
|
|
|
|
+ return Optional.of(PrototypeEntry.of(this, key));
|
|
|
|
|
+ }
|
|
|
|
|
+ return Optional.empty();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public <V> Optional<PrototypeEntry<V>> entry(PrototypeField<V> key) {
|
|
|
|
|
+ if(has(key)) {
|
|
|
|
|
+ return Optional.of(PrototypeEntry.of(this, key));
|
|
|
|
|
+ }
|
|
|
|
|
+ return Optional.empty();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Collection<PrototypeEntry<?>> entries() {
|
|
|
|
|
+ return keys().stream().map(key -> PrototypeEntry.of(this, key)).collect(Collectors.toList());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @SuppressWarnings({"unchecked", "rawtypes"})
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public <V> Collection<PrototypeEntry<V>> entries(IClass<V> type) {
|
|
|
|
|
+ return (Collection)entries()
|
|
|
|
|
+ .stream()
|
|
|
|
|
+ .filter(e -> e.type().isSubclass(type))
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static abstract class BaseMap implements PrototypeMap {
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Optional<PrototypeEntry<?>> entry(String key) {
|
|
|
|
|
+ return apply(scope -> scope.declared().entry(key));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public <V> Optional<PrototypeEntry<V>> entry(PrototypeField<V> key) {
|
|
|
|
|
+ return apply(scope -> scope.declared().entry(key));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Set<Object> keys() {
|
|
|
|
|
+ Set<Object> set = new HashSet<>();
|
|
|
|
|
+ accept(scope -> set.addAll(scope.declared().keys()));
|
|
|
|
|
+ return set;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Collection<PrototypeEntry<?>> entries() {
|
|
|
|
|
+ FlatCollection<PrototypeEntry<?>> flat = new FlatCollection<>();
|
|
|
|
|
+ accept(scope -> flat.join(scope.declared().entries()));
|
|
|
|
|
+ return flat;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public <V> Collection<PrototypeEntry<V>> entries(IClass<V> type) {
|
|
|
|
|
+ FlatCollection<PrototypeEntry<V>> flat = new FlatCollection<>();
|
|
|
|
|
+ accept(scope -> flat.join(scope.declared().entries(type)));
|
|
|
|
|
+ return flat;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean has(String key) {
|
|
|
|
|
+ return test(scope -> scope.declared().has(key));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean has(PrototypeField<?> key) {
|
|
|
|
|
+ return test(scope -> scope.declared().has(key));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Optional<Object> get(String key) {
|
|
|
|
|
+ return apply(scope -> scope.declared().get(key));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public <V> Optional<V> get(PrototypeField<V> key) {
|
|
|
|
|
+ return apply(scope -> scope.declared().get(key));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean set(String key, Object value) {
|
|
|
|
|
+ return test(scope -> scope.declared().has(key) && scope.declared().set(key, value)) || declared().set(key, value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public <V> boolean set(PrototypeField<V> key, V value) {
|
|
|
|
|
+ return test(scope -> scope.declared().has(key) && scope.declared().set(key, value)) || declared().set(key, value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected void accept(Consumer<PrototypeMap> consumer) {
|
|
|
|
|
+ for(PrototypeMap scope = this; scope != null; scope = scope.parent()) {
|
|
|
|
|
+ consumer.accept(scope);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected <T> Optional<T> apply(Function<PrototypeMap, Optional<T>> function) {
|
|
|
|
|
+ for(PrototypeMap scope = this; scope != null; scope = scope.parent()) {
|
|
|
|
|
+ Optional<T> out = function.apply(scope);
|
|
|
|
|
+ if(out.isPresent()) {
|
|
|
|
|
+ return out;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return Optional.empty();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected boolean test(Predicate<PrototypeMap> predicate) {
|
|
|
|
|
+ for(PrototypeMap scope = this; scope != null; scope = scope.parent()) {
|
|
|
|
|
+ if(predicate.test(scope)) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static final class SimpleEntry<V> implements PrototypeEntry<V> {
|
|
|
|
|
+ private final PrototypeMap scope;
|
|
|
|
|
+ private final PrototypeField<V> field;
|
|
|
|
|
+ private final IClass<V> type;
|
|
|
|
|
+ private final String name;
|
|
|
|
|
+ private final V value;
|
|
|
|
|
+
|
|
|
|
|
+ public SimpleEntry(PrototypeMap scope, PrototypeField<V> field, V value) {
|
|
|
|
|
+ this.scope = scope;
|
|
|
|
|
+ this.field = field;
|
|
|
|
|
+ this.type = field.type();
|
|
|
|
|
+ this.name = field.name();
|
|
|
|
|
+ this.value = value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public SimpleEntry(PrototypeMap scope, IClass<V> type, String name, V value) {
|
|
|
|
|
+ this.scope = scope;
|
|
|
|
|
+ this.field = PrototypeField.of(type, name);
|
|
|
|
|
+ this.type = field.type();
|
|
|
|
|
+ this.name = field.name();
|
|
|
|
|
+ this.value = value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public PrototypeMap scope() {
|
|
|
|
|
+ return scope;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public PrototypeField<V> key() {
|
|
|
|
|
+ return field;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public IClass<V> type() {
|
|
|
|
|
+ return type;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String name() {
|
|
|
|
|
+ return name;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public V get() {
|
|
|
|
|
+ return value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean set(V value) {
|
|
|
|
|
+ throw new UnsupportedOperationException("SimpleEntry is immutable");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static final class NameEntry implements PrototypeEntry<Object> {
|
|
|
|
|
+ private final PrototypeMap scope;
|
|
|
|
|
+ private final String name;
|
|
|
|
|
+
|
|
|
|
|
+ public NameEntry(PrototypeMap scope, String name) {
|
|
|
|
|
+ this.scope = scope.declared();
|
|
|
|
|
+ this.name = name;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public PrototypeMap scope() {
|
|
|
|
|
+ return scope;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public PrototypeField<Object> key() {
|
|
|
|
|
+ return PrototypeField.of(name);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public IClass<Object> type() {
|
|
|
|
|
+ return IClass.OBJECT;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String name() {
|
|
|
|
|
+ return name;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Object get() {
|
|
|
|
|
+ return scope.get(name).orElseThrow(NoSuchElementException::new);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean set(Object value) {
|
|
|
|
|
+ return scope.set(name, value);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static final class FieldEntry<V> implements PrototypeEntry<V> {
|
|
|
|
|
+ private final PrototypeMap scope;
|
|
|
|
|
+ private final PrototypeField<V> field;
|
|
|
|
|
+
|
|
|
|
|
+ public FieldEntry(PrototypeMap scope, PrototypeField<V> field) {
|
|
|
|
|
+ this.scope = scope.declared();
|
|
|
|
|
+ this.field = field;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public PrototypeMap scope() {
|
|
|
|
|
+ return scope;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public PrototypeField<V> key() {
|
|
|
|
|
+ return field;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public IClass<V> type() {
|
|
|
|
|
+ return field.type();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String name() {
|
|
|
|
|
+ return field.name();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public V get() {
|
|
|
|
|
+ return scope.get(field).orElseThrow(NoSuchElementException::new);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean set(V value) {
|
|
|
|
|
+ return scope.set(field, value);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|