io.crate.types.DataTypes.java Source code

Java tutorial

Introduction

Here is the source code for io.crate.types.DataTypes.java

Source

/*
 * Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
 * license agreements.  See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.  Crate licenses
 * this file to you 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.
 *
 * However, if you have executed another commercial license agreement
 * with Crate these terms will supersede the license and you may use the
 * software solely pursuant to the terms of the relevant commercial agreement.
 */

package io.crate.types;

import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import io.crate.Streamer;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;

import java.io.IOException;
import java.util.*;

public class DataTypes {

    private final static ESLogger logger = Loggers.getLogger(DataTypes.class);

    /**
     * If you add types here make sure to update the SizeEstimatorFactory in the SQL module.
     */
    public final static UndefinedType UNDEFINED = UndefinedType.INSTANCE;
    public final static NotSupportedType NOT_SUPPORTED = NotSupportedType.INSTANCE;

    public final static ByteType BYTE = ByteType.INSTANCE;
    public final static BooleanType BOOLEAN = BooleanType.INSTANCE;

    public final static StringType STRING = StringType.INSTANCE;
    public final static IpType IP = IpType.INSTANCE;

    public final static DoubleType DOUBLE = DoubleType.INSTANCE;
    public final static FloatType FLOAT = FloatType.INSTANCE;

    public final static ShortType SHORT = ShortType.INSTANCE;
    public final static IntegerType INTEGER = IntegerType.INSTANCE;
    public final static LongType LONG = LongType.INSTANCE;
    public final static TimestampType TIMESTAMP = TimestampType.INSTANCE;

    public final static ObjectType OBJECT = ObjectType.INSTANCE;

    public final static GeoPointType GEO_POINT = GeoPointType.INSTANCE;
    public final static GeoShapeType GEO_SHAPE = GeoShapeType.INSTANCE;

    public final static DataType DOUBLE_ARRAY = new ArrayType(DataTypes.DOUBLE);
    public final static DataType OBJECT_ARRAY = new ArrayType(DataTypes.OBJECT);

    public final static ImmutableList<DataType> PRIMITIVE_TYPES = ImmutableList.<DataType>of(BYTE, BOOLEAN, STRING,
            IP, DOUBLE, FLOAT, SHORT, INTEGER, LONG, TIMESTAMP);
    public final static ImmutableList<DataType> NUMERIC_PRIMITIVE_TYPES = ImmutableList.<DataType>of(DOUBLE, FLOAT,
            BYTE, SHORT, INTEGER, LONG);

    public static final Map<Integer, DataTypeFactory> TYPE_REGISTRY = new MapBuilder<Integer, DataTypeFactory>()
            .put(UndefinedType.ID, UNDEFINED).put(NotSupportedType.ID, NOT_SUPPORTED).put(ByteType.ID, BYTE)
            .put(BooleanType.ID, BOOLEAN).put(StringType.ID, STRING).put(IpType.ID, IP).put(DoubleType.ID, DOUBLE)
            .put(FloatType.ID, FLOAT).put(ShortType.ID, SHORT).put(IntegerType.ID, INTEGER).put(LongType.ID, LONG)
            .put(TimestampType.ID, TIMESTAMP).put(ObjectType.ID, OBJECT).put(GeoPointType.ID, GEO_POINT)
            .put(GeoShapeType.ID, GEO_SHAPE).put(ArrayType.ID, new DataTypeFactory() {
                @Override
                public DataType<?> create() {
                    return new ArrayType();
                }
            }).put(SetType.ID, new DataTypeFactory() {
                @Override
                public DataType<?> create() {
                    return new SetType();
                }
            }).map();

    private static final Set<DataType> NUMBER_CONVERSIONS = ImmutableSet.<DataType>builder()
            .addAll(NUMERIC_PRIMITIVE_TYPES).add(BOOLEAN).add(STRING, TIMESTAMP, IP).build();
    // allowed conversion from key to one of the value types
    // the key type itself does not need to be in the value set
    public static final ImmutableMap<Integer, Set<DataType>> ALLOWED_CONVERSIONS = ImmutableMap
            .<Integer, Set<DataType>>builder().put(BYTE.id(), NUMBER_CONVERSIONS)
            .put(SHORT.id(), NUMBER_CONVERSIONS).put(INTEGER.id(), NUMBER_CONVERSIONS)
            .put(LONG.id(), NUMBER_CONVERSIONS).put(FLOAT.id(), NUMBER_CONVERSIONS)
            .put(DOUBLE.id(), NUMBER_CONVERSIONS).put(BOOLEAN.id(), ImmutableSet.<DataType>of(STRING))
            .put(STRING.id(),
                    ImmutableSet.<DataType>builder().addAll(NUMBER_CONVERSIONS).add(GEO_SHAPE).add(GEO_POINT)
                            .add(BOOLEAN).build())
            .put(IP.id(), ImmutableSet.<DataType>of(STRING)).put(TIMESTAMP.id(), ImmutableSet.<DataType>of(LONG))
            .put(UNDEFINED.id(), ImmutableSet.<DataType>of()) // actually convertible to every type, see NullType
            .put(GEO_POINT.id(), ImmutableSet.<DataType>of(new ArrayType(DOUBLE)))
            .put(OBJECT.id(), ImmutableSet.<DataType>of(GEO_SHAPE)).put(ArrayType.ID, ImmutableSet.<DataType>of()) // convertability handled in ArrayType
            .put(SetType.ID, ImmutableSet.<DataType>of()) // convertability handled in SetType
            .build();

    public static boolean isCollectionType(DataType type) {
        return type.id() == ArrayType.ID || type.id() == SetType.ID;
    }

    public static DataType fromStream(StreamInput in) throws IOException {
        int i = in.readVInt();
        try {
            DataType type = TYPE_REGISTRY.get(i).create();
            type.readFrom(in);
            return type;
        } catch (NullPointerException e) {
            logger.error(String.format(Locale.ENGLISH, "%d is missing in TYPE_REGISTRY", i), e);
            throw e;
        }
    }

    public static void toStream(DataType type, StreamOutput out) throws IOException {
        out.writeVInt(type.id());
        type.writeTo(out);
    }

    private static final Map<Class<?>, DataType> POJO_TYPE_MAPPING = ImmutableMap.<Class<?>, DataType>builder()
            .put(Double.class, DOUBLE).put(Float.class, FLOAT).put(Integer.class, INTEGER).put(Long.class, LONG)
            .put(Short.class, SHORT).put(Byte.class, BYTE).put(Boolean.class, BOOLEAN).put(Map.class, OBJECT)
            .put(String.class, STRING).put(BytesRef.class, STRING).put(Character.class, STRING).build();

    public static DataType<?> guessType(Object value) {
        if (value == null) {
            return UNDEFINED;
        } else if (value instanceof Map) {
            return OBJECT;
        } else if (value instanceof List) {
            return valueFromList((List) value);
        } else if (value.getClass().isArray()) {
            return valueFromList(Arrays.asList((Object[]) value));
        }
        return POJO_TYPE_MAPPING.get(value.getClass());
    }

    private static DataType valueFromList(List<Object> value) {
        List<DataType> innerTypes = new ArrayList<>(value.size());
        if (value.isEmpty()) {
            return new ArrayType(UNDEFINED);
        }
        DataType previous = null;
        DataType current = null;
        for (Object o : value) {
            if (o == null) {
                continue;
            }
            current = guessType(o);
            if (previous != null && !current.equals(previous)) {
                throw new IllegalArgumentException("Mixed dataTypes inside a list are not supported");
            }
            innerTypes.add(current);
            previous = current;
        }

        if (innerTypes.isEmpty() || (innerTypes.size() > 0 && current == null)) {
            return new ArrayType(UNDEFINED);
        } else {
            return new ArrayType(current);
        }
    }

    private static final ImmutableMap<String, DataType> staticTypesNameMap = ImmutableMap
            .<String, DataType>builder().put(UNDEFINED.getName(), UNDEFINED).put(BYTE.getName(), BYTE)
            .put(BOOLEAN.getName(), BOOLEAN).put(STRING.getName(), STRING).put(IP.getName(), IP)
            .put(DOUBLE.getName(), DOUBLE).put(FLOAT.getName(), FLOAT).put(SHORT.getName(), SHORT)
            .put(INTEGER.getName(), INTEGER).put(LONG.getName(), LONG).put(TIMESTAMP.getName(), TIMESTAMP)
            .put(OBJECT.getName(), OBJECT).put(GEO_POINT.getName(), GEO_POINT).put(GEO_SHAPE.getName(), GEO_SHAPE)
            .build();

    public static DataType ofName(String name) {
        DataType dataType = staticTypesNameMap.get(name);
        if (dataType == null) {
            throw new IllegalArgumentException("Cannot find data type of name " + name);
        }
        return dataType;
    }

    private static final ImmutableMap<String, DataType> MAPPING_NAMES_TO_TYPES = ImmutableMap
            .<String, DataType>builder().put("date", DataTypes.TIMESTAMP).put("string", DataTypes.STRING)
            .put("boolean", DataTypes.BOOLEAN).put("byte", DataTypes.BYTE).put("short", DataTypes.SHORT)
            .put("integer", DataTypes.INTEGER).put("long", DataTypes.LONG).put("float", DataTypes.FLOAT)
            .put("double", DataTypes.DOUBLE).put("ip", DataTypes.IP).put("geo_point", DataTypes.GEO_POINT)
            .put("geo_shape", DataTypes.GEO_SHAPE).put("object", DataTypes.OBJECT).put("nested", DataTypes.OBJECT)
            .build();

    @Nullable
    public static DataType ofMappingName(String name) {
        return MAPPING_NAMES_TO_TYPES.get(name);
    }

    public static DataType ofMappingNameSafe(String name) {
        DataType dataType = ofMappingName(name);
        if (dataType == null) {
            throw new IllegalArgumentException("Cannot find data type of mapping name " + name);
        }
        return dataType;
    }

    public static boolean isPrimitive(DataType type) {
        return PRIMITIVE_TYPES.contains(type);
    }

    public static void register(int id, DataTypeFactory dataTypeFactory) {
        if (TYPE_REGISTRY.put(id, dataTypeFactory) != null) {
            throw new IllegalArgumentException("Already got a dataType with id " + id);
        }
    }

    public static Streamer<?>[] getStreamers(Collection<? extends DataType> dataTypes) {
        Streamer<?>[] streamer = new Streamer[dataTypes.size()];
        int idx = 0;
        for (DataType dataType : dataTypes) {
            streamer[idx] = dataType.streamer();
            idx++;
        }
        return streamer;
    }

    private static final Predicate<DataType> NOT_NULL_TYPE_FILTER = new Predicate<DataType>() {
        @Override
        public boolean apply(DataType input) {
            return input != UNDEFINED;
        }
    };

    /**
     * Returns the first data type that is not {@link UNDEFINED}, or {@code UNDEFINED} if none found.
     */
    public static DataType tryFindNotNullType(Iterable<? extends DataType> dataTypes) {
        return Iterables.find(dataTypes, NOT_NULL_TYPE_FILTER, UNDEFINED);
    }
}