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

io.deephaven.engine.util.string.StringUtils Maven / Gradle / Ivy

There is a newer version: 0.36.1
Show newest version
/**
 * Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending
 */
package io.deephaven.engine.util.string;

import io.deephaven.hash.KeyedObjectKey;
import io.deephaven.base.string.cache.*;
import io.deephaven.configuration.Configuration;
import io.deephaven.engine.table.ColumnDefinition;
import io.deephaven.engine.table.TableDefinition;
import io.deephaven.engine.util.caching.C14nUtil;
import org.jetbrains.annotations.NotNull;

import java.io.Serializable;
import java.util.Arrays;

public class StringUtils implements Serializable {

    private static final int STRING_CACHE_SIZE = Configuration.getInstance().getInteger("StringUtils.cacheSize");

    // ------------------------------------------------------------------------------------------------------------------
    // A thread-safe (but not very concurrent) StringCache for use in Deephaven code that desires actual caching
    // ------------------------------------------------------------------------------------------------------------------

    public static final StringCache STRING_CACHE =
            C14nUtil.ENABLED
                    ? new OpenAddressedWeakUnboundedStringCache<>(StringCacheTypeAdapterStringImpl.INSTANCE,
                            C14nUtil.CACHE)
                    : STRING_CACHE_SIZE == 0 ? AlwaysCreateStringCache.STRING_INSTANCE
                            : new ConcurrentBoundedStringCache<>(StringCacheTypeAdapterStringImpl.INSTANCE,
                                    STRING_CACHE_SIZE, 2);

    // ------------------------------------------------------------------------------------------------------------------
    // Optional use of CompressedStrings for Symbol or String data columns (excluding partitioning columns)
    // ------------------------------------------------------------------------------------------------------------------

    private static final boolean USE_COMPRESSED_STRINGS =
            Configuration.getInstance().getBooleanWithDefault("StringUtils.useCompressedStrings", false);

    public static final StringCache COMPRESSED_STRING_CACHE =
            USE_COMPRESSED_STRINGS
                    ? C14nUtil.ENABLED
                            ? new OpenAddressedWeakUnboundedStringCache<>(
                                    StringCacheTypeAdapterCompressedStringImpl.INSTANCE, C14nUtil.CACHE)
                            : STRING_CACHE_SIZE == 0 ? AlwaysCreateStringCache.COMPRESSED_STRING_INSTANCE
                                    : new ConcurrentBoundedStringCache<>(
                                            StringCacheTypeAdapterCompressedStringImpl.INSTANCE, STRING_CACHE_SIZE, 2)
                    : null;

    /**
     * Re-write all non-partitioning String columns in the definition to use CompressedString data type. Note this
     * should only be called when it's known that the source will only provide String data with single-byte encodings.
     *
     * @param tableDefinition table definition
     * @return the new table definition
     */
    public static TableDefinition rewriteStringColumnTypes(final TableDefinition tableDefinition) {
        if (!USE_COMPRESSED_STRINGS) {
            return tableDefinition;
        }
        final ColumnDefinition[] resultColumns = tableDefinition.getColumnsArray();
        for (int ci = 0; ci < resultColumns.length; ++ci) {
            final ColumnDefinition column = resultColumns[ci];
            if (column.getDataType() == String.class
                    && column.getColumnType() != ColumnDefinition.ColumnType.Partitioning) {
                resultColumns[ci] = column.withDataType(CompressedString.class);
            }
        }
        return TableDefinition.of(resultColumns);
    }

    // ------------------------------------------------------------------------------------------------------------------
    // Other utilities
    // ------------------------------------------------------------------------------------------------------------------

    /**
     * Convenience method to combine null and isEmpty checks on a String.
     *
     * @param s a String, possibly null
     * @return true if the string is null or is empty
     */
    public static boolean isNullOrEmpty(String s) {
        return s == null || s.isEmpty();
    }

    @NotNull
    @SuppressWarnings("unchecked")
    public static  StringCache getStringCache(
            Class dataType) {
        if (String.class == dataType) {
            return (StringCache) StringUtils.STRING_CACHE;
        } else if (CompressedString.class == dataType) {
            return (StringCache) StringUtils.COMPRESSED_STRING_CACHE;
        } else {
            // Writing code has been updated to support arbitrary CharSequences without reverting to
            // Externalizable/Serializable implementations. Reading code doesn't know what to do with other
            // CharSequence types, especially given that most are mutable.
            throw new IllegalArgumentException("Unsupported CharSequence type " + dataType);
        }
    }

    // ------------------------------------------------------------------------------------------------------------------
    // String/CharSequence KeyedObjectKey implementation
    // ------------------------------------------------------------------------------------------------------------------

    /**
     * Generic String -> Object key representation.
     * 
     * @param 
     */
    private static class StringKey extends KeyedObjectKey.Basic {

        private static KeyedObjectKey.Basic INSTANCE = new StringKey<>();

        @Override
        public String getKey(@NotNull final VALUE_TYPE value) {
            return value.toString();
        }
    }

    /**
     * Generic accessor for the singleton StringKey instance.
     * 
     * @param  The type of the value for this key implementation
     * @return A String->StringKeyedObject key representation instance.
     */
    @SuppressWarnings("unused")
    public static  KeyedObjectKey.Basic stringKey() {
        // noinspection unchecked
        return (KeyedObjectKey.Basic) StringKey.INSTANCE;
    }

    /**
     * Generic String -> Object key representation, with support for null keys.
     * 
     * @param 
     */
    private static class NullSafeStringKey extends KeyedObjectKey.NullSafeBasic {

        private static KeyedObjectKey.NullSafeBasic INSTANCE = new NullSafeStringKey<>();

        @Override
        public String getKey(@NotNull final VALUE_TYPE value) {
            return value.toString();
        }
    }

    /**
     * Generic accessor for the singleton NullSafeStringKey instance.
     * 
     * @param  The type of the value for this key implementation
     * @return A String->StringKeyedObject key representation instance that supports null keys.
     */
    @SuppressWarnings("unused")
    public static  KeyedObjectKey.NullSafeBasic nullSafeStringKey() {
        // noinspection unchecked
        return (KeyedObjectKey.NullSafeBasic) NullSafeStringKey.INSTANCE;
    }

    /**
     * Interface to make auto-keying by toString() safer and more flexible.
     */
    public interface StringKeyedObject {

        default String getStringRepresentation() {
            return toString();
        }
    }

    /**
     * Generic String -> StringKeyedObject key representation.
     * 
     * @param 
     */
    private static class StringKeyedObjectKey
            extends KeyedObjectKey.Basic {

        private static KeyedObjectKey.Basic INSTANCE =
                new StringKeyedObjectKey<>();

        @Override
        public String getKey(@NotNull final VALUE_TYPE value) {
            return value.getStringRepresentation();
        }
    }

    /**
     * Generic accessor for the singleton StringKeyedObjectKey instance.
     * 
     * @param  The type of the value for this key implementation
     * @return A String->StringKeyedObject key representation instance.
     */
    @SuppressWarnings("unused")
    public static  KeyedObjectKey.Basic stringKeyedObjectKey() {
        // noinspection unchecked
        return (KeyedObjectKey.Basic) StringKeyedObjectKey.INSTANCE;
    }

    /**
     * Generic String -> StringKeyedObject key representation, with support for null keys.
     * 
     * @param 
     */
    private static class NullSafeStringKeyedObjectKey
            extends KeyedObjectKey.NullSafeBasic {

        private static KeyedObjectKey.NullSafeBasic INSTANCE =
                new NullSafeStringKeyedObjectKey<>();

        @Override
        public String getKey(@NotNull final VALUE_TYPE value) {
            return value.getStringRepresentation();
        }
    }

    /**
     * Generic accessor for the singleton NullSafeStringKeyedObjectKey instance.
     * 
     * @param  The type of the value for this key implementation
     * @return A String->StringKeyedObject key representation instance that supports null keys.
     */
    @SuppressWarnings("unused")
    public static  KeyedObjectKey.NullSafeBasic nullSafeStringKeyedObjectKey() {
        // noinspection unchecked
        return (KeyedObjectKey.NullSafeBasic) NullSafeStringKeyedObjectKey.INSTANCE;
    }

    /**
     * Generic CharSequence -> StringKeyedObject key representation.
     * 
     * @param 
     */
    private static class CharSequenceKey
            implements KeyedObjectKey {

        /**
         * Singleton CharSequenceKey instance.
         */
        private static KeyedObjectKey INSTANCE = new CharSequenceKey<>();


        @Override
        public CharSequence getKey(@NotNull final VALUE_TYPE value) {
            return value.getStringRepresentation();
        }

        @Override
        public int hashKey(@NotNull final CharSequence key) {
            if (key instanceof String || key instanceof StringCompatible) {
                return key.hashCode();
            }
            return CharSequenceUtils.hashCode(key);
        }

        @Override
        public boolean equalKey(@NotNull final CharSequence key, @NotNull final VALUE_TYPE value) {
            return CharSequenceUtils.contentEquals(key, getKey(value));
        }
    }

    /**
     * Generic accessor for the singleton CharSequenceKey instance.
     * 
     * @param  The type of the value for this key implementation
     * @return A CharSequence->StringKeyedObject key representation instance.
     */
    public static  KeyedObjectKey charSequenceKey() {
        // noinspection unchecked
        return (KeyedObjectKey) CharSequenceKey.INSTANCE;
    }

    /**
     * Generic CharSequence -> StringKeyedObject key representation.
     * 
     * @param 
     */
    private static class NullSafeCharSequenceKey
            implements KeyedObjectKey {

        /**
         * Singleton CharSequenceKey instance.
         */
        private static KeyedObjectKey INSTANCE =
                new NullSafeCharSequenceKey<>();


        @Override
        public CharSequence getKey(@NotNull final VALUE_TYPE value) {
            return value.getStringRepresentation();
        }

        @Override
        public int hashKey(final CharSequence key) {
            if (key instanceof String || key instanceof StringCompatible) {
                return key.hashCode();
            }
            return key == null ? 0 : CharSequenceUtils.hashCode(key);
        }

        @Override
        public boolean equalKey(final CharSequence key, @NotNull final VALUE_TYPE value) {
            return CharSequenceUtils.nullSafeContentEquals(key, getKey(value));
        }
    }

    /**
     * Generic accessor for the singleton CharSequenceKey instance, with support for null keys.
     * 
     * @param  The type of the value for this key implementation
     * @return A CharSequence->StringKeyedObject key representation instance.
     */
    @SuppressWarnings("unused")
    public static  KeyedObjectKey nullSafeCharSequenceKey() {
        // noinspection unchecked
        return (KeyedObjectKey) NullSafeCharSequenceKey.INSTANCE;
    }

    /**
     * Extracts a message from a throwable. If the message is null or empty, returns toString instead.
     *
     * @param t the throwable
     * @return the message or the string
     */
    public static String extractMessage(Throwable t) {
        return isNullOrEmpty(t.getMessage()) ? t.toString() : t.getMessage();
    }

    /**
     * Possibly replace an array element in one array with an array element in another array. The master strings are
     * searched to see if they contain an element that starts with the startsWithValue. If so, and the strings to be
     * replaced also contain an element which starts with startsWithValue, then that value is replaced with the master
     * string array's value. If multiple values match, only the first value for each array is processed.
     *
     * @param masterStrings the string array containing the values to use
     * @param toReplaceStrings the string array in which to perform the substitution
     * @param startsWithValue the starts-with value to search for
     */
    public static void replaceArrayValue(final String[] masterStrings,
            final String[] toReplaceStrings,
            final String startsWithValue) {
        Arrays.stream(masterStrings)
                .filter(s -> s.startsWith(startsWithValue))
                .findFirst()
                .ifPresent(s -> {
                    for (int i = 0; i < toReplaceStrings.length; i++) {
                        if (toReplaceStrings[i].startsWith(startsWithValue)) {
                            toReplaceStrings[i] = s;
                            return;
                        }
                    }
                });
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy