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

org.dbflute.helper.StringKeyMap Maven / Gradle / Ivy

There is a newer version: 1.2.8
Show newest version
/*
 * Copyright 2014-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.dbflute.helper;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author jflute
 * @param  The type of value.
 */
public class StringKeyMap implements Map, Serializable {

    // ===================================================================================
    //                                                                          Definition
    //                                                                          ==========
    /** The serial version UID for object serialization. (Default) */
    private static final long serialVersionUID = 1L;

    // ===================================================================================
    //                                                                           Attribute
    //                                                                           =========
    protected final Map _searchMap;

    // these maps always have same size of search map if it's valid
    protected final Map _plainMap; // invalid if concurrent
    protected final Map _searchPlainKeyMap; // same life-cycle as plainMap

    protected boolean _flexible;

    // ===================================================================================
    //                                                                         Constructor
    //                                                                         ===========
    protected StringKeyMap(boolean flexible, boolean order, boolean concurrent) {
        if (order && concurrent) {
            String msg = "The 'order' and 'concurrent' should not be both true at the same time!";
            throw new IllegalStateException(msg);
        }
        _flexible = flexible;
        if (concurrent) {
            _searchMap = newConcurrentHashMap();
            _plainMap = null; // invalid if concurrent
            _searchPlainKeyMap = null; // same life-cycle as plainMap
        } else {
            if (order) {
                _searchMap = newLinkedHashMap();
                _plainMap = newLinkedHashMap();
            } else {
                _searchMap = newHashMap();
                _plainMap = newHashMap();
            }
            _searchPlainKeyMap = newHashMap();
        }
    }

    /**
     * Create The map of string key as case insensitive. 
* You can set null key and value. And plain keys to be set is kept. * @param The type of value. * @return The map of string key as case insensitive. (NotNull) */ public static StringKeyMap createAsCaseInsensitive() { return new StringKeyMap(false, false, false); } /** * Create The map of string key as case insensitive and concurrent.
* You cannot set null key and value. And plain keys to be set is NOT kept. * @param The type of value. * @return The map of string key as case insensitive and concurrent. (NotNull) */ public static StringKeyMap createAsCaseInsensitiveConcurrent() { return new StringKeyMap(false, false, true); } /** * Create The map of string key as case insensitive and ordered.
* You can set null key and value. And plain keys to be set is kept. * @param The type of value. * @return The map of string key as case insensitive and ordered. (NotNull) */ public static StringKeyMap createAsCaseInsensitiveOrdered() { return new StringKeyMap(false, true, false); } /** * Create The map of string key as flexible.
* You can set null key and value. And plain keys to be set is kept. * @param The type of value. * @return The map of string key as flexible. (NotNull) */ public static StringKeyMap createAsFlexible() { return new StringKeyMap(true, false, false); } /** * Create The map of string key as flexible and concurrent.
* You cannot set null key and value. And plain keys to be set is NOT kept. * @param The type of value. * @return The map of string key as flexible and concurrent. (NotNull) */ public static StringKeyMap createAsFlexibleConcurrent() { return new StringKeyMap(true, false, true); } /** * Create The map of string key as flexible and ordered.
* You can set null key and value. And plain keys to be set is kept. * @param The type of value. * @return The map of string key as flexible and ordered. (NotNull) */ public static StringKeyMap createAsFlexibleOrdered() { return new StringKeyMap(true, true, false); } // =================================================================================== // Map Emulator // ============ // ----------------------------------------------------- // Key Related // ----------- public VALUE get(Object key) { final String stringKey = convertStringKey(key); return _searchMap.get(stringKey); } public VALUE put(String key, VALUE value) { final String stringKey = convertStringKey(key); if (_plainMap != null) { // non thread safe final String plainKey = generatePlainKey(key, stringKey); _plainMap.put(plainKey, value); _searchPlainKeyMap.put(stringKey, plainKey); } return _searchMap.put(stringKey, value); } public VALUE remove(Object key) { final String stringKey = convertStringKey(key); if (_plainMap != null) { // non thread safe final String plainKey = generatePlainKey(key, stringKey); _plainMap.remove(plainKey); _searchPlainKeyMap.remove(stringKey); } return _searchMap.remove(stringKey); } protected String generatePlainKey(Object key, String stringKey) { final String plainKey = _searchPlainKeyMap.get(stringKey); return (plainKey != null ? plainKey : (key != null ? key.toString() : null)); } public final void putAll(Map map) { final Set entrySet = map.entrySet(); for (Object entryObj : entrySet) { @SuppressWarnings("unchecked") final Entry entry = (Entry) entryObj; put(entry.getKey(), entry.getValue()); } } public boolean containsKey(Object key) { final String stringKey = convertStringKey(key); return _searchMap.containsKey(stringKey); } // ----------------------------------------------------- // Delegate // -------- public void clear() { if (_plainMap != null) { _plainMap.clear(); _searchPlainKeyMap.clear(); } _searchMap.clear(); } public int size() { return _searchMap.size(); } public boolean isEmpty() { return _searchMap.isEmpty(); } public Set keySet() { if (_plainMap != null) { return _plainMap.keySet(); } return _searchMap.keySet(); } public Collection values() { return _searchMap.values(); } public boolean containsValue(Object obj) { return _searchMap.containsValue(obj); } public Set> entrySet() { if (_plainMap != null) { return _plainMap.entrySet(); } return _searchMap.entrySet(); } // =================================================================================== // Original Utility // ================ public boolean equalsUnderCharOption(StringKeyMap map) { // ignores ordered if (map == null) { return false; } if (size() != map.size()) { return false; } final Set> entrySet = map.entrySet(); for (Entry entry : entrySet) { if (!containsKey(entry.getKey())) { return false; } final VALUE value = get(entry.getKey()); if (value != null) { if (!value.equals(entry.getValue())) { return false; } } else { if (entry.getValue() != null) { return false; } } } return true; } // =================================================================================== // Key Converter // ============= protected String convertStringKey(Object key) { if (!(key instanceof String)) { return null; } return toLowerCase(removeConnector((String) key)); } protected String removeConnector(String value) { if (value == null) { return null; } if (_flexible) { // both side quotations if (isSingleQuoted(value)) { value = unquoteSingle(value); } else if (isDoubleQuoted(value)) { value = unquoteDouble(value); } // a main target mark when flexible value = replace(value, "_", ""); // non-compilable marks in Java value = replace(value, "-", ""); value = replace(value, " ", ""); } return value; } protected String toLowerCase(String value) { return value.toLowerCase(); } // =================================================================================== // General Helper // ============== // copied from DBFlute's utilities for independence of this class protected static String replace(String str, String fromStr, String toStr) { StringBuilder sb = null; // lazy load int pos = 0; int pos2 = 0; do { pos = str.indexOf(fromStr, pos2); if (pos2 == 0 && pos < 0) { // first loop and not found return str; // without creating StringBuilder } if (sb == null) { sb = new StringBuilder(); } if (pos == 0) { sb.append(toStr); pos2 = fromStr.length(); } else if (pos > 0) { sb.append(str.substring(pos2, pos)); sb.append(toStr); pos2 = pos + fromStr.length(); } else { // (pos < 0) second or after loop only sb.append(str.substring(pos2)); return sb.toString(); } } while (true); } protected static boolean isSingleQuoted(String str) { return str.length() > 1 && str.startsWith("'") && str.endsWith("'"); } protected static boolean isDoubleQuoted(String str) { return str.length() > 1 && str.startsWith("\"") && str.endsWith("\""); } protected static String unquoteSingle(String str) { if (!isSingleQuoted(str)) { return str; } return str.substring("'".length(), str.length() - "'".length()); } protected static String unquoteDouble(String str) { if (!isDoubleQuoted(str)) { return str; } return str.substring("\"".length(), str.length() - "\"".length()); } protected static ConcurrentHashMap newConcurrentHashMap() { return new ConcurrentHashMap(); } protected static HashMap newLinkedHashMap() { return new LinkedHashMap(); } protected static HashMap newHashMap() { return new HashMap(); } // =================================================================================== // Basic Override // ============== @Override public boolean equals(Object obj) { if (obj instanceof StringKeyMap) { @SuppressWarnings("unchecked") final StringKeyMap map = (StringKeyMap) obj; if (size() != map.size()) { return false; } final Map myMap = _plainMap != null ? _plainMap : _searchMap; final Map yourMap = map._plainMap != null ? map._plainMap : map._searchMap; return myMap.equals(yourMap); } else if (obj instanceof Map) { @SuppressWarnings("unchecked") final Map map = (Map) obj; if (size() != map.size()) { return false; } return _plainMap != null ? _plainMap.equals(map) : _searchMap.equals(map); } else { return false; } } @Override public int hashCode() { return _plainMap != null ? _plainMap.hashCode() : _searchMap.hashCode(); } @Override public String toString() { return _plainMap != null ? _plainMap.toString() : _searchMap.toString(); } }