summaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/search/query/profile/SubstituteString.java
blob: 3252f0f466227ee01c9df5419121cf8f20fef1a6 (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
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.query.profile;

import com.yahoo.processing.request.Properties;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * A string which contains one or more elements of the form %{name},
 * where these occurrences are to be replaced by a query profile lookup on name.
 * <p>
 * This objects does the analysis on creation and provides a (reasonably) fast method of
 * performing the actual substitution (at lookup time).
 * <p>
 * This is a value object. Lookups in this are thread safe.
 *
 * @author bratseth
 */
public class SubstituteString {

    private final List<Component> components;
    private final String stringValue;

    /**
     * Returns a new SubstituteString if the given string contains substitutions, null otherwise.
     */
    public static SubstituteString create(String value) {
        int lastEnd = 0;
        int start = value.indexOf("%{");
        if (start < 0) return null; // Shortcut
        List<Component> components = new ArrayList<>();
        while (start >= 0) {
            int end = value.indexOf("}", start + 2);
            if (end < 0)
                throw new IllegalArgumentException("Unterminated value substitution '" + value.substring(start) + "'");
            String propertyName = value.substring(start+2,end);
            if (propertyName.indexOf("%{") >= 0)
                throw new IllegalArgumentException("Unterminated value substitution '" + value.substring(start) + "'");
            components.add(new StringComponent(value.substring(lastEnd, start)));
            components.add(new PropertyComponent(propertyName));
            lastEnd = end+1;
            start = value.indexOf("%{", lastEnd);
        }
        components.add(new StringComponent(value.substring(lastEnd)));
        return new SubstituteString(components, value);
    }

    private SubstituteString(List<Component> components, String stringValue) {
        this.components = components;
        this.stringValue = stringValue;
    }

    /**
     * Perform the substitution in this, by looking up in the given query profile,
     * and returns the resulting string
     */
    public String substitute(Map<String, String> context, Properties substitution) {
        StringBuilder b = new StringBuilder();
        for (Component component : components)
            b.append(component.getValue(context,substitution));
        return b.toString();
    }

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

    @Override
    public boolean equals(Object other) {
        if (other == this) return true;
        if ( ! (other instanceof SubstituteString)) return false;
        return this.stringValue.equals(((SubstituteString)other).stringValue);
    }

    /** Returns this string in original (unsubstituted) form */
    @Override
    public String toString() {
        return stringValue;
    }

    private abstract static class Component {

        protected abstract String getValue(Map<String, String> context, Properties substitution);

    }

    private final static class StringComponent extends Component {

        private final String value;

        public StringComponent(String value) {
            this.value = value;
        }

        @Override
        public String getValue(Map<String, String> context, Properties substitution) {
            return value;
        }

        @Override
        public String toString() {
            return value;
        }

    }

    private final static class PropertyComponent extends Component {

        private final String propertyName;

        public PropertyComponent(String propertyName) {
            this.propertyName = propertyName;
        }

        @Override
        public String getValue(Map<String,String> context, Properties substitution) {
            Object value = substitution.get(propertyName, context, substitution);
            if (value == null) return "";
            return String.valueOf(value);
        }

        @Override
        public String toString() {
            return "%{" + propertyName + "}";
        }

    }

}