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

import java.util.Iterator;
import java.util.List;

import com.yahoo.prelude.semantics.engine.Choicepoint;
import com.yahoo.prelude.semantics.engine.FlattenedItem;
import com.yahoo.prelude.semantics.engine.RuleEvaluation;

/**
 * A condition which greedily matches anything, represented as "..."
 *
 * @author bratseth
 */
public class EllipsisCondition extends Condition {

    /** Whether this ellipsis is actually referable (enclosed in []) or not */
    private boolean referable;

    /** Creates a referable ellipsis condition with no label */
    public EllipsisCondition() {
        this(true);
    }

    /** Creates an ellipsis condition with no label */
    public EllipsisCondition(boolean referable) {
        this(null,referable);
    }

    /** Creates an ellipsis condition */
    public EllipsisCondition(String label,boolean referable) {
        super(label);
        this.referable=referable;
        if (referable)
            setContextName("...");
    }

    public EllipsisCondition(String label,String context) {
        super(label,context);
    }

    public boolean doesMatch(RuleEvaluation e) {
        // We use a choice point to remember which untried alternatives are not tried (if any)
        // We never need to backtrack to this choice - backtracking is done by the parent
        // if this choice gives a global invalid state
        Choicepoint choicepoint=e.getChoicepoint(this,false);
        if (choicepoint==null) { // First try
            choicepoint=e.getChoicepoint(this,true);
        }
        else {
            if (!choicepoint.isOpen()) return false;
        }

        // Match all the rest of the items the first time, then all except the last item and so on
        int numberOfTermsToMatch=e.itemCount() - e.currentPosition() - choicepoint.tryCount();
        if (numberOfTermsToMatch<0) {
            choicepoint.close();
            return false;
        }
        choicepoint.addTry();

        String matchedTerms=matchTerms(numberOfTermsToMatch,e);
        e.setValue(matchedTerms);
        return true;
    }

    private String matchTerms(int numberOfTerms,RuleEvaluation e) {
        StringBuilder b=new StringBuilder();
        for (int i=0; i<numberOfTerms; i++) {
            e.addMatch(e.currentItem(),e.currentItem().getItem().getIndexedString());
            b.append(e.currentItem().getItem().stringValue());
            if (i<(numberOfTerms-1))
                b.append(" ");
            e.next();
        }
        return b.toString();
    }

    public String getMatchInfo(RuleEvaluation e) {
        Choicepoint choicepoint=e.getChoicepoint(this,false);
        if (choicepoint==null) return null;

        return spaceSeparated(e.items().subList(choicepoint.getState().getPosition(),
                                                e.itemCount() - choicepoint.tryCount() +1 ));
    }

    private String spaceSeparated(List<FlattenedItem> items) {
        StringBuilder buffer=new StringBuilder();
        for (Iterator<FlattenedItem> i=items.iterator(); i.hasNext(); ) {
            buffer.append(i.next().toString());
            if (i.hasNext())
                buffer.append(" ");
        }
        return buffer.toString();
    }

    /** Returns whether this ellipsis condition can be referred from a production */
    public boolean isReferable() {
        return referable || super.isReferable();
    }

    /** Sets whether this ellipsis condition can be referred from a production or not */
    public void setReferable(boolean referable) {
        this.referable=referable;
        if (referable && getContextName()==null)
            setContextName("...");
        if (!referable && "...".equals(getContextName()))
            setContextName(null);
    }

    protected boolean hasOpenChoicepoint(RuleEvaluation e) {
        Choicepoint choicepoint=e.getChoicepoint(this,false);
        if (choicepoint==null) return false; // Not tried yet
        if (!choicepoint.isOpen()) return false;
        return true;
    }

    protected boolean isDefaultContextName() {
        return (getContextName()==null || getContextName().equals("..."));
    }

    protected String toInnerString() {
        if (referable)
            return "[...]";
        else
            return "...";
    }

}