Package com.foundationdb.server.collation

Source Code of com.foundationdb.server.collation.AkCollatorFactory

/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.foundationdb.server.collation;

import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import com.foundationdb.server.error.InvalidCollationSchemeException;
import com.foundationdb.server.error.UnsupportedCollationException;
import com.ibm.icu.text.Collator;
import com.ibm.icu.text.RuleBasedCollator;

/**
* Provides Collator instances. Collator is not threadsafe, so this class keeps
* SoftReferences to thread-local instances.
*
* @author peter
*
*/
public class AkCollatorFactory {

    public final static int UCS_BINARY_ID = 0;

    public final static String UCS_BINARY = "UCS_BINARY";

    public final static AkCollator UCS_BINARY_COLLATOR = new AkCollatorBinary();

    private final static Map<String, Collator> sourceMap = new HashMap<>();

    private final static Map<String, SoftReference<AkCollator>> collatorMap = new ConcurrentHashMap<>();

    private final static Map<Integer, SoftReference<AkCollator>> collationIdMap = new ConcurrentHashMap<>();

    private final static Map<String, Integer> schemeToIdMap = new ConcurrentHashMap<>();

    private final static AtomicInteger collationIdGenerator = new AtomicInteger(UCS_BINARY_ID);

    private volatile static Mode mode = Mode.STRICT;

    /*
     * Note: used only in a single-threaded unit test.
     */
    private static int cacheHits;

    public enum Mode {
        STRICT, LOOSE, DISABLED
    }

    /**
     * Set factory to one of three modes specified by case-insensitive string:
     * <dl>
     * <dt>disabled</dt>
     * <dd>always uses UCS_BINARY_COLLATOR</dd>
     * <dt>strict</dt>
     * <dd>disallows unimplemented collators</dd>
     * <dt>loose</dt>
     * <dd>returns UCS_BINARY_COLLATOR for any unrecognized name</dd>
     * </dl
     */
    public static void setCollationMode(String modeString) {
        try {
            setCollationMode(Mode.valueOf(modeString.toUpperCase()));
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Collation mode must be STRICT, LOOSE or DISABLED: " + modeString);
        }
    }

    public static void setCollationMode(Mode m) {
        if (m == null) {
            throw new NullPointerException();
        }
        synchronized (collatorMap) {
            collationIdMap.clear();
            collatorMap.clear();
            mode = m;
        }
    }

    public static Mode getCollationMode() {
        return mode;
    }

    public static AkCollator getAkCollator(final String scheme) {
        if (mode == Mode.DISABLED || scheme == null) {
            return UCS_BINARY_COLLATOR;
        }

        if (scheme.equalsIgnoreCase(UCS_BINARY)) {
            return mapToBinary(scheme);
        }

        SoftReference<AkCollator> ref = collatorMap.get(scheme);
        if (ref != null) {
            AkCollator akCollator = ref.get();
            if (akCollator != null) {
                cacheHits++;
                return akCollator;
            }
        }

        synchronized (collatorMap) {

            Integer collationId = schemeToIdMap.get(scheme);
            if (collationId == null) {
                collationId = collationIdGenerator.incrementAndGet();
            }

            final AkCollator akCollator;
            try {
                akCollator = new AkCollatorICU(scheme, collationId);
            } catch (InvalidCollationSchemeException | UnsupportedCollationException e) {
                if (mode == Mode.LOOSE) {
                    return mapToBinary(scheme);
                }
                throw e;
            }

            ref = new SoftReference<>(akCollator);
            collatorMap.put(scheme, ref);
            collationIdMap.put(collationId, ref);
            schemeToIdMap.put(scheme, collationId);

            return akCollator;
        }
    }

    public static AkCollator getAkCollator(final int collatorId) {
        final SoftReference<AkCollator> ref = collationIdMap.get(collatorId);
        AkCollator collator = (ref == null ? null : ref.get());
        if (collator == null) {
            if (collatorId == UCS_BINARY_ID) {
                return UCS_BINARY_COLLATOR;
            }
            else {
                String scheme = getKeyByValue(schemeToIdMap, collatorId);
                if (scheme == null) return null;
                return getAkCollator(scheme);
            }
        } else {
            cacheHits++;
        }

        return collator;
    }

    public static <T, E> T getKeyByValue(Map<T, E> map, E value) {
        for (Entry<T, E> entry : map.entrySet()) {
            if (value.equals(entry.getValue())) {
                return entry.getKey();
            }
        }
        return null;
    }

    /**
     * Construct an actual ICU Collator given a collation specifier. The
     * result is a Collator that must be use in a thread-private manner.
     */
    static synchronized Collator forScheme(final CollationSpecifier specifier) {
        RuleBasedCollator collator = (RuleBasedCollator) sourceMap.get(specifier.toString());
        if (collator == null) {
            collator = specifier.createCollator();
            sourceMap.put(specifier.toString(), collator);
        }
        collator = collator.cloneAsThawed();
        return collator;
    }

    private static AkCollator mapToBinary(final String scheme) {
        collatorMap.put(scheme, new SoftReference<>(UCS_BINARY_COLLATOR));
        return UCS_BINARY_COLLATOR;
    }

    /**
     * Intended only for unit tests.
     *
     * @return Number of times either getAkCollator() method has returned a
     *         cached value.
     */
    static int getCacheHits() {
        return cacheHits;
    }
}
TOP

Related Classes of com.foundationdb.server.collation.AkCollatorFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.