summaryrefslogtreecommitdiffstats
path: root/dom/xul/templates/nsTemplateRule.h
blob: d7821ba29fdb838d95e5692ab2536abb772a326a (plain)
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef nsTemplateRule_h__
#define nsTemplateRule_h__

#include "nsCOMPtr.h"
#include "nsIAtom.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFResource.h"
#include "nsIContent.h"
#include "nsIDOMNode.h"
#include "nsTArray.h"
#include "nsString.h"
#include "nsIXULTemplateRuleFilter.h"
#include "nsCycleCollectionParticipant.h"

class nsIXULTemplateQueryProcessor;
class nsTemplateQuerySet;

class nsTemplateCondition
{
public:
    // relations that may be used in a rule. They may be negated with the
    // negate flag. Less and Greater are used for numeric comparisons and
    // Before and After are used for string comparisons. For Less, Greater,
    // Before, After, Startswith, Endswith, and Contains, the source is
    // conceptually on the left of the relation and the target is on the
    // right. For example, if the relation is Contains, that means Match if
    // the source contains the target.
    enum ConditionRelation {
        eUnknown,
        eEquals,
        eLess,
        eGreater,
        eBefore,
        eAfter,
        eStartswith,
        eEndswith,
        eContains
    };

    nsTemplateCondition(nsIAtom* aSourceVariable,
                        const nsAString& aRelation,
                        nsIAtom* aTargetVariable,
                        bool mIgnoreCase,
                        bool mNegate);

    nsTemplateCondition(nsIAtom* aSourceVariable,
                        const nsAString& aRelation,
                        const nsAString& aTargets,
                        bool mIgnoreCase,
                        bool mNegate,
                        bool aIsMultiple);

    nsTemplateCondition(const nsAString& aSource,
                        const nsAString& aRelation,
                        nsIAtom* aTargetVariable,
                        bool mIgnoreCase,
                        bool mNegate);

    ~nsTemplateCondition() { MOZ_COUNT_DTOR(nsTemplateCondition); }

    nsTemplateCondition* GetNext() { return mNext; }
    void SetNext(nsTemplateCondition* aNext) { mNext = aNext; }

    void SetRelation(const nsAString& aRelation);

    bool
    CheckMatch(nsIXULTemplateResult* aResult);

    bool
    CheckMatchStrings(const nsAString& aLeftString,
                      const nsAString& aRightString);
protected:

    nsCOMPtr<nsIAtom>   mSourceVariable;
    nsString            mSource;
    ConditionRelation   mRelation;
    nsCOMPtr<nsIAtom>   mTargetVariable;
    nsTArray<nsString>  mTargetList;
    bool                mIgnoreCase;
    bool                mNegate;

   nsTemplateCondition* mNext;
};

/**
 * A rule consists of:
 *
 * - Conditions, a set of unbound variables with consistency
 *   constraints that specify the values that each variable can
 *   assume. The conditions must be completely and consistently
 *   "bound" for the rule to be considered "matched".
 *
 * - Bindings, a set of unbound variables with consistency constraints
 *   that specify the values that each variable can assume. Unlike the
 *   conditions, the bindings need not be bound for the rule to be
 *   considered matched.
 *
 * - Content that should be constructed when the rule is "activated".
 *
 */
class nsTemplateRule
{
public:
    nsTemplateRule(nsIContent* aRuleNode,
                   nsIContent* aAction,
                   nsTemplateQuerySet* aQuerySet);
    /**
     * The copy-constructor should only be called from nsTArray when appending
     * a new rule, otherwise things break because the copy constructor expects
     * mBindings and mConditions to be nullptr.
     */
    nsTemplateRule(const nsTemplateRule& aOtherRule);

    ~nsTemplateRule();

    /**
     * Return the <action> node that this rule was constructed from, or its
     * logical equivalent for shorthand syntaxes. That is, the parent node of
     * the content that should be generated for this rule.
     */
    nsIContent* GetAction() const { return mAction; }

    /**
     * Return the <rule> content node that this rule was constructed from.
     * @param aResult an out parameter, which will contain the rule node
     * @return NS_OK if no errors occur.
     */
    nsresult GetRuleNode(nsIDOMNode** aResult) const;

    void SetVars(nsIAtom* aRefVariable, nsIAtom* aMemberVariable)
    {
        mRefVariable = aRefVariable;
        mMemberVariable = aMemberVariable;
    }

    void SetRuleFilter(nsIXULTemplateRuleFilter* aRuleFilter)
    {
        mRuleFilter = aRuleFilter;
    }

    nsIAtom* GetTag() { return mTag; }
    void SetTag(nsIAtom* aTag) { mTag = aTag; }

    nsIAtom* GetMemberVariable() { return mMemberVariable; }

    /**
     * Set the first condition for the rule. Other conditions are linked
     * to it using the condition's SetNext method.
     */
    void SetCondition(nsTemplateCondition* aConditions);

    /**
     * Check if the result matches the rule by first looking at the conditions.
     * If the results is accepted by the conditions, the rule filter, if any
     * was set, is checked. If either check rejects a result, a match cannot
     * occur for this rule and result.
     */
    bool
    CheckMatch(nsIXULTemplateResult* aResult) const;

    /**
     * Determine if the rule has the specified binding
     */
    bool
    HasBinding(nsIAtom* aSourceVariable,
               nsAString& aExpr,
               nsIAtom* aTargetVariable) const;

    /**
     * Add a binding to the rule. A binding consists of an already-bound
     * source variable, and the RDF property that should be tested to
     * generate a target value. The target value is bound to a target
     * variable.
     *
     * @param aSourceVariable the source variable that will be used in
     *   the RDF query.
     * @param aExpr the expression that will be used in the query.
     * @param aTargetVariable the variable whose value will be bound
     *   to the RDF node that is returned when querying the binding
     * @return NS_OK if no errors occur.
     */
    nsresult AddBinding(nsIAtom* aSourceVariable,
                        nsAString& aExpr,
                        nsIAtom* aTargetVariable);

    /**
     * Inform the query processor of the bindings that are set for a rule.
     * This should be called after all the bindings for a rule are compiled.
     */
    nsresult
    AddBindingsToQueryProcessor(nsIXULTemplateQueryProcessor* aProcessor);

    void Traverse(nsCycleCollectionTraversalCallback &cb) const
    {
        cb.NoteXPCOMChild(mRuleNode);
        cb.NoteXPCOMChild(mAction);
    }

protected:

    struct Binding {
        nsCOMPtr<nsIAtom>        mSourceVariable;
        nsCOMPtr<nsIAtom>        mTargetVariable;
        nsString                 mExpr;
        Binding*                 mNext;
        Binding*                 mParent;
    };

    // backreference to the query set which owns this rule
    nsTemplateQuerySet* mQuerySet;

    // the <rule> node, or the <template> node if there is no <rule>
    nsCOMPtr<nsIDOMNode> mRuleNode;

    // the <action> node, or, if there is no <action>, the container node
    // which contains the content to generate
    nsCOMPtr<nsIContent> mAction;

    // the rule filter set by the builder's SetRuleFilter function
    nsCOMPtr<nsIXULTemplateRuleFilter> mRuleFilter;

    // indicates that the rule will only match when generating content 
    // to be inserted into a container with this tag
    nsCOMPtr<nsIAtom> mTag;

    // linked-list of the bindings for the rule, owned by the rule.
    Binding* mBindings;

    nsCOMPtr<nsIAtom> mRefVariable;
    nsCOMPtr<nsIAtom> mMemberVariable;

    nsTemplateCondition* mConditions; // owned by nsTemplateRule
};

/** nsTemplateQuerySet
 *
 *  A single <queryset> which holds the query node and the rules for it.
 *  All builders have at least one queryset, which may be created with an
 *  explicit <queryset> tag or implied if the tag is not used.
 *
 *  These queryset objects are created and owned by the builder in its
 *  mQuerySets array.
 */
class nsTemplateQuerySet
{
protected:
    nsTArray<nsTemplateRule> mRules;

    // a number which increments for each successive queryset. It is stored so
    // it can be used as an optimization when updating results so that it is
    // known where to insert them into a match.
    int32_t mPriority;

public:

    // <query> node
    nsCOMPtr<nsIContent> mQueryNode;

    // compiled opaque query object returned by the query processor's
    // CompileQuery call
    nsCOMPtr<nsISupports> mCompiledQuery;

    // indicates that the query will only generate content to be inserted into
    // a container with this tag
    nsCOMPtr<nsIAtom> mTag;

    explicit nsTemplateQuerySet(int32_t aPriority)
        : mPriority(aPriority)
    {
        MOZ_COUNT_CTOR(nsTemplateQuerySet);
    }

    ~nsTemplateQuerySet()
    {
        MOZ_COUNT_DTOR(nsTemplateQuerySet);
    }

    int32_t Priority() const
    {
        return mPriority;
    }

    nsIAtom* GetTag() { return mTag; }
    void SetTag(nsIAtom* aTag) { mTag = aTag; }

    nsTemplateRule* NewRule(nsIContent* aRuleNode,
                            nsIContent* aAction,
                            nsTemplateQuerySet* aQuerySet)
    {
        // nsTemplateMatch stores the index as a 16-bit value,
        // so check to make sure for overflow
        if (mRules.Length() == INT16_MAX)
            return nullptr;

        return mRules.AppendElement(nsTemplateRule(aRuleNode, aAction,
                                    aQuerySet));
    }
    
    void RemoveRule(nsTemplateRule *aRule)
    {
        mRules.RemoveElementAt(aRule - mRules.Elements());
    }

    int16_t RuleCount() const
    {
        return mRules.Length();
    }

    nsTemplateRule* GetRuleAt(int16_t aIndex)
    {
        if (uint32_t(aIndex) < mRules.Length()) {
            return &mRules[aIndex];
        }
        return nullptr;
    }

    void Clear()
    {
        mRules.Clear();
    }
};

#endif // nsTemplateRule_h__