|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsTemplateRule.h" |
|
7 #include "nsTemplateMatch.h" |
|
8 #include "nsXULContentUtils.h" |
|
9 #include "nsUnicharUtils.h" |
|
10 #include "nsReadableUtils.h" |
|
11 #include "nsICollation.h" |
|
12 |
|
13 nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable, |
|
14 const nsAString& aRelation, |
|
15 nsIAtom* aTargetVariable, |
|
16 bool aIgnoreCase, |
|
17 bool aNegate) |
|
18 : mSourceVariable(aSourceVariable), |
|
19 mTargetVariable(aTargetVariable), |
|
20 mIgnoreCase(aIgnoreCase), |
|
21 mNegate(aNegate), |
|
22 mNext(nullptr) |
|
23 { |
|
24 SetRelation(aRelation); |
|
25 |
|
26 MOZ_COUNT_CTOR(nsTemplateCondition); |
|
27 } |
|
28 |
|
29 nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable, |
|
30 const nsAString& aRelation, |
|
31 const nsAString& aTargets, |
|
32 bool aIgnoreCase, |
|
33 bool aNegate, |
|
34 bool aIsMultiple) |
|
35 : mSourceVariable(aSourceVariable), |
|
36 mIgnoreCase(aIgnoreCase), |
|
37 mNegate(aNegate), |
|
38 mNext(nullptr) |
|
39 { |
|
40 SetRelation(aRelation); |
|
41 |
|
42 if (aIsMultiple) { |
|
43 int32_t start = 0, end = 0; |
|
44 while ((end = aTargets.FindChar(',',start)) >= 0) { |
|
45 if (end > start) { |
|
46 mTargetList.AppendElement(Substring(aTargets, start, end - start)); |
|
47 } |
|
48 start = end + 1; |
|
49 } |
|
50 if (start < int32_t(aTargets.Length())) { |
|
51 mTargetList.AppendElement(Substring(aTargets, start)); |
|
52 } |
|
53 } |
|
54 else { |
|
55 mTargetList.AppendElement(aTargets); |
|
56 } |
|
57 |
|
58 MOZ_COUNT_CTOR(nsTemplateCondition); |
|
59 } |
|
60 |
|
61 nsTemplateCondition::nsTemplateCondition(const nsAString& aSource, |
|
62 const nsAString& aRelation, |
|
63 nsIAtom* aTargetVariable, |
|
64 bool aIgnoreCase, |
|
65 bool aNegate) |
|
66 : mSource(aSource), |
|
67 mTargetVariable(aTargetVariable), |
|
68 mIgnoreCase(aIgnoreCase), |
|
69 mNegate(aNegate), |
|
70 mNext(nullptr) |
|
71 { |
|
72 SetRelation(aRelation); |
|
73 |
|
74 MOZ_COUNT_CTOR(nsTemplateCondition); |
|
75 } |
|
76 |
|
77 void |
|
78 nsTemplateCondition::SetRelation(const nsAString& aRelation) |
|
79 { |
|
80 if (aRelation.EqualsLiteral("equals") || aRelation.IsEmpty()) |
|
81 mRelation = eEquals; |
|
82 else if (aRelation.EqualsLiteral("less")) |
|
83 mRelation = eLess; |
|
84 else if (aRelation.EqualsLiteral("greater")) |
|
85 mRelation = eGreater; |
|
86 else if (aRelation.EqualsLiteral("before")) |
|
87 mRelation = eBefore; |
|
88 else if (aRelation.EqualsLiteral("after")) |
|
89 mRelation = eAfter; |
|
90 else if (aRelation.EqualsLiteral("startswith")) |
|
91 mRelation = eStartswith; |
|
92 else if (aRelation.EqualsLiteral("endswith")) |
|
93 mRelation = eEndswith; |
|
94 else if (aRelation.EqualsLiteral("contains")) |
|
95 mRelation = eContains; |
|
96 else |
|
97 mRelation = eUnknown; |
|
98 } |
|
99 |
|
100 bool |
|
101 nsTemplateCondition::CheckMatch(nsIXULTemplateResult* aResult) |
|
102 { |
|
103 bool match = false; |
|
104 |
|
105 nsAutoString leftString; |
|
106 if (mSourceVariable) |
|
107 aResult->GetBindingFor(mSourceVariable, leftString); |
|
108 else |
|
109 leftString.Assign(mSource); |
|
110 |
|
111 if (mTargetVariable) { |
|
112 nsAutoString rightString; |
|
113 aResult->GetBindingFor(mTargetVariable, rightString); |
|
114 |
|
115 match = CheckMatchStrings(leftString, rightString); |
|
116 } |
|
117 else { |
|
118 // iterate over the strings in the target and determine |
|
119 // whether there is a match. |
|
120 uint32_t length = mTargetList.Length(); |
|
121 for (uint32_t t = 0; t < length; t++) { |
|
122 match = CheckMatchStrings(leftString, mTargetList[t]); |
|
123 |
|
124 // stop once a match is found. In negate mode, stop once a |
|
125 // target does not match. |
|
126 if (match != mNegate) break; |
|
127 } |
|
128 } |
|
129 |
|
130 return match; |
|
131 } |
|
132 |
|
133 |
|
134 bool |
|
135 nsTemplateCondition::CheckMatchStrings(const nsAString& aLeftString, |
|
136 const nsAString& aRightString) |
|
137 { |
|
138 bool match = false; |
|
139 |
|
140 if (aRightString.IsEmpty()) { |
|
141 if ((mRelation == eEquals) && aLeftString.IsEmpty()) |
|
142 match = true; |
|
143 } |
|
144 else { |
|
145 switch (mRelation) { |
|
146 case eEquals: |
|
147 if (mIgnoreCase) |
|
148 match = aLeftString.Equals(aRightString, |
|
149 nsCaseInsensitiveStringComparator()); |
|
150 else |
|
151 match = aLeftString.Equals(aRightString); |
|
152 break; |
|
153 |
|
154 case eLess: |
|
155 case eGreater: |
|
156 { |
|
157 // non-numbers always compare false |
|
158 nsresult err; |
|
159 int32_t leftint = PromiseFlatString(aLeftString).ToInteger(&err); |
|
160 if (NS_SUCCEEDED(err)) { |
|
161 int32_t rightint = PromiseFlatString(aRightString).ToInteger(&err); |
|
162 if (NS_SUCCEEDED(err)) { |
|
163 match = (mRelation == eLess) ? (leftint < rightint) : |
|
164 (leftint > rightint); |
|
165 } |
|
166 } |
|
167 |
|
168 break; |
|
169 } |
|
170 |
|
171 case eBefore: |
|
172 { |
|
173 nsICollation* collation = nsXULContentUtils::GetCollation(); |
|
174 if (collation) { |
|
175 int32_t sortOrder; |
|
176 collation->CompareString((mIgnoreCase ? |
|
177 static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) : |
|
178 static_cast<int32_t>(nsICollation::kCollationCaseSensitive)), |
|
179 aLeftString, |
|
180 aRightString, |
|
181 &sortOrder); |
|
182 match = (sortOrder < 0); |
|
183 } |
|
184 else if (mIgnoreCase) { |
|
185 match = (Compare(aLeftString, aRightString, |
|
186 nsCaseInsensitiveStringComparator()) < 0); |
|
187 } |
|
188 else { |
|
189 match = (Compare(aLeftString, aRightString) < 0); |
|
190 } |
|
191 break; |
|
192 } |
|
193 |
|
194 case eAfter: |
|
195 { |
|
196 nsICollation* collation = nsXULContentUtils::GetCollation(); |
|
197 if (collation) { |
|
198 int32_t sortOrder; |
|
199 collation->CompareString((mIgnoreCase ? |
|
200 static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) : |
|
201 static_cast<int32_t>(nsICollation::kCollationCaseSensitive)), |
|
202 aLeftString, |
|
203 aRightString, |
|
204 &sortOrder); |
|
205 match = (sortOrder > 0); |
|
206 } |
|
207 else if (mIgnoreCase) { |
|
208 match = (Compare(aLeftString, aRightString, |
|
209 nsCaseInsensitiveStringComparator()) > 0); |
|
210 } |
|
211 else { |
|
212 match = (Compare(aLeftString, aRightString) > 0); |
|
213 } |
|
214 break; |
|
215 } |
|
216 |
|
217 case eStartswith: |
|
218 if (mIgnoreCase) |
|
219 match = (StringBeginsWith(aLeftString, aRightString, |
|
220 nsCaseInsensitiveStringComparator())); |
|
221 else |
|
222 match = (StringBeginsWith(aLeftString, aRightString)); |
|
223 break; |
|
224 |
|
225 case eEndswith: |
|
226 if (mIgnoreCase) |
|
227 match = (StringEndsWith(aLeftString, aRightString, |
|
228 nsCaseInsensitiveStringComparator())); |
|
229 else |
|
230 match = (StringEndsWith(aLeftString, aRightString)); |
|
231 break; |
|
232 |
|
233 case eContains: |
|
234 { |
|
235 nsAString::const_iterator start, end; |
|
236 aLeftString.BeginReading(start); |
|
237 aLeftString.EndReading(end); |
|
238 if (mIgnoreCase) |
|
239 match = CaseInsensitiveFindInReadable(aRightString, start, end); |
|
240 else |
|
241 match = FindInReadable(aRightString, start, end); |
|
242 break; |
|
243 } |
|
244 |
|
245 default: |
|
246 break; |
|
247 } |
|
248 } |
|
249 |
|
250 if (mNegate) match = !match; |
|
251 |
|
252 return match; |
|
253 } |
|
254 |
|
255 nsTemplateRule::nsTemplateRule(nsIContent* aRuleNode, |
|
256 nsIContent* aAction, |
|
257 nsTemplateQuerySet* aQuerySet) |
|
258 : mQuerySet(aQuerySet), |
|
259 mAction(aAction), |
|
260 mBindings(nullptr), |
|
261 mConditions(nullptr) |
|
262 { |
|
263 MOZ_COUNT_CTOR(nsTemplateRule); |
|
264 mRuleNode = do_QueryInterface(aRuleNode); |
|
265 } |
|
266 |
|
267 nsTemplateRule::nsTemplateRule(const nsTemplateRule& aOtherRule) |
|
268 : mQuerySet(aOtherRule.mQuerySet), |
|
269 mRuleNode(aOtherRule.mRuleNode), |
|
270 mAction(aOtherRule.mAction), |
|
271 mBindings(nullptr), |
|
272 mConditions(nullptr) |
|
273 { |
|
274 MOZ_COUNT_CTOR(nsTemplateRule); |
|
275 } |
|
276 |
|
277 nsTemplateRule::~nsTemplateRule() |
|
278 { |
|
279 MOZ_COUNT_DTOR(nsTemplateRule); |
|
280 |
|
281 while (mBindings) { |
|
282 Binding* doomed = mBindings; |
|
283 mBindings = mBindings->mNext; |
|
284 delete doomed; |
|
285 } |
|
286 |
|
287 while (mConditions) { |
|
288 nsTemplateCondition* cdel = mConditions; |
|
289 mConditions = mConditions->GetNext(); |
|
290 delete cdel; |
|
291 } |
|
292 } |
|
293 |
|
294 nsresult |
|
295 nsTemplateRule::GetRuleNode(nsIDOMNode** aRuleNode) const |
|
296 { |
|
297 *aRuleNode = mRuleNode; |
|
298 NS_IF_ADDREF(*aRuleNode); |
|
299 return NS_OK; |
|
300 } |
|
301 |
|
302 void nsTemplateRule::SetCondition(nsTemplateCondition* aCondition) |
|
303 { |
|
304 while (mConditions) { |
|
305 nsTemplateCondition* cdel = mConditions; |
|
306 mConditions = mConditions->GetNext(); |
|
307 delete cdel; |
|
308 } |
|
309 |
|
310 mConditions = aCondition; |
|
311 } |
|
312 |
|
313 bool |
|
314 nsTemplateRule::CheckMatch(nsIXULTemplateResult* aResult) const |
|
315 { |
|
316 // check the conditions in the rule first |
|
317 nsTemplateCondition* condition = mConditions; |
|
318 while (condition) { |
|
319 if (!condition->CheckMatch(aResult)) |
|
320 return false; |
|
321 |
|
322 condition = condition->GetNext(); |
|
323 } |
|
324 |
|
325 if (mRuleFilter) { |
|
326 // if a rule filter was set, check it for a match. If an error occurs, |
|
327 // assume that the match was acceptable |
|
328 bool match; |
|
329 nsresult rv = mRuleFilter->Match(aResult, mRuleNode, &match); |
|
330 return NS_FAILED(rv) || match; |
|
331 } |
|
332 |
|
333 return true; |
|
334 } |
|
335 |
|
336 bool |
|
337 nsTemplateRule::HasBinding(nsIAtom* aSourceVariable, |
|
338 nsAString& aExpr, |
|
339 nsIAtom* aTargetVariable) const |
|
340 { |
|
341 for (Binding* binding = mBindings; binding != nullptr; binding = binding->mNext) { |
|
342 if ((binding->mSourceVariable == aSourceVariable) && |
|
343 (binding->mExpr.Equals(aExpr)) && |
|
344 (binding->mTargetVariable == aTargetVariable)) |
|
345 return true; |
|
346 } |
|
347 |
|
348 return false; |
|
349 } |
|
350 |
|
351 nsresult |
|
352 nsTemplateRule::AddBinding(nsIAtom* aSourceVariable, |
|
353 nsAString& aExpr, |
|
354 nsIAtom* aTargetVariable) |
|
355 { |
|
356 NS_PRECONDITION(aSourceVariable != 0, "no source variable!"); |
|
357 if (! aSourceVariable) |
|
358 return NS_ERROR_INVALID_ARG; |
|
359 |
|
360 NS_PRECONDITION(aTargetVariable != 0, "no target variable!"); |
|
361 if (! aTargetVariable) |
|
362 return NS_ERROR_INVALID_ARG; |
|
363 |
|
364 NS_ASSERTION(! HasBinding(aSourceVariable, aExpr, aTargetVariable), |
|
365 "binding added twice"); |
|
366 |
|
367 Binding* newbinding = new Binding; |
|
368 if (! newbinding) |
|
369 return NS_ERROR_OUT_OF_MEMORY; |
|
370 |
|
371 newbinding->mSourceVariable = aSourceVariable; |
|
372 newbinding->mTargetVariable = aTargetVariable; |
|
373 newbinding->mParent = nullptr; |
|
374 |
|
375 newbinding->mExpr.Assign(aExpr); |
|
376 |
|
377 Binding* binding = mBindings; |
|
378 Binding** link = &mBindings; |
|
379 |
|
380 // Insert it at the end, unless we detect that an existing |
|
381 // binding's source is dependent on the newbinding's target. |
|
382 // |
|
383 // XXXwaterson this isn't enough to make sure that we get all of |
|
384 // the dependencies worked out right, but it'll do for now. For |
|
385 // example, if you have (ab, bc, cd), and insert them in the order |
|
386 // (cd, ab, bc), you'll get (bc, cd, ab). The good news is, if the |
|
387 // person uses a natural ordering when writing the XUL, it'll all |
|
388 // work out ok. |
|
389 while (binding) { |
|
390 if (binding->mSourceVariable == newbinding->mTargetVariable) { |
|
391 binding->mParent = newbinding; |
|
392 break; |
|
393 } |
|
394 else if (binding->mTargetVariable == newbinding->mSourceVariable) { |
|
395 newbinding->mParent = binding; |
|
396 } |
|
397 |
|
398 link = &binding->mNext; |
|
399 binding = binding->mNext; |
|
400 } |
|
401 |
|
402 // Insert the newbinding |
|
403 *link = newbinding; |
|
404 newbinding->mNext = binding; |
|
405 return NS_OK; |
|
406 } |
|
407 |
|
408 nsresult |
|
409 nsTemplateRule::AddBindingsToQueryProcessor(nsIXULTemplateQueryProcessor* aProcessor) |
|
410 { |
|
411 Binding* binding = mBindings; |
|
412 |
|
413 while (binding) { |
|
414 nsresult rv = aProcessor->AddBinding(mRuleNode, binding->mTargetVariable, |
|
415 binding->mSourceVariable, binding->mExpr); |
|
416 if (NS_FAILED(rv)) return rv; |
|
417 |
|
418 binding = binding->mNext; |
|
419 } |
|
420 |
|
421 return NS_OK; |
|
422 } |