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

functionalj.map.FuncMapDerived Maven / Gradle / Ivy

There is a newer version: 1.0.17
Show newest version
// ============================================================================
// Copyright (c) 2017-2021 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.map;

import static java.util.stream.Collectors.joining;

import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;

import functionalj.function.Func2;
import functionalj.list.FuncList;
import functionalj.map.MapAction.FilterBoth;
import functionalj.map.MapAction.FilterKey;
import functionalj.map.MapAction.Mapping;
import functionalj.map.MapAction.With;
import functionalj.stream.StreamPlus;
import lombok.val;


public class FuncMapDerived extends FuncMap {
    
    final Map map;
    
    private final MapAction action;
    
    FuncMapDerived(Map map, MapAction action) {
        this.map    = map;
        this.action = action;
    }
    
    public boolean isLazy() {
        return true;
    }
    
    public boolean isEager() {
        return false;
    }
    
    public FuncMap lazy() {
        return this;
    }
    public FuncMap eager() {
        return new ImmutableFuncMap(this, false);
    }
    
    private Stream> originalEntryStream() {
        Stream> stream
            = (map instanceof FuncMap)
            ? ((FuncMap)map).entries().stream()
            : map.entrySet().stream();
        return stream;
    }
    
    @SuppressWarnings("unchecked")
    StreamPlus> entryStream() {
        if (action instanceof With) {
            val with = (With)action;
            Stream> entries 
                    = originalEntryStream()
                        .filter  (e -> !Objects.equals(e.getKey(), with.key))
                        .map     (e -> (Map.Entry)e);
            Stream> combined = StreamPlus.concat(
                entries,
                Stream.of(FuncMap.Entry.of(with.key, with.value))
            );
            if (map instanceof TreeMap) {
                val comparator = ((TreeMap)map).comparator();
                combined = combined.sorted((a, b) -> {
                    val aKey = a.getKey();
                    val bKey = b.getKey();
                    return comparator.compare(aKey, bKey);
                });
            }
            return StreamPlus.from(combined);
        }
        if (action instanceof FilterKey) {
            val filter = (FilterKey)action;
            Stream> entries
                    =  originalEntryStream()
                        .filter  (e -> filter.keyCheck.test(e.getKey()))
                        .map     (e -> (Map.Entry)e);
            return StreamPlus.from(entries);
        }
        if (action instanceof FilterBoth) {
            val filter = (FilterBoth)action;
            val check  = (Predicate>)(e -> filter.check.test(e.getKey(), e.getValue()));
            Stream> entries
                    = originalEntryStream()
                       .map     (e -> (Map.Entry)e)
                       .filter  (e -> check.test(e));
            return StreamPlus.from(entries);
        }
        if (action instanceof Mapping) {
            val mapper = (Mapping)action;
            Stream> entries
                    = originalEntryStream()
                       .map(e -> {
                           val key = e.getKey();
                           val orgValue = e.getValue();
                           val newValue = mapper.mapper.apply(key, orgValue);
                           val newEntry = FuncMap.Entry.of(key, newValue);
                           return newEntry;
                       });
            return StreamPlus.from(entries);
        }
        
        val source 
                = originalEntryStream()
                .map(e -> (Map.Entry)e);
        return StreamPlus.from(source);
    }
    
    @Override
    @SuppressWarnings("unchecked")
    public int size() {
        if (action instanceof With) {
            val with = (With)action;
            if (map.containsKey(with.key))
                return map.size();
            
            return map.size() + 1;
        }
        if (action instanceof FilterKey) {
            val filter = (FilterKey)action;
            return (int)map.keySet().stream().filter(filter.keyCheck).count();
        }
        if (action instanceof FilterBoth) {
            val filter = (FilterBoth)action;
            val check  = (Predicate>)(e -> filter.check.test(e.getKey(), e.getValue()));
            return (int)originalEntryStream()
                    .filter(check)
                    .count();
        }
        return map.size();
    }
    @Override
    public boolean isEmpty() {
        if (action instanceof With)
            return false;
        
        // TODO - Find a faster way.
        return size() == 0;
    }
    
    public String toString() {
        return "{" +
                entryStream()
                    .map(each -> each.getKey() + ":" + each.getValue())
                    .collect(joining(", ")) +
                "}";
    }
    
    @Override
    public int hashCode() {
        int hashCode = entryStream()
                .mapToInt(each -> Objects.hashCode(each.getKey())*37 + Objects.hashCode(each.getValue()))
                .sum();
        return 43 + FuncMap.class.hashCode() + hashCode;
    }
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Map))
            return false;
        
        // TODO - not efficient
        // Try to use: entryStream()
        val thisMap = toImmutableMap();
        
        val keyExist = ((Predicate)((Map)o)::containsKey).negate();
        val hasMissingKey = thisMap.keys().anyMatch(keyExist);
        if (hasMissingKey)
            return false;
        
        val thatMap = ImmutableFuncMap.from((Map)o);
        if (thatMap.size() != thisMap.size())
            return false;
        
        val matchEntry = (Predicate>)(t -> Objects.equals(thatMap.get(t.getKey()), t.getValue()));
        val allMatchValue 
                = thisMap
                .entries()
                .allMatch(matchEntry);
        return allMatchValue;
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public boolean hasKey(Predicate keyCheck) {
        if (action instanceof With) {
            val with = (With)action;
            if (keyCheck.test(with.key))
                return true;
        }
        if (action instanceof FilterKey) {
            val filter = (FilterKey)action;
            return map
                    .keySet  ().stream()
                    .filter  (filter.keyCheck)
                    .anyMatch(keyCheck);
        }
        if (action instanceof FilterBoth) {
            val filter = (FilterBoth)action;
            val check  = (Predicate>)(e -> filter.check.test(e.getKey(), e.getValue()));
            return originalEntryStream()
                    .filter  (e -> check.test((Map.Entry)e))
                    .anyMatch(e -> keyCheck.test(e.getKey()));
        }
        
        return map
                .keySet().stream()
                .anyMatch(keyCheck);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public boolean hasKey(KEY key) {
        if (action instanceof With) {
            val with = (With)action;
            if (Objects.equals(key, with.key))
                return true;
        }
        if (action instanceof FilterKey) {
            val filter = (FilterKey)action;
            if (!filter.keyCheck.test(key))
                return false;
        }
        if (action instanceof FilterBoth) {
            val filter = (FilterBoth)action;
            val check  = (Predicate>)(e -> filter.check.test(e.getKey(), e.getValue()));
            return originalEntryStream()
                    .filter  (e -> check.test((Map.Entry)e))
                    .anyMatch(e -> Objects.equals(e.getKey(), key));
        }
        
        return map.containsKey(key);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public boolean containsKey(Object key) {
        return hasKey((KEY)key);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public boolean hasValue(Predicate valueCheck) {
        if (action instanceof With) {
            val with = (With)action;
            if (valueCheck.test(with.value))
                return true;
        }
        
        if (action instanceof FilterKey) {
            val filter = (FilterKey)action;
            return originalEntryStream()
                    .filter  (e -> filter.keyCheck.test(e.getKey()))
                    .anyMatch(e -> valueCheck.test((VALUE)e.getValue()));
        }
        if (action instanceof FilterBoth) {
            val filter = (FilterBoth)action;
            val check  = (Predicate>)(e -> filter.check.test(e.getKey(), e.getValue()));
            return originalEntryStream()
                    .filter  (e -> check.test((Map.Entry)e))
                    .anyMatch(e -> valueCheck.test((VALUE)e.getValue()));
        }
        if (action instanceof Mapping) {
            val mapping = (Mapping)action;
            val mpapper = mapping.mapper;
            return originalEntryStream()
                    .map     (e -> mpapper.apply(e.getKey(), e.getValue()))
                    .anyMatch(v -> valueCheck.test(v));
        }
        
        return map
                .values().stream()
                .map     (v -> ((VALUE)v))
                .anyMatch(v -> valueCheck.test(v));
    }
    
    @Override
    public boolean hasValue(VALUE value) {
        return hasValue(v -> Objects.equals(v, value));
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public boolean containsValue(Object value) {
        return hasValue((VALUE)value);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public Optional findBy(KEY key) {
        if (action instanceof With) {
            val with = (With)action;
            if (Objects.equals(key, with.key))
                return Optional.ofNullable(with.value);
        }
        if (action instanceof FilterKey) {
            val filter = (FilterKey)action;
            if (!filter.keyCheck.test(key))
                return Optional.empty();
        }
        if (action instanceof FilterBoth) {
            val filter = (FilterBoth)action;
            val check  = (Predicate>)(e -> filter.check.test(e.getKey(), e.getValue()));
            
            val hasKey = originalEntryStream()
                    .filter  (e -> check.test((Map.Entry)e))
                    .anyMatch(e -> Objects.equals(e.getKey(), key));
            if (!hasKey)
                return Optional.empty();
        }
        if (action instanceof Mapping) {
            val mapping = (Mapping)action;
            val mapper  = mapping.mapper;
            
            val orgValue = map.get(key);
            if (orgValue == null) {
                return Optional.empty();
            }
            
            val newValue = mapper.apply(key, orgValue);
            if (newValue == null) {
                return Optional.empty();
            }
            return Optional.of(newValue);
        }
        
        val value = map.get(key);
        return Optional
                .ofNullable((VALUE)value);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public VALUE get(Object keyObj) {
        KEY key = (KEY)keyObj;
        if (action instanceof With) {
            val with = (With)action;
            if (Objects.equals(key, with.key))
                return with.value;
        }
        if (action instanceof FilterKey) {
            val filter = (FilterKey)action;
            if (!filter.keyCheck.test((KEY)key))
                return null;
        }
        if (action instanceof FilterBoth) {
            val filter = (FilterBoth)action;
            val check  = (Predicate>)(e -> filter.check.test(e.getKey(), e.getValue()));
            
            val hasKey = originalEntryStream()
                    .filter  (e -> check.test((Map.Entry)e))
                    .anyMatch(e -> Objects.equals(e.getKey(), key));
            if (!hasKey)
                return null;
        }
        if (action instanceof Mapping) {
            val mapping = (Mapping)action;
            val mapper  = mapping.mapper;
            
            val orgValue = map.get(key);
            if (orgValue == null) {
                return null;
            }
            
            val newValue = mapper.apply(key, orgValue);
            if (newValue == null) {
                return null;
            }
            return newValue;
        }
        
        val value = (VALUE)map.get(key);
        return value;
    }
    
    @Override
    public VALUE getOrDefault(Object key, VALUE orElse) {
        VALUE v;
        return (((v = get(key)) != null) || containsKey(key))
            ? v
            : orElse;
    }
    
    @Override
    public FuncList select(Predicate keyPredicate) {
        return entryStream()
                .filter(Map.Entry::getKey, keyPredicate)
                .map   (Map.Entry::getValue)
                .toList();
    }
    
    @Override
    public FuncList> selectEntry(Predicate keyPredicate) {
        return entryStream()
                .filter(Map.Entry::getKey, keyPredicate)
                .map(e -> (Map.Entry)e)
                .toList();
    }
    
    @Override
    public FuncMap with(KEY key, VALUE value) {
        val action = new With(key, value);
        return new FuncMapDerived(this, action);
    }
    
    @Override
    public FuncMap withAll(Map entries) {
        // TODO: Find the way to do it
        val newMap = new HashMap<>(this.toMap());
        newMap.putAll(entries);
        return ImmutableFuncMap.from(newMap);
    }
    
    @Override
    public FuncMap exclude(KEY key) {
        val action = new FilterKey(k -> !Objects.equals(k, key));
        return new FuncMapDerived(this, action);
    }
    
    @Override
    public FuncMap filter(Predicate keyCheck) {
        val action = new FilterKey(keyCheck);
        return new FuncMapDerived(this, action);
    }
    
    @Override
    public FuncMap filter(BiPredicate entryCheck) {
        val action = new FilterBoth(entryCheck);
        return new FuncMapDerived(this, action);
    }
    
    @Override
    public FuncMap filterByEntry(Predicate> entryCheck) {
        val action = new FilterBoth((k, v) -> entryCheck.test(FuncMap.Entry.of(k,  v)));
        return new FuncMapDerived(this, action);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public FuncList keys() {
        if (action instanceof With) {
            val with   = (With)action;
            return FuncList.from(()->{
                StreamPlus stream = StreamPlus.concat(
                    map.keySet().stream()
                       .filter(k -> !Objects.equals(k, with.key)),
                   Stream.of(with.key)
                );
                if (map instanceof TreeMap) {
                    val treeMap    = (TreeMap)map;
                    val comparator = treeMap.comparator();
                    stream = stream.sorted(comparator);
                }
                return stream;
            });
        }
        if (action instanceof FilterKey) {
            val filter = (FilterKey)action;
            return FuncList.from(()->StreamPlus.from(map.keySet().stream().filter(filter.keyCheck)));
        }
        if (action instanceof FilterBoth) {
            val filter = (FilterBoth)action;
            val check  = (Predicate>)(e -> filter.check.test(e.getKey(), e.getValue()));
            return FuncList.from(()->{
                return StreamPlus.from(
                        originalEntryStream()
                                .filter  (e -> check.test((Map.Entry)e))
                                .map     (Map.Entry::getKey));
            });
        }
        
        return FuncList.from(()->StreamPlus.from( map.keySet().stream()));
    }
    
    @Override
    public FuncList values() {
        return FuncList.from(()->entryStream().map(Map.Entry::getValue));
    }
    
    @Override
    public Set keySet() {
        if (action != null)
            return keys().toSet();
        
        return map.keySet();
    }
    
    @Override
    public Set> entrySet() {
        return entryStream()
                .toSet();
    }
    @Override
    public FuncList> entries() {
        return FuncList.from(()->entryStream());
    }
    
    @Override
    public Map toMap() {
        return this;
    }
    
    @Override
    public ImmutableFuncMap toImmutableMap() {
        return ImmutableFuncMap.from(this);
    }
    
    @Override
    public FuncMap sorted() {
        val map = new TreeMap();
        entryStream()
            .forEach(e -> map.put(e.getKey(), e.getValue()));
        return new ImmutableFuncMap(map, isLazy());
    }
    
    @Override
    public FuncMap sorted(Comparator> comparator) {
        val map = new LinkedHashMap();
        this
        .entryStream()
        .sorted (comparator)
        .forEach(e -> map.put(e.getKey(), e.getValue()));
        return FuncMap.from(map);
    }
    
    @Override
    public FuncMap sortedByKey(Comparator comparator) {
        val map = new TreeMap(comparator);
        entryStream()
        .forEach(e -> map.put(e.getKey(), e.getValue()));
        return new ImmutableFuncMap(map, isLazy());
    }
    
    @Override
    public FuncMap sortedByValue(Comparator comparator) {
        return sorted((e1, e2)->comparator.compare(e1.getValue(), e2.getValue()));
    }
    
    @Override
    public  FuncMap mapEntry(BiFunction mapper) {
        val mapFunc = Func2.from(mapper);
        val mapping = new MapAction.Mapping(mapFunc);
        val mapped  = new FuncMapDerived(this, mapping);
        if (isEager()) {
            return mapped.toImmutableMap();
        }
        return mapped;
    }
    
    @Override
    public void forEach(BiConsumer action) {
        entryStream()
        .forEach(entry -> {
            val key   = entry.getKey();
            val value = entry.getValue();
            action.accept(key, value);
        });
    }
    
    @Override
    public void forEach(Consumer> action) {
        entryStream()
        .forEach(action);
    }
    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy