aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileVisitor.java
blob: d5cf9a3d74d5c91aa81f1d5909314a206e78b221 (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
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.query.profile;

/**
 * Instances of this is used to visit nodes in a graph of query profiles
 *
 * <code>
 * Visitor are called in the following sequence on each query profile:
 * enter=enter(referenceName);
 * onQueryProfile(this)
 * if (enter) {
 *   getLocalKey()
 *     ...calls on nested content found in variants, this and inherited, in that order
 *   leave(referenceName)
 * }
 *
 * The first enter call will be on the root node, which has an empt reference name.
 * </code>
 *
 * @author bratseth
 */
abstract class QueryProfileVisitor {

    /**
     * Called when a new <b>nested</b> profile in the graph is entered.
     * This default implementation does nothing but returning true.
     * If the node is entered (if true is returned from this), a corresponding {@link #leave(String)} call will happen
     * later.
     *
     * @param name the name this profile is nested as, or the empty string if we are entering the root profile
     * @return whether we should visit the content of this node or not
     */
    public boolean enter(String name) { return true; }

    /**
     * Called when the last {@link #enter(String) entered} nested profile is left.
     * That is: One leave call is made for each enter call which returns true,
     * but due to nesting those calls are not necessarily alternating.
     * This default implementation does nothing.
     */
    public void leave(String name) { }

    /**
     * Called when a value (not a query profile) is encountered.
     *
     * @param localName the local name of this value (the full name, if needed, must be reconstructed
     *        by the information given by the history of {@link #enter(String)} and {@link #leave(String)} calls
     * @param value the value
     * @param binding the binding this holds for
     * @param owner the query profile having this value, or null only when profile is the root profile
     * @param variant the variant having this value, or null if it is not in a variant
     */
    public abstract void onValue(String localName,
                                 Object value,
                                 DimensionBinding binding,
                                 QueryProfile owner,
                                 DimensionValues variant);

    /**
     * Called when a query profile is encountered.
     *
     * @param profile the query profile reference encountered
     * @param binding the binding this holds for
     * @param owner the profile making this reference, or null only when profile is the root profile
     * @param variant the variant having this value, or null if it is not in a variant
     */
    public abstract void onQueryProfile(QueryProfile profile,
                                        DimensionBinding binding,
                                        QueryProfile owner,
                                        DimensionValues variant);

    /** Returns whether this visitor is done visiting what it needed to visit at this point */
    public abstract boolean isDone();

    /** Returns whether we should, at this point, visit inherited profiles. This default implementation returns true */
    public boolean visitInherited() { return true; }

    /**
     * Returns the current local key which should be visited in the last {@link #enter(String) entered} sub-profile
     * (or in the top level profile if none is entered), or null to visit all content
     */
    public abstract String getLocalKey();

    /**
     * Calls onValue or onQueryProfile on this and visits the content if it's a profile
     *
     * @param variant the variant having this value, or null if it is not in a variant
     */
    final void acceptValue(String key,
                           Object value,
                           DimensionBinding dimensionBinding,
                           QueryProfile owner,
                           DimensionValues variant) {
        if (value == null) return;
        if (value instanceof QueryProfile) {
            QueryProfile queryProfileValue = (QueryProfile)value;
            queryProfileValue.acceptAndEnter(key, this, dimensionBinding.createFor(queryProfileValue.getDimensions()), owner);
        }
        else {
            onValue(key, value, dimensionBinding, owner, variant);
        }
    }

}