aboutsummaryrefslogtreecommitdiffstats
path: root/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/Reference.java
blob: 75bbe55128c00c3f436a517218e84979596d8432 (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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchlib.rankingexpression;

import com.yahoo.searchlib.rankingexpression.rule.Arguments;
import com.yahoo.searchlib.rankingexpression.rule.CompositeNode;
import com.yahoo.searchlib.rankingexpression.rule.ExpressionNode;
import com.yahoo.searchlib.rankingexpression.rule.NameNode;
import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode;
import com.yahoo.searchlib.rankingexpression.rule.SerializationContext;
import com.yahoo.tensor.evaluation.Name;

import java.util.Deque;
import java.util.Objects;
import java.util.Optional;

/**
 * A reference to a feature, function, or value in ranking expressions
 *
 * @author bratseth
 */
public class Reference extends Name implements Comparable<Reference> {

    private final int hashCode;

    private final Arguments arguments;

    /** The output, or null if none */
    private final String output;

    /** True if this was created by the "fromIdentifier" method. This lets us separate 'foo()' and 'foo' */
    private final boolean isIdentifier;

    public static Reference fromIdentifier(String identifier) {
        return new Reference(identifier, Arguments.EMPTY, null, true);
    }

    public Reference(String name, Arguments arguments, String output) {
        this(name, arguments, output, false);
    }

    private Reference(String name, Arguments arguments, String output, boolean isIdentifier) {
        super(name);
        Objects.requireNonNull(name, "name cannot be null");
        Objects.requireNonNull(arguments, "arguments cannot be null");
        this.arguments = arguments;
        this.output = output;
        this.hashCode = Objects.hash(name(), arguments, output, isIdentifier);
        this.isIdentifier = isIdentifier;
    }


    public Arguments arguments() { return arguments; }

    public String output() { return output; }

    /**
     * Creates a reference to a simple feature consisting of a name and a single argument
     */
    public static Reference simple(String name, String argumentValue) {
        return new Reference(name, new Arguments(new ReferenceNode(argumentValue)), null);
    }

    /**
     * Returns the given simple feature as a reference, or empty if it is not a valid simple
     * feature string on the form name(argument).
     */
    public static Optional<Reference> simple(String feature) {
        int startParenthesis = feature.indexOf('(');
        if (startParenthesis < 0)
            return Optional.empty();
        int endParenthesis = feature.lastIndexOf(')');
        String featureName = feature.substring(0, startParenthesis);
        if (startParenthesis < 1 || endParenthesis < startParenthesis) return Optional.empty();
        String argument = feature.substring(startParenthesis + 1, endParenthesis);
        if (argument.startsWith("'") || argument.startsWith("\""))
            argument = argument.substring(1);
        if (argument.endsWith("'") || argument.endsWith("\""))
            argument = argument.substring(0, argument.length() - 1);
        return Optional.of(simple(featureName, argument));
    }

    /** Returns true if this was created by fromIdentifier. Identifiers have no arguments or outputs. */
    public boolean isIdentifier() { return isIdentifier; }

    /**
     * A <i>simple feature reference</i> is a reference with a single identifier argument
     * (and an optional output).
     */
    public boolean isSimple() {
        return simpleArgument().isPresent();
    }

    /**
     * If the arguments of this contains a single argument which is an identifier, it is returned.
     * Otherwise null is returned.
     */
    public Optional<String> simpleArgument() {
        if (arguments.expressions().size() != 1) return Optional.empty();
        ExpressionNode argument = arguments.expressions().get(0);

        if (argument instanceof ReferenceNode) {
            ReferenceNode refArgument = (ReferenceNode) argument;

            if ( ! refArgument.reference().isIdentifier()) return Optional.empty();

            return Optional.of(refArgument.getName());
        }
        else if (argument instanceof NameNode) {
            return Optional.of(((NameNode) argument).getValue());
        }
        else {
            return Optional.empty();
        }
    }

    public Reference withArguments(Arguments arguments) {
        return new Reference(name(), arguments, output, isIdentifier && arguments.isEmpty());
    }

    public Reference withOutput(String output) {
        return new Reference(name(), arguments, output, isIdentifier && output == null);
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if (o.hashCode() != this.hashCode()) return false; // because this has a fast hashCode
        if ( ! (o instanceof Reference)) return false;
        Reference other = (Reference) o;
        if (!Objects.equals(other.name(), this.name())) return false;
        if (!Objects.equals(other.arguments, this.arguments)) return false;
        if (!Objects.equals(other.output, this.output)) return false;
        if (!Objects.equals(other.isIdentifier, this.isIdentifier)) return false;
        return true;
    }

    @Override
    public int hashCode() {
        return hashCode;
    }

    @Override
    public String toString() {
        return toString(new StringBuilder(), new SerializationContext(), null, null).toString();
    }

    public StringBuilder toString(StringBuilder b, SerializationContext context, Deque<String> path, CompositeNode parent) {
        b.append(name());
        if (arguments.expressions().size() > 0) {
            b.append("(");
            for (int i = 0; i < arguments.expressions().size(); i++) {
                ExpressionNode e = arguments.expressions().get(i);
                e.toString(b, context, path, parent);
                if (i+1 < arguments.expressions().size()) {
                    b.append(',');
                }

            }
            b.append(")");
        }
        if (output != null)
            b.append(".").append(output);
        return b;
    }

    @Override
    public int compareTo(Reference o) {
        return this.toString().compareTo(o.toString());
    }

}