aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/search/yql/TypeCheckers.java
blob: fd36909614ee744207470d56607ec49c1876b235 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.yql;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.inject.TypeLiteral;

import java.lang.reflect.ParameterizedType;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

final class TypeCheckers {

    public static final TypeLiteral<List<String>> LIST_OF_STRING = new TypeLiteral<List<String>>() {
    };
    public static final TypeLiteral<List<List<String>>> LIST_OF_LIST_OF_STRING = new TypeLiteral<List<List<String>>>() {
    };
    public static final TypeLiteral<List<OperatorNode<SequenceOperator>>> SEQUENCES = new TypeLiteral<List<OperatorNode<SequenceOperator>>>() {
    };
    public static final TypeLiteral<List<OperatorNode<ExpressionOperator>>> EXPRS = new TypeLiteral<List<OperatorNode<ExpressionOperator>>>() {
    };
    public static final TypeLiteral<List<List<OperatorNode<ExpressionOperator>>>> LIST_OF_EXPRS = new TypeLiteral<List<List<OperatorNode<ExpressionOperator>>>>() {
    };
    public static final ImmutableSet<Class<?>> LITERAL_TYPES = ImmutableSet.<Class<?>>builder()
            .add(String.class)
            .add(Integer.class)
            .add(Double.class)
            .add(Boolean.class)
            .add(Float.class)
            .add(Byte.class)
            .add(Long.class)
            .add(List.class)
            .add(Map.class)
            .build();

    private TypeCheckers() {
    }

    public static ArgumentsTypeChecker make(Operator target, Object... types) {
        // Class<?> extends Operator -> NodeTypeChecker
        if (types == null) {
            types = new Object[0];
        }
        List<OperatorTypeChecker> checkers = Lists.newArrayListWithCapacity(types.length);
        for (int i = 0; i < types.length; ++i) {
            checkers.add(createChecker(target, i, types[i]));
        }
        return new ArgumentsTypeChecker(target, checkers);
    }

    // this is festooned with instance checkes before all the casting
    @SuppressWarnings("unchecked")
    private static OperatorTypeChecker createChecker(Operator parent, int idx, Object value) {
        if (value instanceof TypeLiteral) {
            TypeLiteral<?> lit = (TypeLiteral<?>) value;
            Class<?> raw = lit.getRawType();
            if (List.class.isAssignableFrom(raw)) {
                Preconditions.checkArgument(lit.getType() instanceof ParameterizedType, "TypeLiteral without a ParameterizedType for List");
                ParameterizedType type = (ParameterizedType) lit.getType();
                TypeLiteral<?> arg = TypeLiteral.get(type.getActualTypeArguments()[0]);
                if (OperatorNode.class.isAssignableFrom(arg.getRawType())) {
                    Preconditions.checkArgument(arg.getType() instanceof ParameterizedType, "Type spec must be List<OperatorNode<?>>");
                    Class<?> rawType = (Class<?>) TypeLiteral.get(((ParameterizedType) arg.getType()).getActualTypeArguments()[0]).getRawType();
                    Class<? extends Operator> optype = (Class<? extends Operator>) rawType;
                    return new OperatorNodeListTypeChecker(parent, idx, optype, ImmutableSet.<Operator>of());
                } else {
                    return new JavaListTypeChecker(parent, idx, arg.getRawType());
                }
            }
            throw new IllegalArgumentException("don't know how to handle TypeLiteral " + value);
        }
        if (value instanceof Class) {
            Class<?> clazz = (Class<?>) value;
            if (Operator.class.isAssignableFrom(clazz)) {
                return new NodeTypeChecker(parent, idx, (Class<? extends Operator>) clazz, ImmutableSet.<Operator>of());
            } else {
                return new JavaTypeChecker(parent, idx, clazz);
            }
        } else if (value instanceof Operator) {
            Operator operator = (Operator) value;
            Class<? extends Operator> clazz = operator.getClass();
            Set<? extends Operator> allowed;
            if (Enum.class.isInstance(value)) {
                Class<? extends Enum> enumClazz = (Class<? extends Enum>) clazz;
                allowed = (Set<? extends Operator>) EnumSet.of(enumClazz.cast(value));
            } else {
                allowed = ImmutableSet.of(operator);
            }
            return new NodeTypeChecker(parent, idx, clazz, allowed);
        } else if (value instanceof EnumSet) {
            EnumSet<?> v = (EnumSet<?>) value;
            Enum elt = Iterables.get(v, 0);
            if (elt instanceof Operator) {
                Class<? extends Operator> opclass = (Class<? extends Operator>) elt.getClass();
                Set<? extends Operator> allowed = (Set<? extends Operator>) v;
                return new NodeTypeChecker(parent, idx, opclass, allowed);
            }
        } else if (value instanceof Set) {
            // Set<Class<?>>
            return new JavaUnionTypeChecker(parent, idx, (Set<Class<?>>) value);
        }
        throw new IllegalArgumentException("I don't know how to create a checker from " + value);
    }

}