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

org.kitteh.irc.client.library.util.CIKeyMap Maven / Gradle / Ivy

/*
 * * Copyright (C) 2013-2023 Matt Baxter https://kitteh.org
 *
 * 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 org.kitteh.irc.client.library.util;

import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.kitteh.irc.client.library.Client;
import org.kitteh.irc.client.library.feature.CaseMapping;

import java.util.AbstractMap;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * A thread-safe hash map with case insensitive keys tied to {@link Client}'s
 * {@link CaseMapping}. Note that some methods do not behave like all maps.
 */
public class CIKeyMap implements Map {
    private final Client client;
    private CaseMapping lastCaseMapping;
    private final Map> map = new ConcurrentHashMap<>();

    /**
     * Constructs a map tied to a client.
     *
     * @param client the client to which this map is tied
     */
    public CIKeyMap(@NonNull Client client) {
        this.client = Sanity.nullCheck(client, "Client");
    }

    /**
     * Converts a given input to lower case based on the current {@link
     * CaseMapping}.
     *
     * @param input input to convert
     * @return lower cased input
     */
    protected final synchronized @NonNull String toLowerCase(@NonNull String input) {
        CaseMapping caseMapping = this.client.getServerInfo().getCaseMapping();
        if (caseMapping != this.lastCaseMapping) {
            Set> entrySet = this.entrySet();
            this.lastCaseMapping = caseMapping;
            this.map.clear();
            entrySet.forEach(entry -> this.put(entry.getKey(), entry.getValue()));
        }
        return caseMapping.toLowerCase(input);
    }

    @Override
    public int size() {
        return this.map.size();
    }

    @Override
    public boolean isEmpty() {
        return this.map.isEmpty();
    }

    @Override
    public boolean containsKey(@Nullable Object key) {
        return (key instanceof String) && this.map.containsKey(this.toLowerCase((String) key));
    }

    @Override
    public boolean containsValue(@Nullable Object value) {
        for (Pair pair : this.map.values()) {
            if ((value == null) ? (pair.getRight() == null) : value.equals(pair.getRight())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public @Nullable Value get(@Nullable Object key) {
        if (key instanceof String) {
            Pair pair = this.map.get(this.toLowerCase((String) key));
            return (pair == null) ? null : pair.getRight();
        }
        return null;
    }

    @Override
    public @Nullable Value put(@NonNull String key, @Nullable Value value) {
        Sanity.nullCheck(key, "Key");
        Pair pair = this.map.put(this.toLowerCase(key), new Pair<>(key, value));
        return (pair == null) ? null : pair.getRight();
    }

    @Override
    public @Nullable Value remove(@Nullable Object key) {
        if (key instanceof String) {
            Pair pair = this.map.remove(this.toLowerCase((String) key));
            return (pair == null) ? null : pair.getRight();
        }
        return null;
    }

    @Override
    public void putAll(@NonNull Map m) {
        Sanity.nullCheck(m, "Map");
        m.forEach(this::put);
    }

    @Override
    public void clear() {
        this.map.clear();
    }

    /**
     * Gets an UNCHANGING representation of the keys.
     *
     * @return set of keys
     */
    @Override
    public @NonNull Set keySet() {
        return this.map.values().stream().map(Pair::getLeft).collect(Collectors.toSet());
    }

    /**
     * Gets an UNCHANGING representation of the values.
     *
     * @return list of values
     */
    @Override
    public @NonNull Collection values() {
        return this.map.values().stream().map(Pair::getRight).collect(Collectors.toList());
    }

    /**
     * Gets an UNCHANGING representation of the entries.
     *
     * @return set of entries
     */
    @Override
    public @NonNull Set> entrySet() {
        return this.map.values().stream().map(pair -> new AbstractMap.SimpleImmutableEntry<>(pair.getLeft(), pair.getRight())).collect(Collectors.toSet());
    }

    @Override
    public @NonNull String toString() {
        return new ToStringer(this).add("client", this.client).add("map", this.map.values().stream().collect(Collectors.toMap(Pair::getLeft, Pair::getRight))).toString();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy