tenfourfox/dom/xul/templates/nsTemplateRule.cpp
Cameron Kaiser c9b2922b70 hello FPR
2017-04-19 00:56:45 -07:00

423 lines
13 KiB
C++

/* -*- 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/. */
#include "nsTemplateRule.h"
#include "nsTemplateMatch.h"
#include "nsXULContentUtils.h"
#include "nsUnicharUtils.h"
#include "nsReadableUtils.h"
#include "nsICollation.h"
nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable,
const nsAString& aRelation,
nsIAtom* aTargetVariable,
bool aIgnoreCase,
bool aNegate)
: mSourceVariable(aSourceVariable),
mTargetVariable(aTargetVariable),
mIgnoreCase(aIgnoreCase),
mNegate(aNegate),
mNext(nullptr)
{
SetRelation(aRelation);
MOZ_COUNT_CTOR(nsTemplateCondition);
}
nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable,
const nsAString& aRelation,
const nsAString& aTargets,
bool aIgnoreCase,
bool aNegate,
bool aIsMultiple)
: mSourceVariable(aSourceVariable),
mIgnoreCase(aIgnoreCase),
mNegate(aNegate),
mNext(nullptr)
{
SetRelation(aRelation);
if (aIsMultiple) {
int32_t start = 0, end = 0;
while ((end = aTargets.FindChar(',',start)) >= 0) {
if (end > start) {
mTargetList.AppendElement(Substring(aTargets, start, end - start));
}
start = end + 1;
}
if (start < int32_t(aTargets.Length())) {
mTargetList.AppendElement(Substring(aTargets, start));
}
}
else {
mTargetList.AppendElement(aTargets);
}
MOZ_COUNT_CTOR(nsTemplateCondition);
}
nsTemplateCondition::nsTemplateCondition(const nsAString& aSource,
const nsAString& aRelation,
nsIAtom* aTargetVariable,
bool aIgnoreCase,
bool aNegate)
: mSource(aSource),
mTargetVariable(aTargetVariable),
mIgnoreCase(aIgnoreCase),
mNegate(aNegate),
mNext(nullptr)
{
SetRelation(aRelation);
MOZ_COUNT_CTOR(nsTemplateCondition);
}
void
nsTemplateCondition::SetRelation(const nsAString& aRelation)
{
if (aRelation.EqualsLiteral("equals") || aRelation.IsEmpty())
mRelation = eEquals;
else if (aRelation.EqualsLiteral("less"))
mRelation = eLess;
else if (aRelation.EqualsLiteral("greater"))
mRelation = eGreater;
else if (aRelation.EqualsLiteral("before"))
mRelation = eBefore;
else if (aRelation.EqualsLiteral("after"))
mRelation = eAfter;
else if (aRelation.EqualsLiteral("startswith"))
mRelation = eStartswith;
else if (aRelation.EqualsLiteral("endswith"))
mRelation = eEndswith;
else if (aRelation.EqualsLiteral("contains"))
mRelation = eContains;
else
mRelation = eUnknown;
}
bool
nsTemplateCondition::CheckMatch(nsIXULTemplateResult* aResult)
{
bool match = false;
nsAutoString leftString;
if (mSourceVariable)
aResult->GetBindingFor(mSourceVariable, leftString);
else
leftString.Assign(mSource);
if (mTargetVariable) {
nsAutoString rightString;
aResult->GetBindingFor(mTargetVariable, rightString);
match = CheckMatchStrings(leftString, rightString);
}
else {
// iterate over the strings in the target and determine
// whether there is a match.
uint32_t length = mTargetList.Length();
for (uint32_t t = 0; t < length; t++) {
match = CheckMatchStrings(leftString, mTargetList[t]);
// stop once a match is found. In negate mode, stop once a
// target does not match.
if (match != mNegate) break;
}
}
return match;
}
bool
nsTemplateCondition::CheckMatchStrings(const nsAString& aLeftString,
const nsAString& aRightString)
{
bool match = false;
if (aRightString.IsEmpty()) {
if ((mRelation == eEquals) && aLeftString.IsEmpty())
match = true;
}
else {
switch (mRelation) {
case eEquals:
if (mIgnoreCase)
match = aLeftString.Equals(aRightString,
nsCaseInsensitiveStringComparator());
else
match = aLeftString.Equals(aRightString);
break;
case eLess:
case eGreater:
{
// non-numbers always compare false
nsresult err;
int32_t leftint = PromiseFlatString(aLeftString).ToInteger(&err);
if (NS_SUCCEEDED(err)) {
int32_t rightint = PromiseFlatString(aRightString).ToInteger(&err);
if (NS_SUCCEEDED(err)) {
match = (mRelation == eLess) ? (leftint < rightint) :
(leftint > rightint);
}
}
break;
}
case eBefore:
{
nsICollation* collation = nsXULContentUtils::GetCollation();
if (collation) {
int32_t sortOrder;
collation->CompareString((mIgnoreCase ?
static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) :
static_cast<int32_t>(nsICollation::kCollationCaseSensitive)),
aLeftString,
aRightString,
&sortOrder);
match = (sortOrder < 0);
}
else if (mIgnoreCase) {
match = (Compare(aLeftString, aRightString,
nsCaseInsensitiveStringComparator()) < 0);
}
else {
match = (Compare(aLeftString, aRightString) < 0);
}
break;
}
case eAfter:
{
nsICollation* collation = nsXULContentUtils::GetCollation();
if (collation) {
int32_t sortOrder;
collation->CompareString((mIgnoreCase ?
static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) :
static_cast<int32_t>(nsICollation::kCollationCaseSensitive)),
aLeftString,
aRightString,
&sortOrder);
match = (sortOrder > 0);
}
else if (mIgnoreCase) {
match = (Compare(aLeftString, aRightString,
nsCaseInsensitiveStringComparator()) > 0);
}
else {
match = (Compare(aLeftString, aRightString) > 0);
}
break;
}
case eStartswith:
if (mIgnoreCase)
match = (StringBeginsWith(aLeftString, aRightString,
nsCaseInsensitiveStringComparator()));
else
match = (StringBeginsWith(aLeftString, aRightString));
break;
case eEndswith:
if (mIgnoreCase)
match = (StringEndsWith(aLeftString, aRightString,
nsCaseInsensitiveStringComparator()));
else
match = (StringEndsWith(aLeftString, aRightString));
break;
case eContains:
{
nsAString::const_iterator start, end;
aLeftString.BeginReading(start);
aLeftString.EndReading(end);
if (mIgnoreCase)
match = CaseInsensitiveFindInReadable(aRightString, start, end);
else
match = FindInReadable(aRightString, start, end);
break;
}
default:
break;
}
}
if (mNegate) match = !match;
return match;
}
nsTemplateRule::nsTemplateRule(nsIContent* aRuleNode,
nsIContent* aAction,
nsTemplateQuerySet* aQuerySet)
: mQuerySet(aQuerySet),
mAction(aAction),
mBindings(nullptr),
mConditions(nullptr)
{
MOZ_COUNT_CTOR(nsTemplateRule);
mRuleNode = do_QueryInterface(aRuleNode);
}
nsTemplateRule::nsTemplateRule(const nsTemplateRule& aOtherRule)
: mQuerySet(aOtherRule.mQuerySet),
mRuleNode(aOtherRule.mRuleNode),
mAction(aOtherRule.mAction),
mBindings(nullptr),
mConditions(nullptr)
{
MOZ_COUNT_CTOR(nsTemplateRule);
}
nsTemplateRule::~nsTemplateRule()
{
MOZ_COUNT_DTOR(nsTemplateRule);
while (mBindings) {
Binding* doomed = mBindings;
mBindings = mBindings->mNext;
delete doomed;
}
while (mConditions) {
nsTemplateCondition* cdel = mConditions;
mConditions = mConditions->GetNext();
delete cdel;
}
}
nsresult
nsTemplateRule::GetRuleNode(nsIDOMNode** aRuleNode) const
{
*aRuleNode = mRuleNode;
NS_IF_ADDREF(*aRuleNode);
return NS_OK;
}
void nsTemplateRule::SetCondition(nsTemplateCondition* aCondition)
{
while (mConditions) {
nsTemplateCondition* cdel = mConditions;
mConditions = mConditions->GetNext();
delete cdel;
}
mConditions = aCondition;
}
bool
nsTemplateRule::CheckMatch(nsIXULTemplateResult* aResult) const
{
// check the conditions in the rule first
nsTemplateCondition* condition = mConditions;
while (condition) {
if (!condition->CheckMatch(aResult))
return false;
condition = condition->GetNext();
}
if (mRuleFilter) {
// if a rule filter was set, check it for a match. If an error occurs,
// assume that the match was acceptable
bool match;
nsresult rv = mRuleFilter->Match(aResult, mRuleNode, &match);
return NS_FAILED(rv) || match;
}
return true;
}
bool
nsTemplateRule::HasBinding(nsIAtom* aSourceVariable,
nsAString& aExpr,
nsIAtom* aTargetVariable) const
{
for (Binding* binding = mBindings; binding != nullptr; binding = binding->mNext) {
if ((binding->mSourceVariable == aSourceVariable) &&
(binding->mExpr.Equals(aExpr)) &&
(binding->mTargetVariable == aTargetVariable))
return true;
}
return false;
}
nsresult
nsTemplateRule::AddBinding(nsIAtom* aSourceVariable,
nsAString& aExpr,
nsIAtom* aTargetVariable)
{
NS_PRECONDITION(aSourceVariable != 0, "no source variable!");
if (! aSourceVariable)
return NS_ERROR_INVALID_ARG;
NS_PRECONDITION(aTargetVariable != 0, "no target variable!");
if (! aTargetVariable)
return NS_ERROR_INVALID_ARG;
NS_ASSERTION(! HasBinding(aSourceVariable, aExpr, aTargetVariable),
"binding added twice");
Binding* newbinding = new Binding;
if (! newbinding)
return NS_ERROR_OUT_OF_MEMORY;
newbinding->mSourceVariable = aSourceVariable;
newbinding->mTargetVariable = aTargetVariable;
newbinding->mParent = nullptr;
newbinding->mExpr.Assign(aExpr);
Binding* binding = mBindings;
Binding** link = &mBindings;
// Insert it at the end, unless we detect that an existing
// binding's source is dependent on the newbinding's target.
//
// XXXwaterson this isn't enough to make sure that we get all of
// the dependencies worked out right, but it'll do for now. For
// example, if you have (ab, bc, cd), and insert them in the order
// (cd, ab, bc), you'll get (bc, cd, ab). The good news is, if the
// person uses a natural ordering when writing the XUL, it'll all
// work out ok.
while (binding) {
if (binding->mSourceVariable == newbinding->mTargetVariable) {
binding->mParent = newbinding;
break;
}
else if (binding->mTargetVariable == newbinding->mSourceVariable) {
newbinding->mParent = binding;
}
link = &binding->mNext;
binding = binding->mNext;
}
// Insert the newbinding
*link = newbinding;
newbinding->mNext = binding;
return NS_OK;
}
nsresult
nsTemplateRule::AddBindingsToQueryProcessor(nsIXULTemplateQueryProcessor* aProcessor)
{
Binding* binding = mBindings;
while (binding) {
nsresult rv = aProcessor->AddBinding(mRuleNode, binding->mTargetVariable,
binding->mSourceVariable, binding->mExpr);
if (NS_FAILED(rv)) return rv;
binding = binding->mNext;
}
return NS_OK;
}