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