content/xul/templates/src/nsTemplateRule.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/xul/templates/src/nsTemplateRule.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,422 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsTemplateRule.h"
    1.10 +#include "nsTemplateMatch.h"
    1.11 +#include "nsXULContentUtils.h"
    1.12 +#include "nsUnicharUtils.h"
    1.13 +#include "nsReadableUtils.h"
    1.14 +#include "nsICollation.h"
    1.15 +
    1.16 +nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable,
    1.17 +                                         const nsAString& aRelation,
    1.18 +                                         nsIAtom* aTargetVariable,
    1.19 +                                         bool aIgnoreCase,
    1.20 +                                         bool aNegate)
    1.21 +    : mSourceVariable(aSourceVariable),
    1.22 +      mTargetVariable(aTargetVariable),
    1.23 +      mIgnoreCase(aIgnoreCase),
    1.24 +      mNegate(aNegate),
    1.25 +      mNext(nullptr)
    1.26 +{
    1.27 +    SetRelation(aRelation);
    1.28 +
    1.29 +    MOZ_COUNT_CTOR(nsTemplateCondition);
    1.30 +}
    1.31 +
    1.32 +nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable,
    1.33 +                                         const nsAString& aRelation,
    1.34 +                                         const nsAString& aTargets,
    1.35 +                                         bool aIgnoreCase,
    1.36 +                                         bool aNegate,
    1.37 +                                         bool aIsMultiple)
    1.38 +    : mSourceVariable(aSourceVariable),
    1.39 +      mIgnoreCase(aIgnoreCase),
    1.40 +      mNegate(aNegate),
    1.41 +      mNext(nullptr)
    1.42 +{
    1.43 +    SetRelation(aRelation);
    1.44 +
    1.45 +    if (aIsMultiple) {
    1.46 +        int32_t start = 0, end = 0;
    1.47 +        while ((end = aTargets.FindChar(',',start)) >= 0) {
    1.48 +            if (end > start) {
    1.49 +                mTargetList.AppendElement(Substring(aTargets, start, end - start));
    1.50 +            }
    1.51 +            start = end + 1;
    1.52 +        }
    1.53 +        if (start < int32_t(aTargets.Length())) {
    1.54 +            mTargetList.AppendElement(Substring(aTargets, start));
    1.55 +        }
    1.56 +    }
    1.57 +    else {
    1.58 +        mTargetList.AppendElement(aTargets);
    1.59 +    }
    1.60 +
    1.61 +    MOZ_COUNT_CTOR(nsTemplateCondition);
    1.62 +}
    1.63 +
    1.64 +nsTemplateCondition::nsTemplateCondition(const nsAString& aSource,
    1.65 +                                         const nsAString& aRelation,
    1.66 +                                         nsIAtom* aTargetVariable,
    1.67 +                                         bool aIgnoreCase,
    1.68 +                                         bool aNegate)
    1.69 +    : mSource(aSource),
    1.70 +      mTargetVariable(aTargetVariable),
    1.71 +      mIgnoreCase(aIgnoreCase),
    1.72 +      mNegate(aNegate),
    1.73 +      mNext(nullptr)
    1.74 +{
    1.75 +    SetRelation(aRelation);
    1.76 +
    1.77 +    MOZ_COUNT_CTOR(nsTemplateCondition);
    1.78 +}
    1.79 +
    1.80 +void
    1.81 +nsTemplateCondition::SetRelation(const nsAString& aRelation)
    1.82 +{
    1.83 +    if (aRelation.EqualsLiteral("equals") || aRelation.IsEmpty())
    1.84 +        mRelation = eEquals;
    1.85 +    else if (aRelation.EqualsLiteral("less"))
    1.86 +        mRelation = eLess;
    1.87 +    else if (aRelation.EqualsLiteral("greater"))
    1.88 +        mRelation = eGreater;
    1.89 +    else if (aRelation.EqualsLiteral("before"))
    1.90 +        mRelation = eBefore;
    1.91 +    else if (aRelation.EqualsLiteral("after"))
    1.92 +        mRelation = eAfter;
    1.93 +    else if (aRelation.EqualsLiteral("startswith"))
    1.94 +        mRelation = eStartswith;
    1.95 +    else if (aRelation.EqualsLiteral("endswith"))
    1.96 +        mRelation = eEndswith;
    1.97 +    else if (aRelation.EqualsLiteral("contains"))
    1.98 +        mRelation = eContains;
    1.99 +    else
   1.100 +        mRelation = eUnknown;
   1.101 +}
   1.102 +
   1.103 +bool
   1.104 +nsTemplateCondition::CheckMatch(nsIXULTemplateResult* aResult)
   1.105 +{
   1.106 +    bool match = false;
   1.107 +
   1.108 +    nsAutoString leftString;
   1.109 +    if (mSourceVariable)
   1.110 +      aResult->GetBindingFor(mSourceVariable, leftString);
   1.111 +    else
   1.112 +      leftString.Assign(mSource);
   1.113 +
   1.114 +    if (mTargetVariable) {
   1.115 +        nsAutoString rightString;
   1.116 +        aResult->GetBindingFor(mTargetVariable, rightString);
   1.117 +
   1.118 +        match = CheckMatchStrings(leftString, rightString);
   1.119 +    }
   1.120 +    else {
   1.121 +        // iterate over the strings in the target and determine
   1.122 +        // whether there is a match.
   1.123 +        uint32_t length = mTargetList.Length();
   1.124 +        for (uint32_t t = 0; t < length; t++) {
   1.125 +            match = CheckMatchStrings(leftString, mTargetList[t]);
   1.126 +
   1.127 +            // stop once a match is found. In negate mode, stop once a
   1.128 +            // target does not match.
   1.129 +            if (match != mNegate) break;
   1.130 +        }
   1.131 +    }
   1.132 +
   1.133 +    return match;
   1.134 +}
   1.135 +
   1.136 +
   1.137 +bool
   1.138 +nsTemplateCondition::CheckMatchStrings(const nsAString& aLeftString,
   1.139 +                                       const nsAString& aRightString)
   1.140 +{
   1.141 +    bool match = false;
   1.142 +
   1.143 +    if (aRightString.IsEmpty()) {
   1.144 +        if ((mRelation == eEquals) && aLeftString.IsEmpty())
   1.145 +            match = true;
   1.146 +    }
   1.147 +    else {
   1.148 +        switch (mRelation) {
   1.149 +            case eEquals:
   1.150 +                if (mIgnoreCase)
   1.151 +                    match = aLeftString.Equals(aRightString,
   1.152 +                                               nsCaseInsensitiveStringComparator());
   1.153 +                else
   1.154 +                    match = aLeftString.Equals(aRightString);
   1.155 +                break;
   1.156 +
   1.157 +            case eLess:
   1.158 +            case eGreater:
   1.159 +            {
   1.160 +                // non-numbers always compare false
   1.161 +                nsresult err;
   1.162 +                int32_t leftint = PromiseFlatString(aLeftString).ToInteger(&err);
   1.163 +                if (NS_SUCCEEDED(err)) {
   1.164 +                    int32_t rightint = PromiseFlatString(aRightString).ToInteger(&err);
   1.165 +                    if (NS_SUCCEEDED(err)) {
   1.166 +                        match = (mRelation == eLess) ? (leftint < rightint) :
   1.167 +                                                       (leftint > rightint);
   1.168 +                    }
   1.169 +                }
   1.170 +
   1.171 +                break;
   1.172 +            }
   1.173 +
   1.174 +            case eBefore:
   1.175 +            {
   1.176 +                nsICollation* collation = nsXULContentUtils::GetCollation();
   1.177 +                if (collation) {
   1.178 +                    int32_t sortOrder;
   1.179 +                    collation->CompareString((mIgnoreCase ?
   1.180 +                                              static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) :
   1.181 +                                              static_cast<int32_t>(nsICollation::kCollationCaseSensitive)),
   1.182 +                                              aLeftString,
   1.183 +                                              aRightString,
   1.184 +                                              &sortOrder);
   1.185 +                    match = (sortOrder < 0);
   1.186 +                }
   1.187 +                else if (mIgnoreCase) {
   1.188 +                    match = (Compare(aLeftString, aRightString,
   1.189 +                                     nsCaseInsensitiveStringComparator()) < 0);
   1.190 +                }
   1.191 +                else {
   1.192 +                    match = (Compare(aLeftString, aRightString) < 0);
   1.193 +                }
   1.194 +                break;
   1.195 +            }
   1.196 +
   1.197 +            case eAfter:
   1.198 +            {
   1.199 +                nsICollation* collation = nsXULContentUtils::GetCollation();
   1.200 +                if (collation) {
   1.201 +                    int32_t sortOrder;
   1.202 +                    collation->CompareString((mIgnoreCase ?
   1.203 +                                              static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) :
   1.204 +                                              static_cast<int32_t>(nsICollation::kCollationCaseSensitive)),
   1.205 +                                              aLeftString,
   1.206 +                                              aRightString,
   1.207 +                                              &sortOrder);
   1.208 +                    match = (sortOrder > 0);
   1.209 +                }
   1.210 +                else if (mIgnoreCase) {
   1.211 +                    match = (Compare(aLeftString, aRightString,
   1.212 +                                     nsCaseInsensitiveStringComparator()) > 0);
   1.213 +                }
   1.214 +                else {
   1.215 +                    match = (Compare(aLeftString, aRightString) > 0);
   1.216 +                }
   1.217 +                break;
   1.218 +            }
   1.219 +
   1.220 +            case eStartswith:
   1.221 +                if (mIgnoreCase)
   1.222 +                    match = (StringBeginsWith(aLeftString, aRightString,
   1.223 +                                              nsCaseInsensitiveStringComparator()));
   1.224 +                else
   1.225 +                    match = (StringBeginsWith(aLeftString, aRightString));
   1.226 +                break;
   1.227 +
   1.228 +            case eEndswith:
   1.229 +                if (mIgnoreCase)
   1.230 +                    match = (StringEndsWith(aLeftString, aRightString,
   1.231 +                                            nsCaseInsensitiveStringComparator()));
   1.232 +                else
   1.233 +                    match = (StringEndsWith(aLeftString, aRightString));
   1.234 +                break;
   1.235 +
   1.236 +            case eContains:
   1.237 +            {
   1.238 +                nsAString::const_iterator start, end;
   1.239 +                aLeftString.BeginReading(start);
   1.240 +                aLeftString.EndReading(end);
   1.241 +                if (mIgnoreCase)
   1.242 +                    match = CaseInsensitiveFindInReadable(aRightString, start, end);
   1.243 +                else
   1.244 +                    match = FindInReadable(aRightString, start, end);
   1.245 +                break;
   1.246 +            }
   1.247 +
   1.248 +            default:
   1.249 +                break;
   1.250 +        }
   1.251 +    }
   1.252 +
   1.253 +    if (mNegate) match = !match;
   1.254 +
   1.255 +    return match;
   1.256 +}
   1.257 +
   1.258 +nsTemplateRule::nsTemplateRule(nsIContent* aRuleNode,
   1.259 +                               nsIContent* aAction,
   1.260 +                               nsTemplateQuerySet* aQuerySet)
   1.261 +        : mQuerySet(aQuerySet),
   1.262 +          mAction(aAction),
   1.263 +          mBindings(nullptr),
   1.264 +          mConditions(nullptr)
   1.265 +{
   1.266 +    MOZ_COUNT_CTOR(nsTemplateRule);
   1.267 +    mRuleNode = do_QueryInterface(aRuleNode);
   1.268 +}
   1.269 +
   1.270 +nsTemplateRule::nsTemplateRule(const nsTemplateRule& aOtherRule)
   1.271 +        : mQuerySet(aOtherRule.mQuerySet),
   1.272 +          mRuleNode(aOtherRule.mRuleNode),
   1.273 +          mAction(aOtherRule.mAction),
   1.274 +          mBindings(nullptr),
   1.275 +          mConditions(nullptr)
   1.276 +{
   1.277 +    MOZ_COUNT_CTOR(nsTemplateRule);
   1.278 +}
   1.279 +
   1.280 +nsTemplateRule::~nsTemplateRule()
   1.281 +{
   1.282 +    MOZ_COUNT_DTOR(nsTemplateRule);
   1.283 +    
   1.284 +    while (mBindings) {
   1.285 +        Binding* doomed = mBindings;
   1.286 +        mBindings = mBindings->mNext;
   1.287 +        delete doomed;
   1.288 +    }
   1.289 +
   1.290 +    while (mConditions) {
   1.291 +        nsTemplateCondition* cdel = mConditions;
   1.292 +        mConditions = mConditions->GetNext();
   1.293 +        delete cdel;
   1.294 +    }
   1.295 +}
   1.296 +
   1.297 +nsresult
   1.298 +nsTemplateRule::GetRuleNode(nsIDOMNode** aRuleNode) const
   1.299 +{
   1.300 +    *aRuleNode = mRuleNode;
   1.301 +    NS_IF_ADDREF(*aRuleNode);
   1.302 +    return NS_OK;
   1.303 +}
   1.304 +
   1.305 +void nsTemplateRule::SetCondition(nsTemplateCondition* aCondition)
   1.306 +{
   1.307 +    while (mConditions) {
   1.308 +        nsTemplateCondition* cdel = mConditions;
   1.309 +        mConditions = mConditions->GetNext();
   1.310 +        delete cdel;
   1.311 +    }
   1.312 +
   1.313 +    mConditions = aCondition;
   1.314 +}
   1.315 +
   1.316 +bool
   1.317 +nsTemplateRule::CheckMatch(nsIXULTemplateResult* aResult) const
   1.318 +{
   1.319 +    // check the conditions in the rule first
   1.320 +    nsTemplateCondition* condition = mConditions;
   1.321 +    while (condition) {
   1.322 +        if (!condition->CheckMatch(aResult))
   1.323 +            return false;
   1.324 +
   1.325 +        condition = condition->GetNext();
   1.326 +    }
   1.327 +
   1.328 +    if (mRuleFilter) {
   1.329 +        // if a rule filter was set, check it for a match. If an error occurs,
   1.330 +        // assume that the match was acceptable
   1.331 +        bool match;
   1.332 +        nsresult rv = mRuleFilter->Match(aResult, mRuleNode, &match);
   1.333 +        return NS_FAILED(rv) || match;
   1.334 +    }
   1.335 +
   1.336 +    return true;
   1.337 +}
   1.338 +
   1.339 +bool
   1.340 +nsTemplateRule::HasBinding(nsIAtom* aSourceVariable,
   1.341 +                           nsAString& aExpr,
   1.342 +                           nsIAtom* aTargetVariable) const
   1.343 +{
   1.344 +    for (Binding* binding = mBindings; binding != nullptr; binding = binding->mNext) {
   1.345 +        if ((binding->mSourceVariable == aSourceVariable) &&
   1.346 +            (binding->mExpr.Equals(aExpr)) &&
   1.347 +            (binding->mTargetVariable == aTargetVariable))
   1.348 +            return true;
   1.349 +    }
   1.350 +
   1.351 +    return false;
   1.352 +}
   1.353 +
   1.354 +nsresult
   1.355 +nsTemplateRule::AddBinding(nsIAtom* aSourceVariable,
   1.356 +                           nsAString& aExpr,
   1.357 +                           nsIAtom* aTargetVariable)
   1.358 +{
   1.359 +    NS_PRECONDITION(aSourceVariable != 0, "no source variable!");
   1.360 +    if (! aSourceVariable)
   1.361 +        return NS_ERROR_INVALID_ARG;
   1.362 +
   1.363 +    NS_PRECONDITION(aTargetVariable != 0, "no target variable!");
   1.364 +    if (! aTargetVariable)
   1.365 +        return NS_ERROR_INVALID_ARG;
   1.366 +
   1.367 +    NS_ASSERTION(! HasBinding(aSourceVariable, aExpr, aTargetVariable),
   1.368 +                 "binding added twice");
   1.369 +
   1.370 +    Binding* newbinding = new Binding;
   1.371 +    if (! newbinding)
   1.372 +        return NS_ERROR_OUT_OF_MEMORY;
   1.373 +
   1.374 +    newbinding->mSourceVariable = aSourceVariable;
   1.375 +    newbinding->mTargetVariable = aTargetVariable;
   1.376 +    newbinding->mParent         = nullptr;
   1.377 +
   1.378 +    newbinding->mExpr.Assign(aExpr);
   1.379 +
   1.380 +    Binding* binding = mBindings;
   1.381 +    Binding** link = &mBindings;
   1.382 +
   1.383 +    // Insert it at the end, unless we detect that an existing
   1.384 +    // binding's source is dependent on the newbinding's target.
   1.385 +    //
   1.386 +    // XXXwaterson this isn't enough to make sure that we get all of
   1.387 +    // the dependencies worked out right, but it'll do for now. For
   1.388 +    // example, if you have (ab, bc, cd), and insert them in the order
   1.389 +    // (cd, ab, bc), you'll get (bc, cd, ab). The good news is, if the
   1.390 +    // person uses a natural ordering when writing the XUL, it'll all
   1.391 +    // work out ok.
   1.392 +    while (binding) {
   1.393 +        if (binding->mSourceVariable == newbinding->mTargetVariable) {
   1.394 +            binding->mParent = newbinding;
   1.395 +            break;
   1.396 +        }
   1.397 +        else if (binding->mTargetVariable == newbinding->mSourceVariable) {
   1.398 +            newbinding->mParent = binding;
   1.399 +        }
   1.400 +
   1.401 +        link = &binding->mNext;
   1.402 +        binding = binding->mNext;
   1.403 +    }
   1.404 +
   1.405 +    // Insert the newbinding
   1.406 +    *link = newbinding;
   1.407 +    newbinding->mNext = binding;
   1.408 +    return NS_OK;
   1.409 +}
   1.410 +
   1.411 +nsresult
   1.412 +nsTemplateRule::AddBindingsToQueryProcessor(nsIXULTemplateQueryProcessor* aProcessor)
   1.413 +{
   1.414 +    Binding* binding = mBindings;
   1.415 +
   1.416 +    while (binding) {
   1.417 +        nsresult rv = aProcessor->AddBinding(mRuleNode, binding->mTargetVariable,
   1.418 +                                             binding->mSourceVariable, binding->mExpr);
   1.419 +        if (NS_FAILED(rv)) return rv;
   1.420 +
   1.421 +        binding = binding->mNext;
   1.422 +    }
   1.423 +
   1.424 +    return NS_OK;
   1.425 +}

mercurial