aaronr%us.ibm.com 60893804cc [XForms] NPOTDB schema validation testsuite fails. Bug 411194, p=doronr r=olli+aaronr
git-svn-id: svn://10.0.0.236/trunk@242608 18797224-902f-48f8-a5cc-f745e15eee43
2008-01-07 22:48:51 +00:00

4884 lines
159 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Schema Validation.
*
* The Initial Developer of the Original Code is
* IBM Corporation.
* Portions created by the Initial Developer are Copyright (C) 2004-2005
* IBM Corporation. All Rights Reserved.
*
* Contributor(s):
* Doron Rosenberg <doronr@us.ibm.com> (original author)
* Laurent Jouanneau <laurent@xulfr.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsSchemaValidator.h"
// content includes
#include "nsIContent.h"
#include "nsIDOMNode.h"
#include "nsIDOM3Node.h"
#include "nsIDOMElement.h"
#include "nsIDOMAttr.h"
#include "nsIDOMNodeList.h"
#include "nsIParserService.h"
#include "nsIDOMNamedNodeMap.h"
#include "nsDataHashtable.h"
#include "nsIVariant.h"
#include "nsIAttribute.h"
// string includes
#include "nsStringAPI.h"
#include "nsUnicharUtils.h"
// XPCOM includes
#include "nsMemory.h"
#include "nsIServiceManager.h"
#include "nsIComponentManager.h"
#include "nsIClassInfoImpl.h"
#include "nsISchema.h"
#include "nsISchemaLoader.h"
#include "nsSchemaValidatorUtils.h"
#include "nsNetUtil.h"
#include "prlog.h"
#include "nspr.h"
#include <stdlib.h>
#include <math.h>
#include <float.h>
#include "prprf.h"
#include "prtime.h"
#include "plbase64.h"
#include <ctype.h>
#include "nsIAtomService.h"
#define NS_SCHEMA_1999_NAMESPACE "http://www.w3.org/1999/XMLSchema"
#define NS_SCHEMA_2001_NAMESPACE "http://www.w3.org/2001/XMLSchema"
#define NS_SCHEMA_INSTANCE_NAMESPACE "http://www.w3.org/2001/XMLSchema-instance"
#ifdef PR_LOGGING
PRLogModuleInfo *gSchemaValidationLog = PR_NewLogModule("schemaValidation");
#define LOG(x) PR_LOG(gSchemaValidationLog, PR_LOG_DEBUG, x)
#define LOG_ENABLED() PR_LOG_TEST(gSchemaValidationLog, PR_LOG_DEBUG)
#else
#define LOG(x)
#endif
NS_IMPL_ISUPPORTS1_CI(nsSchemaValidator, nsISchemaValidator)
nsSchemaValidator::nsSchemaValidator() : mForceInvalid(PR_FALSE)
{
}
nsSchemaValidator::~nsSchemaValidator()
{
}
////////////////////////////////////////////////////////////
//
// nsSchemaValidator implementation
//
////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsSchemaValidator::LoadSchema(nsISchema* aSchema)
{
if (aSchema)
aSchema->GetCollection(getter_AddRefs(mSchema));
return NS_OK;
}
NS_IMETHODIMP
nsSchemaValidator::ValidateString(const nsAString & aValue,
const nsAString & aType,
const nsAString & aNamespace,
PRBool *aResult)
{
LOG(("--------- nsSchemaValidator::ValidateString called ---------"));
// empty type is invalid
if (aType.IsEmpty())
return NS_ERROR_NOT_AVAILABLE;
// no schemas loaded and type is not builtin, abort
if (!mSchema && !aNamespace.EqualsLiteral(NS_SCHEMA_1999_NAMESPACE) &&
!aNamespace.EqualsLiteral(NS_SCHEMA_2001_NAMESPACE))
return NS_ERROR_SCHEMAVALIDATOR_NO_SCHEMA_LOADED;
// figure out if it's a simple or complex type
nsCOMPtr<nsISchemaType> type;
nsresult rv = GetType(aType, aNamespace, getter_AddRefs(type));
NS_ENSURE_SUCCESS(rv, rv);
PRUint16 typevalue;
rv = type->GetSchemaType(&typevalue);
NS_ENSURE_SUCCESS(rv, rv);
PRBool isValid = PR_FALSE;
if (typevalue == nsISchemaType::SCHEMA_TYPE_SIMPLE) {
nsCOMPtr<nsISchemaSimpleType> simpleType = do_QueryInterface(type);
NS_ENSURE_TRUE(simpleType, NS_ERROR_FAILURE);
LOG((" Type is a simple type! \n String to validate is: |%s|",
NS_ConvertUTF16toUTF8(aValue).get()));
rv = ValidateSimpletype(aValue, simpleType, &isValid);
} else {
// if its not a simpletype, validating a string makes no sense.
rv = NS_ERROR_UNEXPECTED;
LOG((" -- unexpected type"));
}
#ifdef PR_LOGGING
if (!isValid)
LOG((" *** INVALID ***"));
#endif
*aResult = isValid;
return rv;
}
NS_IMETHODIMP
nsSchemaValidator::Validate(nsIDOMNode* aElement, PRBool *aResult)
{
LOG(("--------- nsSchemaValidator::Validate called ---------"));
if (!aElement)
return NS_ERROR_SCHEMAVALIDATOR_NO_DOM_NODE_SPECIFIED;
// init the override
mForceInvalid = PR_FALSE;
// will hold the type to validate against
nsCOMPtr<nsISchemaType> type;
nsresult rv = GetElementXsiType(aElement, getter_AddRefs(type));
NS_ENSURE_SUCCESS(rv, rv);
if (!type) {
if (!mSchema) {
// no type attribute and no loaded schemas, so abort
// XXX: needed better error here
return NS_ERROR_SCHEMAVALIDATOR_NO_SCHEMA_LOADED;
}
// no type attribute, look for an xsd:element in the schema that matches
LOG((" -- no type attribute found, so looking for matching xsd:element"));
// get namespace from node
nsAutoString schemaTypeNamespace;
rv = aElement->GetNamespaceURI(schemaTypeNamespace);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString localName;
rv = aElement->GetLocalName(localName);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISchemaElement> element;
mSchema->GetElement(localName, schemaTypeNamespace, getter_AddRefs(element));
if (!element) {
// no type and no matching element, abort
// XXX: needed better error here
return NS_ERROR_SCHEMAVALIDATOR_NO_SCHEMA_LOADED;
}
rv = element->GetType(getter_AddRefs(type));
NS_ENSURE_SUCCESS(rv, rv);
}
/*
* We allow the schema validator to continue validating a structure
* even if the nodevalue is invalid per its simpletype binding. This is done
* so that we continue to mark nodes with their types, even if we encounter
* an invalid nodevalue. We remember that we had an invalid nodevalue by
* using mForceInvalid as an override for the final return.
*/
PRBool isValid = PR_FALSE;
rv = ValidateAgainstType(aElement, type, &isValid);
*aResult = mForceInvalid ? PR_FALSE : isValid;
return rv;
}
// This is passed into setProperty so that the variant passed as
// an xsdtype is released when the property is destroyed
static void VariantDTor(void *aObject,
nsIAtom *aPropertyName,
void *aPropertyValue,
void *aData)
{
nsIVariant *pVariant = static_cast<nsIVariant *>(aPropertyValue);
NS_IF_RELEASE(pVariant);
}
NS_IMETHODIMP
nsSchemaValidator::ValidateAgainstType(nsIDOMNode* aElement,
nsISchemaType* aType,
PRBool *aResult)
{
if (!aElement)
return NS_ERROR_SCHEMAVALIDATOR_NO_DOM_NODE_SPECIFIED;
NS_ENSURE_STATE(aType);
PRUint16 typevalue;
nsresult rv = aType->GetSchemaType(&typevalue);
NS_ENSURE_SUCCESS(rv, rv);
PRBool isValid = PR_FALSE;
if (typevalue == nsISchemaType::SCHEMA_TYPE_SIMPLE) {
nsCOMPtr<nsISchemaSimpleType> simpleType = do_QueryInterface(aType);
if (simpleType) {
// get the nodevalue using DOM 3's textContent
nsAutoString nodeValue;
nsCOMPtr<nsIDOM3Node> domNode3 = do_QueryInterface(aElement);
domNode3->GetTextContent(nodeValue);
LOG((" Type is a simple type!"));
LOG((" String to validate is: |%s|",
NS_ConvertUTF16toUTF8(nodeValue).get()));
rv = ValidateSimpletype(nodeValue, simpleType, &isValid);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString typeName, nodeName;
rv = aType->GetName(typeName);
NS_ENSURE_SUCCESS(rv, rv);
rv = aElement->GetLocalName(nodeName);
NS_ENSURE_SUCCESS(rv, rv);
// go on validating, but remember we failed
if (!isValid) {
mForceInvalid = PR_TRUE;
isValid = PR_TRUE;
}
nsCOMPtr<nsIWritableVariant> holder =
do_CreateInstance("@mozilla.org/variant;1");
NS_ENSURE_STATE(holder);
holder->SetAsInterface(nsISchemaType::GetIID(), aType);
// and save on the node
nsCOMPtr<nsIContent> content = do_QueryInterface(domNode3);
NS_ENSURE_STATE(content);
// we have to be really careful to set the destructor function correctly.
// this also has to be a pointer to a variant
nsCOMPtr<nsIAtomService> atomServ =
do_GetService(NS_ATOMSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAtom> key;
rv = atomServ->GetAtomUTF8("xsdtype", getter_AddRefs(key));
NS_ENSURE_SUCCESS(rv, rv);
nsIVariant *pVariant = holder;
NS_IF_ADDREF(pVariant);
rv = content->SetProperty(key, pVariant, &VariantDTor);
NS_ENSURE_SUCCESS(rv, rv);
}
} else if (typevalue == nsISchemaType::SCHEMA_TYPE_COMPLEX) {
nsCOMPtr<nsISchemaComplexType> complexType = do_QueryInterface(aType);
if (complexType) {
LOG((" Type is a complex type!"));
rv = ValidateComplextype(aElement, complexType, &isValid);
}
} else {
rv = NS_ERROR_UNEXPECTED;
}
*aResult = isValid;
return rv;
}
/*
Returns the nsISchemaType for a given value/type/namespace pair.
*/
NS_IMETHODIMP
nsSchemaValidator::GetType(const nsAString & aType,
const nsAString & aNamespace,
nsISchemaType ** aSchemaType)
{
nsresult rv;
if (!mSchema) {
// if we are a built-in type, we can get a nsISchemaType for it from
// nsISchemaCollection->GetType.
nsCOMPtr<nsISchemaLoader> schemaLoader =
do_CreateInstance("@mozilla.org/xmlextras/schemas/schemaloader;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
mSchema = do_QueryInterface(schemaLoader);
NS_ENSURE_STATE(mSchema);
}
// First try looking for xsi:type
rv = mSchema->GetType(aType, aNamespace, aSchemaType);
NS_ENSURE_SUCCESS(rv, rv);
// maybe its a xsd:element
if (!*aSchemaType) {
nsCOMPtr<nsISchemaElement> schemaElement;
rv = mSchema->GetElement(aType, aNamespace, getter_AddRefs(schemaElement));
NS_ENSURE_SUCCESS(rv, rv);
if (schemaElement) {
rv = schemaElement->GetType(aSchemaType);
NS_ENSURE_SUCCESS(rv, rv);
}
if (!*aSchemaType) {
// if its not a built-in type as well, its an unknown schema type.
LOG((" Error: The Schema type was not found.\n"));
rv = NS_ERROR_SCHEMAVALIDATOR_TYPE_NOT_FOUND;
}
}
return rv;
}
nsresult
nsSchemaValidator::ValidateSimpletype(const nsAString & aNodeValue,
nsISchemaSimpleType *aSchemaSimpleType,
PRBool *aResult)
{
NS_ENSURE_ARG(aSchemaSimpleType);
// get the simpletype type.
PRUint16 simpleTypeValue;
nsresult rv = aSchemaSimpleType->GetSimpleType(&simpleTypeValue);
NS_ENSURE_SUCCESS(rv, rv);
PRBool isValid = PR_FALSE;
switch (simpleTypeValue) {
case nsISchemaSimpleType::SIMPLE_TYPE_RESTRICTION: {
// handle simpletypes with restrictions
rv = ValidateRestrictionSimpletype(aNodeValue, aSchemaSimpleType, &isValid);
break;
}
case nsISchemaSimpleType::SIMPLE_TYPE_BUILTIN: {
// handle built-in types
rv = ValidateBuiltinType(aNodeValue, aSchemaSimpleType, &isValid);
break;
}
case nsISchemaSimpleType::SIMPLE_TYPE_LIST: {
// handle lists
rv = ValidateListSimpletype(aNodeValue, aSchemaSimpleType, nsnull,
&isValid);
break;
}
case nsISchemaSimpleType::SIMPLE_TYPE_UNION: {
// handle unions
rv = ValidateUnionSimpletype(aNodeValue, aSchemaSimpleType, &isValid);
break;
}
}
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::ValidateDerivedSimpletype(const nsAString & aNodeValue,
nsSchemaDerivedSimpleType *aDerived,
PRBool *aResult)
{
// This method is called when validating a simpletype that derives from another
// simpletype.
PRBool isValid = PR_FALSE;
PRUint16 simpleTypeValue;
nsresult rv = aDerived->mBaseType->GetSimpleType(&simpleTypeValue);
switch (simpleTypeValue) {
case nsISchemaSimpleType::SIMPLE_TYPE_BUILTIN: {
rv = ValidateDerivedBuiltinType(aNodeValue, aDerived, &isValid);
break;
}
case nsISchemaSimpleType::SIMPLE_TYPE_RESTRICTION: {
// this happens when for example someone derives from a union which then
// derives from another type.
rv = nsSchemaValidatorUtils::GetDerivedSimpleType(aDerived->mBaseType,
aDerived);
ValidateDerivedSimpletype(aNodeValue, aDerived, &isValid);
break;
}
case nsISchemaSimpleType::SIMPLE_TYPE_LIST: {
rv = ValidateListSimpletype(aNodeValue, aDerived->mBaseType, aDerived,
&isValid);
break;
}
case nsISchemaSimpleType::SIMPLE_TYPE_UNION: {
rv = ValidateDerivedUnionSimpletype(aNodeValue, aDerived, &isValid);
break;
}
}
*aResult = isValid;
return rv;
}
/**
* Simpletype restrictions allow restricting a built-in type with several
* facets, such as totalDigits or maxLength.
*/
nsresult
nsSchemaValidator::ValidateRestrictionSimpletype(const nsAString & aNodeValue,
nsISchemaSimpleType *aType,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
nsCOMPtr<nsISchemaRestrictionType> restrictionType = do_QueryInterface(aType);
NS_ENSURE_STATE(restrictionType);
LOG((" The simpletype definition contains restrictions."));
nsCOMPtr<nsISchemaSimpleType> simpleBaseType;
nsresult rv = restrictionType->GetBaseType(getter_AddRefs(simpleBaseType));
NS_ENSURE_SUCCESS(rv, rv);
nsSchemaDerivedSimpleType derivedType;
rv = nsSchemaValidatorUtils::GetDerivedSimpleType(aType, &derivedType);
rv = ValidateDerivedSimpletype(aNodeValue, &derivedType, &isValid);
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::ValidateDerivedBuiltinType(const nsAString & aNodeValue,
nsSchemaDerivedSimpleType *aDerived,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
// now that we have loaded all the restriction facets,
// check the base type and validate
nsCOMPtr<nsISchemaBuiltinType> schemaBuiltinType =
do_QueryInterface(aDerived->mBaseType);
NS_ENSURE_STATE(schemaBuiltinType);
PRUint16 builtinTypeValue;
nsresult rv = schemaBuiltinType->GetBuiltinType(&builtinTypeValue);
#ifdef PR_LOGGING
DumpBaseType(schemaBuiltinType);
#endif
switch (builtinTypeValue) {
case nsISchemaBuiltinType::BUILTIN_TYPE_STRING: {
rv = ValidateBuiltinTypeString(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_NORMALIZED_STRING: {
if (nsSchemaValidatorUtils::IsValidSchemaNormalizedString(aNodeValue)) {
rv = ValidateBuiltinTypeString(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
}
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_TOKEN: {
if (nsSchemaValidatorUtils::IsValidSchemaToken(aNodeValue)) {
rv = ValidateBuiltinTypeString(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
}
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_LANGUAGE: {
if (nsSchemaValidatorUtils::IsValidSchemaLanguage(aNodeValue)) {
rv = ValidateBuiltinTypeString(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
}
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_BOOLEAN: {
rv = ValidateBuiltinTypeBoolean(aNodeValue, &isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_GDAY: {
rv = ValidateBuiltinTypeGDay(aNodeValue,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_GMONTH: {
rv = ValidateBuiltinTypeGMonth(aNodeValue,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_GYEAR: {
rv = ValidateBuiltinTypeGYear(aNodeValue,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_GYEARMONTH: {
rv = ValidateBuiltinTypeGYearMonth(aNodeValue,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_GMONTHDAY: {
rv = ValidateBuiltinTypeGMonthDay(aNodeValue,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_DATE: {
rv = ValidateBuiltinTypeDate(aNodeValue,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_TIME: {
rv = ValidateBuiltinTypeTime(aNodeValue,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_DATETIME: {
rv = ValidateBuiltinTypeDateTime(aNodeValue,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_DURATION: {
rv = ValidateBuiltinTypeDuration(aNodeValue,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_INTEGER: {
rv = ValidateBuiltinTypeInteger(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
/* http://w3.org/TR/xmlschema-2/#nonPositiveInteger */
case nsISchemaBuiltinType::BUILTIN_TYPE_NONPOSITIVEINTEGER: {
if (aDerived->maxExclusive.value.IsEmpty()) {
aDerived->maxExclusive.value.AssignLiteral("1");
} else if (aDerived->maxInclusive.value.IsEmpty()) {
aDerived->maxInclusive.value.AssignLiteral("0");
}
rv = ValidateBuiltinTypeInteger(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
/* http://w3.org/TR/xmlschema-2/#nonNegativeInteger */
case nsISchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER: {
if (aDerived->minExclusive.value.IsEmpty()) {
aDerived->minExclusive.value.AssignLiteral("-1");
} else if (aDerived->minInclusive.value.IsEmpty()) {
aDerived->minInclusive.value.AssignLiteral("0");
}
rv = ValidateBuiltinTypeInteger(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#negativeInteger */
case nsISchemaBuiltinType::BUILTIN_TYPE_NEGATIVEINTEGER: {
if (aDerived->maxExclusive.value.IsEmpty()) {
aDerived->maxExclusive.value.AssignLiteral("0");
} else if (aDerived->maxInclusive.value.IsEmpty()) {
aDerived->maxInclusive.value.AssignLiteral("-1");
}
rv = ValidateBuiltinTypeInteger(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
/* http://w3.org/TR/xmlschema-2/#positiveInteger */
case nsISchemaBuiltinType::BUILTIN_TYPE_POSITIVEINTEGER: {
if (aDerived->minInclusive.value.IsEmpty()) {
aDerived->minInclusive.value.AssignLiteral("1");
} else if (aDerived->minExclusive.value.IsEmpty()) {
aDerived->minExclusive.value.AssignLiteral("0");
}
rv = ValidateBuiltinTypeInteger(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#long */
case nsISchemaBuiltinType::BUILTIN_TYPE_LONG: {
if (aDerived->maxInclusive.value.IsEmpty()) {
aDerived->maxInclusive.value.AssignLiteral("9223372036854775807");
}
if (aDerived->minInclusive.value.IsEmpty()) {
aDerived->minInclusive.value.AssignLiteral("-9223372036854775808");
}
rv = ValidateBuiltinTypeInteger(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#int */
case nsISchemaBuiltinType::BUILTIN_TYPE_INT: {
if (aDerived->maxInclusive.value.IsEmpty()) {
aDerived->maxInclusive.value.AssignLiteral("2147483647");
}
if (aDerived->minInclusive.value.IsEmpty()) {
aDerived->minInclusive.value.AssignLiteral("-2147483648");
}
rv = ValidateBuiltinTypeInteger(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#short */
case nsISchemaBuiltinType::BUILTIN_TYPE_SHORT: {
if (aDerived->maxInclusive.value.IsEmpty()) {
aDerived->maxInclusive.value.AssignLiteral("32767");
}
if (aDerived->minInclusive.value.IsEmpty()) {
aDerived->minInclusive.value.AssignLiteral("-32768");
}
rv = ValidateBuiltinTypeInteger(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#unsignedLong */
case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDLONG: {
if (aDerived->maxInclusive.value.IsEmpty()) {
aDerived->maxInclusive.value.AssignLiteral("18446744073709551615");
}
if (aDerived->minInclusive.value.IsEmpty()) {
aDerived->minInclusive.value.AssignLiteral("0");
}
rv = ValidateBuiltinTypeInteger(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#unsignedInt */
case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDINT: {
if (aDerived->maxInclusive.value.IsEmpty()) {
aDerived->maxInclusive.value.AssignLiteral("4294967295");
}
if (aDerived->minInclusive.value.IsEmpty()) {
aDerived->minInclusive.value.AssignLiteral("0");
}
rv = ValidateBuiltinTypeInteger(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#unsignedShort */
case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDSHORT: {
if (aDerived->maxInclusive.value.IsEmpty()) {
aDerived->maxInclusive.value.AssignLiteral("65535");
}
if (aDerived->minInclusive.value.IsEmpty()) {
aDerived->minInclusive.value.AssignLiteral("0");
}
rv = ValidateBuiltinTypeInteger(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#unsignedByte */
case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDBYTE: {
if (aDerived->maxInclusive.value.IsEmpty()) {
aDerived->maxInclusive.value.AssignLiteral("255");
}
if (aDerived->minInclusive.value.IsEmpty()) {
aDerived->minInclusive.value.AssignLiteral("0");
}
rv = ValidateBuiltinTypeInteger(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_BYTE: {
rv = ValidateBuiltinTypeByte(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_FLOAT: {
rv = ValidateBuiltinTypeFloat(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_DOUBLE: {
rv = ValidateBuiltinTypeDouble(aNodeValue,
aDerived->totalDigits.value,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_DECIMAL: {
rv = ValidateBuiltinTypeDecimal(aNodeValue,
aDerived->totalDigits.value,
aDerived->fractionDigits.value,
aDerived->fractionDigits.isDefined,
aDerived->maxExclusive.value,
aDerived->minExclusive.value,
aDerived->maxInclusive.value,
aDerived->minInclusive.value,
&aDerived->enumerationList,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_ANYURI: {
rv = ValidateBuiltinTypeAnyURI(aNodeValue,
aDerived->length.value,
aDerived->minLength.value,
aDerived->maxLength.value,
&aDerived->enumerationList,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_BASE64BINARY: {
rv = ValidateBuiltinTypeBase64Binary(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_HEXBINARY: {
rv = ValidateBuiltinTypeHexBinary(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_QNAME: {
rv = ValidateBuiltinTypeQName(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#name */
case nsISchemaBuiltinType::BUILTIN_TYPE_NAME: {
if (nsSchemaValidatorUtils::IsValidSchemaName(aNodeValue)) {
rv = ValidateBuiltinTypeString(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
}
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_NCNAME: {
if (nsSchemaValidatorUtils::IsValidSchemaNCName(aNodeValue)) {
rv = ValidateBuiltinTypeString(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
}
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_ID: {
if (nsSchemaValidatorUtils::IsValidSchemaID(aNodeValue)) {
rv = ValidateBuiltinTypeString(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
}
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_IDREF: {
if (nsSchemaValidatorUtils::IsValidSchemaIDRef(aNodeValue)) {
rv = ValidateBuiltinTypeString(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
}
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_IDREFS: {
if (nsSchemaValidatorUtils::IsValidSchemaIDRefs(aNodeValue)) {
rv = ValidateBuiltinTypeString(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
}
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_NMTOKEN: {
if (nsSchemaValidatorUtils::IsValidSchemaNMToken(aNodeValue)) {
rv = ValidateBuiltinTypeString(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
}
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_NMTOKENS: {
if (nsSchemaValidatorUtils::IsValidSchemaNMTokens(aNodeValue)) {
rv = ValidateBuiltinTypeString(aNodeValue,
aDerived->length.value,
aDerived->length.isDefined,
aDerived->minLength.value,
aDerived->minLength.isDefined,
aDerived->maxLength.value,
aDerived->maxLength.isDefined,
&aDerived->enumerationList,
&isValid);
}
break;
}
default:
rv = NS_ERROR_SCHEMAVALIDATOR_TYPE_NOT_FOUND;
break;
}
// finally check if a pattern is defined, as all types can be constrained by
// regexp patterns.
if (isValid && aDerived->pattern.isDefined) {
// check if the pattern matches
nsCOMPtr<nsISchemaValidatorRegexp> regexp = do_GetService(kREGEXP_CID);
rv = regexp->RunRegexp(aNodeValue, aDerived->pattern.value, "g", &isValid);
NS_ENSURE_SUCCESS(rv, rv);
#ifdef PR_LOGGING
LOG((" Checking Regular Expression"));
if (isValid) {
LOG((" -- pattern validates!"));
} else {
LOG((" -- pattern does not validate!"));
}
#endif
}
*aResult = isValid;
return rv;
}
/*
Handles validation of built-in schema types.
*/
nsresult
nsSchemaValidator::ValidateBuiltinType(const nsAString & aNodeValue,
nsISchemaSimpleType *aSchemaSimpleType,
PRBool *aResult)
{
nsCOMPtr<nsISchemaBuiltinType> builtinType =
do_QueryInterface(aSchemaSimpleType);
NS_ENSURE_STATE(builtinType);
PRUint16 builtinTypeValue;
nsresult rv = builtinType->GetBuiltinType(&builtinTypeValue);
NS_ENSURE_SUCCESS(rv, rv);
#ifdef PR_LOGGING
DumpBaseType(builtinType);
#endif
PRBool isValid = PR_FALSE;
switch(builtinTypeValue) {
case nsISchemaBuiltinType::BUILTIN_TYPE_STRING: {
isValid = PR_TRUE;
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_NORMALIZED_STRING: {
if (nsSchemaValidatorUtils::IsValidSchemaNormalizedString(aNodeValue)) {
rv = ValidateBuiltinTypeString(aNodeValue, 0, PR_FALSE, 0, PR_FALSE, 0,
PR_FALSE, nsnull, &isValid);
}
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_TOKEN: {
if (nsSchemaValidatorUtils::IsValidSchemaToken(aNodeValue)) {
rv = ValidateBuiltinTypeString(aNodeValue, 0, PR_FALSE, 0, PR_FALSE, 0,
PR_FALSE, nsnull, &isValid);
}
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_LANGUAGE: {
if (nsSchemaValidatorUtils::IsValidSchemaLanguage(aNodeValue)) {
rv = ValidateBuiltinTypeString(aNodeValue, 0, PR_FALSE, 0, PR_FALSE, 0,
PR_FALSE, nsnull, &isValid);
}
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_BOOLEAN: {
rv = ValidateBuiltinTypeBoolean(aNodeValue, &isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_GDAY: {
isValid = IsValidSchemaGDay(aNodeValue, nsnull);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_GMONTH: {
isValid = IsValidSchemaGMonth(aNodeValue, nsnull);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_GYEAR: {
isValid = IsValidSchemaGYear(aNodeValue, nsnull);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_GYEARMONTH: {
isValid = IsValidSchemaGYearMonth(aNodeValue, nsnull);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_GMONTHDAY: {
isValid = IsValidSchemaGMonthDay(aNodeValue, nsnull);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_DATE: {
nsSchemaDate tmp;
isValid = IsValidSchemaDate(aNodeValue, &tmp);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_TIME: {
nsSchemaTime tmp;
isValid = IsValidSchemaTime(aNodeValue, &tmp);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_DATETIME: {
nsSchemaDateTime tmp;
isValid = IsValidSchemaDateTime(aNodeValue, &tmp);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_DURATION: {
nsCOMPtr<nsISchemaDuration> temp;
isValid = IsValidSchemaDuration(aNodeValue, getter_AddRefs(temp));
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_INTEGER: {
isValid = nsSchemaValidatorUtils::IsValidSchemaInteger(aNodeValue, nsnull);
break;
}
/* http://w3.org/TR/xmlschema-2/#nonPositiveInteger */
case nsISchemaBuiltinType::BUILTIN_TYPE_NONPOSITIVEINTEGER: {
// nonPositiveInteger inherits from integer, with maxInclusive
// being 0
ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(),
EmptyString(), NS_LITERAL_STRING("0"),
EmptyString(), nsnull, &isValid);
break;
}
/* http://w3.org/TR/xmlschema-2/#nonNegativeInteger */
case nsISchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER: {
// nonNegativeInteger inherits from integer, with minInclusive
// being 0 (only positive integers)
ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(),
EmptyString(), EmptyString(),
NS_LITERAL_STRING("0"), nsnull, &isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#negativeInteger */
case nsISchemaBuiltinType::BUILTIN_TYPE_NEGATIVEINTEGER: {
// negativeInteger inherits from integer, with maxInclusive
// being -1 (only negative integers)
ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(),
EmptyString(), NS_LITERAL_STRING("-1"),
EmptyString(), nsnull, &isValid);
break;
}
/* http://w3.org/TR/xmlschema-2/#positiveInteger */
case nsISchemaBuiltinType::BUILTIN_TYPE_POSITIVEINTEGER: {
// positiveInteger inherits from integer, with minInclusive
// being 1
ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(),
EmptyString(), EmptyString(),
NS_LITERAL_STRING("1"), nsnull, &isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#long */
case nsISchemaBuiltinType::BUILTIN_TYPE_LONG: {
// maxInclusive is 9223372036854775807 and minInclusive is
// -9223372036854775808
ValidateBuiltinTypeInteger(aNodeValue, nsnull,
EmptyString(), EmptyString(),
NS_LITERAL_STRING("9223372036854775807"),
NS_LITERAL_STRING("-9223372036854775808"),
nsnull, &isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#int */
case nsISchemaBuiltinType::BUILTIN_TYPE_INT: {
// maxInclusive is 2147483647 and minInclusive is -2147483648
ValidateBuiltinTypeInteger(aNodeValue, nsnull,
EmptyString(), EmptyString(),
NS_LITERAL_STRING("2147483647"),
NS_LITERAL_STRING("-2147483648"),
nsnull, &isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#short */
case nsISchemaBuiltinType::BUILTIN_TYPE_SHORT: {
// maxInclusive is 32767 and minInclusive is -32768
ValidateBuiltinTypeInteger(aNodeValue, nsnull,
EmptyString(), EmptyString(),
NS_LITERAL_STRING("32767"),
NS_LITERAL_STRING("-32768"),
nsnull, &isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#unsignedLong */
case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDLONG: {
// maxInclusive is 18446744073709551615. and minInclusive is 0
ValidateBuiltinTypeInteger(aNodeValue, nsnull,
EmptyString(), EmptyString(),
NS_LITERAL_STRING("18446744073709551615"),
NS_LITERAL_STRING("0"),
nsnull, &isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#unsignedInt */
case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDINT: {
// maxInclusive is 4294967295. and minInclusive is 0
ValidateBuiltinTypeInteger(aNodeValue, nsnull,
EmptyString(), EmptyString(),
NS_LITERAL_STRING("4294967295"),
NS_LITERAL_STRING("0"),
nsnull, &isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#unsignedShort */
case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDSHORT: {
// maxInclusive is 65535. and minInclusive is 0
ValidateBuiltinTypeInteger(aNodeValue, nsnull,
EmptyString(), EmptyString(),
NS_LITERAL_STRING("65535"),
NS_LITERAL_STRING("0"),
nsnull, &isValid);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#unsignedByte */
case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDBYTE: {
// maxInclusive is 255. and minInclusive is 0
ValidateBuiltinTypeInteger(aNodeValue, nsnull,
EmptyString(), EmptyString(),
NS_LITERAL_STRING("255"),
NS_LITERAL_STRING("0"),
nsnull, &isValid);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_BYTE: {
isValid = IsValidSchemaByte(aNodeValue, nsnull);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_FLOAT: {
isValid = IsValidSchemaFloat(aNodeValue, nsnull);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_DOUBLE: {
isValid = IsValidSchemaDouble(aNodeValue, nsnull);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_DECIMAL: {
nsAutoString wholePart, fractionPart;
isValid = IsValidSchemaDecimal(aNodeValue, wholePart, fractionPart);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_ANYURI: {
isValid = IsValidSchemaAnyURI(aNodeValue);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_BASE64BINARY: {
char* decodedString;
isValid = IsValidSchemaBase64Binary(aNodeValue, &decodedString);
nsMemory::Free(decodedString);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_HEXBINARY: {
isValid = IsValidSchemaHexBinary(aNodeValue);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_QNAME: {
isValid = IsValidSchemaQName(aNodeValue);
break;
}
/* http://www.w3.org/TR/xmlschema-2/#name */
case nsISchemaBuiltinType::BUILTIN_TYPE_NAME: {
isValid = nsSchemaValidatorUtils::IsValidSchemaName(aNodeValue);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_NCNAME: {
isValid = nsSchemaValidatorUtils::IsValidSchemaNCName(aNodeValue);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_ID: {
isValid = nsSchemaValidatorUtils::IsValidSchemaID(aNodeValue);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_IDREF: {
isValid = nsSchemaValidatorUtils::IsValidSchemaIDRef(aNodeValue);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_IDREFS: {
isValid = nsSchemaValidatorUtils::IsValidSchemaIDRefs(aNodeValue);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_NMTOKEN: {
isValid = nsSchemaValidatorUtils::IsValidSchemaNMToken(aNodeValue);
break;
}
case nsISchemaBuiltinType::BUILTIN_TYPE_NMTOKENS: {
isValid = nsSchemaValidatorUtils::IsValidSchemaNMTokens(aNodeValue);
break;
}
default:
rv = NS_ERROR_SCHEMAVALIDATOR_TYPE_NOT_FOUND;
break;
}
*aResult = isValid;
return rv;
}
/* http://www.w3.org/TR/xmlschema-2/#dt-list */
nsresult
nsSchemaValidator::ValidateListSimpletype(const nsAString & aNodeValue,
nsISchemaSimpleType *aSchemaSimpleType,
nsSchemaDerivedSimpleType *aFacets,
PRBool *aResult)
{
/* When a datatype is derived from an list datatype, the following facets apply:
* length, maxLength, minLength, enumeration, pattern, whitespace
*
* The length facets apply to the whole list, ie the number of items in the list.
* Patterns are applied to the whole list as well.
*/
PRBool isValid = PR_FALSE;
nsresult rv;
nsCOMPtr<nsISchemaListType> listType = do_QueryInterface(aSchemaSimpleType, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISchemaSimpleType> listSimpleType;
rv = listType->GetListType(getter_AddRefs(listSimpleType));
NS_ENSURE_SUCCESS(rv, rv);
if (listSimpleType) {
nsCStringArray stringArray;
stringArray.ParseString(NS_ConvertUTF16toUTF8(aNodeValue).get(), " \t\r\n");
PRUint32 count = stringArray.Count();
// if facets have been provided, check them first
PRBool facetsValid = PR_TRUE;
if (aFacets) {
if (aFacets->length.isDefined) {
if (aFacets->length.value != count) {
facetsValid = PR_FALSE;
LOG((" Not valid: List is not the right length (%d)",
aFacets->length.value));
}
}
if (facetsValid && aFacets->maxLength.isDefined) {
if (aFacets->maxLength.value < count) {
facetsValid = PR_FALSE;
LOG((" Not valid: Length (%d) of the list is too large",
aFacets->maxLength.value));
}
}
if (facetsValid && aFacets->minLength.isDefined) {
if (aFacets->minLength.value > count) {
facetsValid = PR_FALSE;
LOG((" Not valid: Length (%d) of the list is too small",
aFacets->minLength.value));
}
}
if (facetsValid && aFacets->pattern.isDefined) {
// check if the pattern matches
nsCOMPtr<nsISchemaValidatorRegexp> regexp = do_GetService(kREGEXP_CID);
rv = regexp->RunRegexp(aNodeValue, aFacets->pattern.value, "g",
&facetsValid);
#ifdef PR_LOGGING
LOG((" Checking Regular Expression"));
if (facetsValid) {
LOG((" -- pattern validates!"));
} else {
LOG((" -- pattern does not validate!"));
}
#endif
}
if (facetsValid && aFacets->enumerationList.Count() > 0) {
facetsValid =
nsSchemaValidatorUtils::HandleEnumeration(aNodeValue,
aFacets->enumerationList);
}
}
// either no facets passed in or facets validated fine
if (facetsValid) {
nsAutoString tmp;
for (PRUint32 i=0; i < count; ++i) {
CopyUTF8toUTF16(*stringArray[i], tmp);
LOG((" Validating List Item (%d): %s", i, NS_ConvertUTF16toUTF8(tmp).get()));
rv = ValidateSimpletype(tmp, listSimpleType, &isValid);
if (!isValid)
break;
}
}
}
*aResult = isValid;
return rv;
}
/* http://www.w3.org/TR/xmlschema-2/#dt-union */
nsresult
nsSchemaValidator::ValidateUnionSimpletype(const nsAString & aNodeValue,
nsISchemaSimpleType *aSchemaSimpleType,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
nsresult rv;
nsCOMPtr<nsISchemaUnionType> unionType = do_QueryInterface(aSchemaSimpleType,
&rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISchemaSimpleType> unionSimpleType;
PRUint32 count;
unionType->GetUnionTypeCount(&count);
// compare against the union simpletypes in order until a match is found
for (PRUint32 i=0; i < count; ++i) {
rv = unionType->GetUnionType(i, getter_AddRefs(unionSimpleType));
NS_ENSURE_SUCCESS(rv, rv);
LOG((" Validating Untion Type #%d", i));
rv = ValidateSimpletype(aNodeValue, unionSimpleType, &isValid);
if (isValid)
break;
}
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::ValidateDerivedUnionSimpletype(const nsAString & aNodeValue,
nsSchemaDerivedSimpleType *aDerived,
PRBool *aResult)
{
// This method is called when a simple type is derived from a union type
// via restrictions. So we walk all the possible types, and pass in the
// loaded restriction facets so that they will override the ones defined
// my the union type. We actually have to create a custom
// nsSchemaDerivedSimpleType for each type, since we need to change the basetype
// and to avoid any new restrictions being added to aDerived.
PRBool isValid = PR_FALSE;
nsresult rv;
nsCOMPtr<nsISchemaUnionType> unionType = do_QueryInterface(aDerived->mBaseType,
&rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISchemaSimpleType> unionSimpleType;
PRUint32 count;
unionType->GetUnionTypeCount(&count);
// compare against the union simpletypes in order until a match is found
for (PRUint32 i=0; i < count; ++i) {
rv = unionType->GetUnionType(i, getter_AddRefs(unionSimpleType));
NS_ENSURE_SUCCESS(rv, rv);
nsSchemaDerivedSimpleType derivedType;
nsSchemaValidatorUtils::CopyDerivedSimpleType(&derivedType, aDerived);
derivedType.mBaseType = unionSimpleType;
LOG((" Validating Union Type #%d", i));
rv = ValidateDerivedSimpletype(aNodeValue, &derivedType, &isValid);
if (isValid)
break;
}
*aResult = isValid;
return rv;
}
/* http://www.w3.org/TR/xmlschema-2/#string */
nsresult
nsSchemaValidator::ValidateBuiltinTypeString(const nsAString & aNodeValue,
PRUint32 aLength,
PRBool aLengthDefined,
PRUint32 aMinLength,
PRBool aMinLengthDefined,
PRUint32 aMaxLength,
PRBool aMaxLengthDefined,
nsStringArray *aEnumerationList,
PRBool *aResult)
{
// XXX needs to check if all chars are legal per spec
// IsUTF8(NS_ConvertUTF16toUTF8(theString).get())
PRBool isValid = PR_TRUE;
PRUint32 length = aNodeValue.Length();
if (aLengthDefined && (length != aLength)) {
isValid = PR_FALSE;
LOG((" Not valid: Not the right length (%d)", length));
}
if (isValid && aMinLengthDefined && (length < aMinLength)) {
isValid = PR_FALSE;
LOG((" Not valid: Length (%d) is too small", length));
}
if (isValid && aMaxLengthDefined && (length > aMaxLength)) {
isValid = PR_FALSE;
LOG((" Not valid: Length (%d) is too large", length));
}
if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) {
isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue,
*aEnumerationList);
}
#ifdef PR_LOGGING
if (isValid)
LOG((" Value is valid!"));
#endif
*aResult = isValid;
return NS_OK;
}
/* http://www.w3.org/TR/xmlschema-2/#boolean */
nsresult
nsSchemaValidator::ValidateBuiltinTypeBoolean(const nsAString & aNodeValue,
PRBool *aResult)
{
// possible values are 0, 1, true, false. Spec makes no mention if TRUE is
// valid, so performing a case insensitive comparision to be safe
*aResult = aNodeValue.EqualsLiteral("false") ||
aNodeValue.EqualsLiteral("true") ||
aNodeValue.EqualsLiteral("1") ||
aNodeValue.EqualsLiteral("0");
#ifdef PR_LOGGING
if (*aResult) {
LOG((" Value is valid."));
} else {
LOG((" Not valid: Value (%s) is not in {1,0,true,false}.",
NS_ConvertUTF16toUTF8(aNodeValue).get()));
}
#endif
return NS_OK;
}
/* http://www.w3.org/TR/xmlschema-2/#gDay */
nsresult
nsSchemaValidator::ValidateBuiltinTypeGDay(const nsAString & aNodeValue,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
PRBool *aResult)
{
PRBool isValidGDay;
nsSchemaGDay gday;
PRBool isValid = IsValidSchemaGDay(aNodeValue, &gday);
if (isValid && !aMaxExclusive.IsEmpty()) {
nsSchemaGDay maxExclusive;
isValidGDay = IsValidSchemaGDay(aMaxExclusive, &maxExclusive);
if (isValidGDay) {
if (gday.day >= maxExclusive.day) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%d) is too large", gday.day));
}
}
}
if (isValid && !aMinExclusive.IsEmpty()) {
nsSchemaGDay minExclusive;
isValidGDay = IsValidSchemaGDay(aMinExclusive, &minExclusive);
if (isValidGDay) {
if (gday.day <= minExclusive.day) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%d) is too small", gday.day));
}
}
}
if (isValid && !aMaxInclusive.IsEmpty()) {
nsSchemaGDay maxInclusive;
isValidGDay = IsValidSchemaGDay(aMaxInclusive, &maxInclusive);
if (isValidGDay) {
if (gday.day > maxInclusive.day) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%d) is too large", gday.day));
}
}
}
if (isValid && !aMinInclusive.IsEmpty()) {
nsSchemaGDay minInclusive;
isValidGDay = IsValidSchemaGDay(aMinInclusive, &minInclusive);
if (isValidGDay) {
if (gday.day < minInclusive.day) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%d) is too small", gday.day));
}
}
}
#ifdef PR_LOGGING
LOG((isValid ? (" Value is valid!") : (" Value is not valid!")));
#endif
*aResult = isValid;
return NS_OK;
}
PRBool
nsSchemaValidator::IsValidSchemaGDay(const nsAString & aNodeValue,
nsSchemaGDay *aResult)
{
// GDay looks like this: ---DD(Z|(+|-)hh:mm)
PRUint32 strLength = aNodeValue.Length();
// ---DD ---DDZ ---DD(+|-)hh:mm
if (strLength != 5 && strLength != 6 && strLength != 11)
return PR_FALSE;
char timezoneHour[3] = "";
char timezoneMinute[3] = "";
PRUnichar tzSign = PRUnichar(' ');
int dayInt;
// iterate over the string and parse/validate
const PRUnichar *start, *end;
aNodeValue.BeginReading(&start, &end);
nsAutoString nodeValue(aNodeValue);
// validate the ---DD part
PRBool isValid = Substring(start, start + 3).EqualsLiteral("---") &&
IsValidSchemaGType(Substring(start + 3, start + 5),
1, 31, &dayInt);
if (isValid) {
tzSign = nodeValue.CharAt(5);
if (strLength == 6) {
isValid &= (tzSign == 'Z');
} else if (strLength == 11) {
const nsAString &tz = Substring(start + 6, end);
isValid &= ((tzSign == '+' || tzSign == '-') &&
nsSchemaValidatorUtils::ParseSchemaTimeZone(tz, timezoneHour,
timezoneMinute));
}
}
if (isValid && aResult) {
char * pEnd;
aResult->day = dayInt;
aResult->tz_negative = (tzSign == '-') ? PR_TRUE : PR_FALSE;
aResult->tz_hour = (timezoneHour[0] == '\0') ? nsnull : strtol(timezoneHour, &pEnd, 10);
aResult->tz_minute = (timezoneMinute[0] == '\0') ? nsnull : strtol(timezoneMinute, &pEnd, 10);
}
return isValid;
}
// Helper function used to validate gregorian date types
PRBool
nsSchemaValidator::IsValidSchemaGType(const nsAString & aNodeValue,
long aMinValue, long aMaxValue,
int *aResult)
{
long intValue;
PRBool isValid =
nsSchemaValidatorUtils::IsValidSchemaInteger(aNodeValue, &intValue, PR_TRUE);
*aResult = intValue;
return isValid && (intValue >= aMinValue) && (intValue <= aMaxValue);
}
/* http://www.w3.org/TR/xmlschema-2/#gMonth */
nsresult
nsSchemaValidator::ValidateBuiltinTypeGMonth(const nsAString & aNodeValue,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
PRBool *aResult)
{
nsSchemaGMonth gmonth;
PRBool isValid = IsValidSchemaGMonth(aNodeValue, &gmonth);
if (isValid && !aMaxExclusive.IsEmpty()) {
nsSchemaGMonth maxExclusive;
if (IsValidSchemaGMonth(aMaxExclusive, &maxExclusive) &&
gmonth.month >= maxExclusive.month) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%d) is too large", gmonth.month));
}
}
if (isValid && !aMinExclusive.IsEmpty()) {
nsSchemaGMonth minExclusive;
if (IsValidSchemaGMonth(aMinExclusive, &minExclusive) &&
gmonth.month <= minExclusive.month) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%d) is too small", gmonth.month));
}
}
if (isValid && !aMaxInclusive.IsEmpty()) {
nsSchemaGMonth maxInclusive;
if (IsValidSchemaGMonth(aMaxInclusive, &maxInclusive) &&
gmonth.month > maxInclusive.month) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%d) is too large", gmonth.month));
}
}
if (isValid && !aMinInclusive.IsEmpty()) {
nsSchemaGMonth minInclusive;
if (IsValidSchemaGMonth(aMinInclusive, &minInclusive) &&
gmonth.month < minInclusive.month) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%d) is too small", gmonth.month));
}
}
#ifdef PR_LOGGING
LOG((isValid ? (" Value is valid!") : (" Value is not valid!")));
#endif
*aResult = isValid;
return NS_OK;
}
PRBool
nsSchemaValidator::IsValidSchemaGMonth(const nsAString & aNodeValue,
nsSchemaGMonth *aResult)
{
// GMonth looks like this: --MM(Z|(+|-)hh:mm)
PRUint32 strLength = aNodeValue.Length();
// --MM --MMZ --MM(+|-)hh:mm
if ((strLength != 4) && (strLength != 5) && (strLength != 10))
return PR_FALSE;
char timezoneHour[3] = "";
char timezoneMinute[3] = "";
PRUnichar tzSign = 0;
int monthInt;
const PRUnichar *start, *end;
aNodeValue.BeginReading(&start, &end);
nsAutoString nodeValue(aNodeValue);
// validate the --MM part
PRBool isValid = Substring(start, start + 2).EqualsLiteral("--") &&
IsValidSchemaGType(Substring(start + 2, start + 4),
1, 12, &monthInt);
if (isValid) {
tzSign = nodeValue.CharAt(4);
if (strLength == 5) {
isValid &= (tzSign == 'Z');
} else if (strLength == 10) {
const nsAString &tz = Substring(start + 5, end);
isValid &= ((tzSign == '+' || tzSign == '-') &&
nsSchemaValidatorUtils::ParseSchemaTimeZone(tz, timezoneHour,
timezoneMinute));
}
}
if (isValid && aResult) {
char * pEnd;
aResult->month = monthInt;
aResult->tz_negative = (tzSign == '-') ? PR_TRUE : PR_FALSE;
aResult->tz_hour = (timezoneHour[0] == '\0') ? nsnull : strtol(timezoneHour, &pEnd, 10);
aResult->tz_minute = (timezoneMinute[0] == '\0') ? nsnull : strtol(timezoneMinute, &pEnd, 10);
}
return isValid;
}
/* http://www.w3.org/TR/xmlschema-2/#gYear */
nsresult
nsSchemaValidator::ValidateBuiltinTypeGYear(const nsAString & aNodeValue,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
PRBool *aResult)
{
nsSchemaGYear gyear;
PRBool isValid = IsValidSchemaGYear(aNodeValue, &gyear);
if (isValid && !aMaxExclusive.IsEmpty()) {
nsSchemaGYear maxExclusive;
if (IsValidSchemaGYear(aMaxExclusive, &maxExclusive) &&
gyear.year >= maxExclusive.year) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%ld) is too large", gyear.year));
}
}
if (isValid && !aMinExclusive.IsEmpty()) {
nsSchemaGYear minExclusive;
if (IsValidSchemaGYear(aMinExclusive, &minExclusive) &&
gyear.year <= minExclusive.year) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%ld) is too small", gyear.year));
}
}
if (isValid && !aMaxInclusive.IsEmpty()) {
nsSchemaGYear maxInclusive;
if (IsValidSchemaGYear(aMaxInclusive, &maxInclusive) &&
gyear.year > maxInclusive.year) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%ld) is too large", gyear.year));
}
}
if (isValid && !aMinInclusive.IsEmpty()) {
nsSchemaGYear minInclusive;
if (IsValidSchemaGYear(aMinInclusive, &minInclusive) &&
gyear.year < minInclusive.year) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%ld) is too small", gyear.year));
}
}
#ifdef PR_LOGGING
LOG((isValid ? (" Value is valid!") : (" Value is not valid!")));
#endif
*aResult = isValid;
return NS_OK;
}
PRBool
nsSchemaValidator::IsValidSchemaGYear(const nsAString & aNodeValue,
nsSchemaGYear *aResult)
{
// GYear looks like this: (-)CCYY(Z|(+|-)hh:mm)
PRBool isValid = PR_FALSE;
PRUint32 strLength = aNodeValue.Length();
char timezoneHour[3] = "";
char timezoneMinute[3] = "";
PRUnichar tzSign = 0;
const PRUnichar *start, *end, *buffStart;
aNodeValue.BeginReading(&start, &end);
aNodeValue.BeginReading(&buffStart);
PRUint32 state = 0;
PRUint32 buffLength = 0, yearLength = 0;
PRBool done = PR_FALSE;
long yearNum;
if (aNodeValue.First() == '-') {
// jump the negative sign
*start++;
buffLength++;
}
while ((start != end) && !done) {
// 0 - year
// 1 - timezone
if (state == 0) {
// year
PRUnichar temp = *start++;
// walk till the first non-numerical char
while (((temp >= '0') && (temp <= '9')) && (buffLength < strLength)) {
buffLength++;
temp = *start++;
yearLength++;
}
// if we are not at the end of the string, back up one, as we hit the
// timezone separator.
if (buffLength < strLength)
*start--;
// need a minimum of 4 digits for year
if (yearLength >= 4) {
isValid = nsSchemaValidatorUtils::IsValidSchemaInteger(
Substring(buffStart, start), &yearNum, PR_TRUE);
}
// 0 is an invalid year per the spec
if (isValid && (yearNum == 0))
isValid = PR_FALSE;
if (isValid && (strLength > buffLength)) {
state = 1;
} else {
done = PR_TRUE;
}
} else if (state == 1) {
// timezone
int lengthDiff = (strLength - buffLength);
if (lengthDiff == 0){
// timezone is optional
isValid = PR_TRUE;
} else if (lengthDiff == 1) {
// timezone is one character, so has to be 'Z'
isValid = (*start == 'Z');
} else if (lengthDiff == 6){
// timezone is (+|-)hh:mm
tzSign = *start++;
if ((tzSign == '+') || (tzSign == '-')) {
isValid = nsSchemaValidatorUtils::ParseSchemaTimeZone(
Substring(start, end), timezoneHour, timezoneMinute);
}
} else {
// invalid length for timezone
isValid = PR_FALSE;
}
done = PR_TRUE;
}
}
if (isValid && aResult) {
char * pEnd;
aResult->year = yearNum;
aResult->tz_negative = (tzSign == '-') ? PR_TRUE : PR_FALSE;
aResult->tz_hour = (timezoneHour[0] == '\0') ? nsnull : strtol(timezoneHour, &pEnd, 10);
aResult->tz_minute = (timezoneMinute[0] == '\0') ? nsnull : strtol(timezoneMinute, &pEnd, 10);
}
return isValid;
}
/* http://www.w3.org/TR/xmlschema-2/#gYearMonth */
nsresult
nsSchemaValidator::ValidateBuiltinTypeGYearMonth(const nsAString & aNodeValue,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
PRBool *aResult)
{
nsSchemaGYearMonth gyearmonth;
PRBool isValid = IsValidSchemaGYearMonth(aNodeValue, &gyearmonth);
if (isValid && !aMaxExclusive.IsEmpty()) {
nsSchemaGYearMonth maxExclusive;
if (IsValidSchemaGYearMonth(aMaxExclusive, &maxExclusive) &&
(nsSchemaValidatorUtils::CompareGYearMonth(gyearmonth, maxExclusive) > -1)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%ld-%d) is too large", gyearmonth.gYear.year,
gyearmonth.gMonth.month));
}
}
if (isValid && !aMinExclusive.IsEmpty()) {
nsSchemaGYearMonth minExclusive;
if (IsValidSchemaGYearMonth(aMinExclusive, &minExclusive) &&
(nsSchemaValidatorUtils::CompareGYearMonth(gyearmonth, minExclusive) < 1)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%ld-%d) is too small", gyearmonth.gYear.year,
gyearmonth.gMonth.month));
}
}
if (isValid && !aMaxInclusive.IsEmpty()) {
nsSchemaGYearMonth maxInclusive;
if (IsValidSchemaGYearMonth(aMaxInclusive, &maxInclusive) &&
(nsSchemaValidatorUtils::CompareGYearMonth(gyearmonth, maxInclusive) > 0)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%ld-%d) is too large", gyearmonth.gYear.year,
gyearmonth.gMonth.month));
}
}
if (isValid && !aMinInclusive.IsEmpty()) {
nsSchemaGYearMonth minInclusive;
if (IsValidSchemaGYearMonth(aMinInclusive, &minInclusive) &&
(nsSchemaValidatorUtils::CompareGYearMonth(gyearmonth, minInclusive) < 0)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%ld-%d) is too small", gyearmonth.gYear.year,
gyearmonth.gMonth.month));
}
}
#ifdef PR_LOGGING
LOG((isValid ? (" Value is valid!") : (" Value is not valid!")));
#endif
*aResult = isValid;
return NS_OK;
}
PRBool
nsSchemaValidator::IsValidSchemaGYearMonth(const nsAString & aNodeValue,
nsSchemaGYearMonth *aYearMonth)
{
// GYearMonth looks like this: (-)CCYY-MM(Z|(+|-)hh:mm)
PRBool isValid = PR_FALSE;
const PRUnichar *start, *end, *buffStart;
aNodeValue.BeginReading(&start, &end);
aNodeValue.BeginReading(&buffStart);
PRUint32 buffLength = 0;
PRBool done = PR_FALSE;
PRUint32 strLength = aNodeValue.Length();
if (aNodeValue.First() == '-') {
// jump the negative sign
*start++;
*buffStart++;
buffLength++;
}
while ((start != end) && !done) {
if (*start++ == '-') {
// separator found
int monthLength = strLength - buffLength;
// -MMZ -MM(+|-)hh:mm -MM
if ((monthLength != 4) && (monthLength != 9) && (monthLength != 3))
return PR_FALSE;
// validate year
nsAutoString year;
// back one up since we have the separator included
year = Substring(buffStart, --start);
isValid = IsValidSchemaGYear(year,
aYearMonth ? &aYearMonth->gYear : nsnull);
if (isValid) {
nsAutoString month;
month.AppendLiteral("--");
start++;
month.Append(Substring(start, end));
isValid = IsValidSchemaGMonth(month,
aYearMonth ? &aYearMonth->gMonth : nsnull);
}
done = PR_TRUE;
} else {
buffLength++;
}
}
return isValid;
}
/* http://www.w3.org/TR/xmlschema-2/#gMonthDay */
nsresult
nsSchemaValidator::ValidateBuiltinTypeGMonthDay(const nsAString & aNodeValue,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
PRBool *aResult)
{
nsSchemaGMonthDay gmonthday;
PRBool isValid = IsValidSchemaGMonthDay(aNodeValue, &gmonthday);
if (isValid && !aMaxExclusive.IsEmpty()) {
nsSchemaGMonthDay maxExclusive;
if (IsValidSchemaGMonthDay(aMaxExclusive, &maxExclusive) &&
(nsSchemaValidatorUtils::CompareGMonthDay(gmonthday, maxExclusive) > -1)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%d-%d) is too large", gmonthday.gMonth.month,
gmonthday.gDay.day));
}
}
if (isValid && !aMinExclusive.IsEmpty()) {
nsSchemaGMonthDay minExclusive;
if (IsValidSchemaGMonthDay(aMinExclusive, &minExclusive) &&
(nsSchemaValidatorUtils::CompareGMonthDay(gmonthday, minExclusive) < 1)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%d-%d) is too small", gmonthday.gMonth.month,
gmonthday.gDay.day));
}
}
if (isValid && !aMaxInclusive.IsEmpty()) {
nsSchemaGMonthDay maxInclusive;
if (IsValidSchemaGMonthDay(aMaxInclusive, &maxInclusive) &&
(nsSchemaValidatorUtils::CompareGMonthDay(gmonthday, maxInclusive) > 0)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%d-%d) is too large", gmonthday.gMonth.month,
gmonthday.gDay.day));
}
}
if (isValid && !aMinInclusive.IsEmpty()) {
nsSchemaGMonthDay minInclusive;
if (IsValidSchemaGMonthDay(aMinInclusive, &minInclusive) &&
(nsSchemaValidatorUtils::CompareGMonthDay(gmonthday, minInclusive) < 0)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%d-%d) is too small", gmonthday.gMonth.month,
gmonthday.gDay.day));
}
}
#ifdef PR_LOGGING
LOG((isValid ? (" Value is valid!") : (" Value is not valid!")));
#endif
*aResult = isValid;
return NS_OK;
}
PRBool
nsSchemaValidator::IsValidSchemaGMonthDay(const nsAString & aNodeValue,
nsSchemaGMonthDay *aMonthDay)
{
// GMonthDay looks like this: --MM-DD(Z|(+|-)hh:mm)
PRBool isValid = PR_FALSE;
const PRUnichar *start, *end, *buffStart;
aNodeValue.BeginReading(&start, &end);
aNodeValue.BeginReading(&buffStart);
PRUint32 buffLength = 0;
PRUint32 state = 0;
PRBool done = PR_FALSE;
PRUint32 strLength = aNodeValue.Length();
// --MM-DD --MM-DDZ --MM-DD(+|-)hh:mm
if ((strLength != 7) && (strLength != 8) && (strLength != 13))
return PR_FALSE;
while ((start != end) && !done) {
if (state == 0) {
// separator
if (*start++ == '-') {
buffLength++;
if (buffLength == 2) {
state = 1;
buffStart = start;
}
} else {
done = PR_TRUE;
}
} else if (state == 1) {
// month part - construct an --MM--
nsAutoString month;
month.AppendLiteral("--");
start += 2;
month.Append(Substring(buffStart, start));
isValid = IsValidSchemaGMonth(month,
aMonthDay ? &aMonthDay->gMonth : nsnull);
if (isValid) {
buffStart = start;
state = 2;
} else {
done = PR_TRUE;
}
} else if (state == 2) {
// day part - construct ---DD(timezone)
nsAutoString day;
day.AppendLiteral("---");
day.Append(Substring(++buffStart, end));
isValid = IsValidSchemaGDay(day,
aMonthDay ? &aMonthDay->gDay : nsnull);
done = PR_TRUE;
}
}
return isValid;
}
/* http://www.w3.org/TR/xmlschema-2/#time */
nsresult
nsSchemaValidator::ValidateBuiltinTypeTime(const nsAString & aNodeValue,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
PRBool *aResult)
{
nsSchemaTime time;
int timeCompare;
PRBool isValid = IsValidSchemaTime(aNodeValue, &time);
if (isValid && !aMinExclusive.IsEmpty()) {
nsSchemaTime minExclusive;
if (IsValidSchemaTime(aMinExclusive, &minExclusive)) {
timeCompare =
nsSchemaValidatorUtils::CompareTime(time, minExclusive);
if (timeCompare < 1) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too small"));
}
}
}
if (isValid && !aMaxExclusive.IsEmpty()) {
nsSchemaTime maxExclusive;
if (IsValidSchemaTime(aMaxExclusive, &maxExclusive)) {
timeCompare =
nsSchemaValidatorUtils::CompareTime(time, maxExclusive);
if (timeCompare > -1) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too large"));
}
}
}
if (isValid && !aMaxInclusive.IsEmpty()) {
nsSchemaTime maxInclusive;
if (IsValidSchemaTime(aMaxInclusive, &maxInclusive)) {
timeCompare =
nsSchemaValidatorUtils::CompareTime(time, maxInclusive);
if (timeCompare > 0) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too large"));
}
}
}
if (isValid && !aMinInclusive.IsEmpty()) {
nsSchemaTime minInclusive;
if (IsValidSchemaTime(aMinInclusive, &minInclusive)) {
timeCompare =
nsSchemaValidatorUtils::CompareTime(time, minInclusive);
if (timeCompare < 0) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too small"));
}
}
}
*aResult = isValid;
return NS_OK;
}
NS_IMETHODIMP
nsSchemaValidator::ValidateBuiltinTypeTime(const nsAString & aValue,
PRTime *aResult)
{
nsresult rv = NS_OK;
nsSchemaTime time;
PRBool isValid = IsValidSchemaTime(aValue, &time);
if (isValid) {
char fulldate[100] = "";
// 22-AUG-1993 10:59:12.82
sprintf(fulldate, "22-AUG-1993 %d:%d:%d.%u", time.hour, time.minute,
time.second, time.millisecond);
PR_ParseTimeString(fulldate, PR_TRUE, aResult);
} else {
*aResult = nsnull;
rv = NS_ERROR_ILLEGAL_VALUE;
}
return rv;
}
PRBool
nsSchemaValidator::IsValidSchemaTime(const nsAString & aNodeValue,
nsSchemaTime *aResult)
{
PRBool isValid = PR_FALSE;
nsAutoString timeString(aNodeValue);
PRUint32 len = timeString.Length();
PRUnichar end = 0;
if (len > 0) {
// length counts null terminator
end = timeString.CharAt(len - 1);
}
// if no timezone ([+/-]hh:ss) or no timezone Z, add a Z to the end so that
// nsSchemaValidatorUtils::ParseSchemaTime can parse it
if ((timeString.FindChar('-') == kNotFound) &&
(timeString.FindChar('+') == kNotFound) &&
end != 'Z') {
timeString.Append('Z');
}
LOG((" Validating Time: "));
isValid = nsSchemaValidatorUtils::ParseSchemaTime(timeString, aResult);
return isValid;
}
/* http://www.w3.org/TR/xmlschema-2/#date */
nsresult
nsSchemaValidator::ValidateBuiltinTypeDate(const nsAString & aNodeValue,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
nsSchemaDate date;
int dateCompare;
isValid = IsValidSchemaDate(aNodeValue, &date);
if (isValid && !aMaxExclusive.IsEmpty()) {
nsSchemaDate maxExclusive;
if (IsValidSchemaDate(aMaxExclusive, &maxExclusive)) {
dateCompare = nsSchemaValidatorUtils::CompareDate(date, maxExclusive);
if (dateCompare > -1) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too large"));
}
}
}
if (isValid && !aMinExclusive.IsEmpty()) {
nsSchemaDate minExclusive;
if (IsValidSchemaDate(aMinExclusive, &minExclusive)) {
dateCompare = nsSchemaValidatorUtils::CompareDate(date, minExclusive);
if (dateCompare < 1) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too small"));
}
}
}
if (isValid && !aMaxInclusive.IsEmpty()) {
nsSchemaDate maxInclusive;
if (IsValidSchemaDate(aMaxInclusive, &maxInclusive)) {
dateCompare = nsSchemaValidatorUtils::CompareDate(date, maxInclusive);
if (dateCompare > 0) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too large"));
}
}
}
if (isValid && !aMinInclusive.IsEmpty()) {
nsSchemaDate minInclusive;
if (IsValidSchemaDate(aMinInclusive, &minInclusive)) {
dateCompare = nsSchemaValidatorUtils::CompareDate(date, minInclusive);
if (dateCompare < 0) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too small"));
}
}
}
*aResult = isValid;
return NS_OK;
}
NS_IMETHODIMP
nsSchemaValidator::ValidateBuiltinTypeDate(const nsAString & aValue,
PRTime *aResult)
{
nsresult rv = NS_OK;
nsSchemaDate date;
PRBool isValid = IsValidSchemaDate(aValue, &date);
if (isValid) {
char fulldate[100] = "";
nsCAutoString monthShorthand;
nsSchemaValidatorUtils::GetMonthShorthand(date.month, monthShorthand);
// 22-AUG-1993 10:59:12.82
sprintf(fulldate, "%d-%s-%u 00:00:00", date.day,
monthShorthand.get(), date.year);
PR_ParseTimeString(fulldate, PR_TRUE, aResult);
} else {
*aResult = nsnull;
rv = NS_ERROR_ILLEGAL_VALUE;
}
return rv;
}
/* http://www.w3.org/TR/xmlschema-2/#dateTime */
nsresult
nsSchemaValidator::ValidateBuiltinTypeDateTime(const nsAString & aNodeValue,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
PRBool *aResult)
{
nsSchemaDateTime dateTime;
PRBool isValid = IsValidSchemaDateTime(aNodeValue, &dateTime);
if (isValid && !aMaxExclusive.IsEmpty()) {
nsSchemaDateTime maxExclusive;
if (IsValidSchemaDateTime(aMaxExclusive, &maxExclusive) &&
CompareSchemaDateTime(dateTime, maxExclusive) > -1) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too large"));
}
}
if (isValid && !aMinExclusive.IsEmpty()) {
nsSchemaDateTime minExclusive;
if (IsValidSchemaDateTime(aMinExclusive, &minExclusive) &&
CompareSchemaDateTime(dateTime, minExclusive) < 1) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too small"));
}
}
if (isValid && !aMaxInclusive.IsEmpty()) {
nsSchemaDateTime maxInclusive;
if (IsValidSchemaDateTime(aMaxInclusive, &maxInclusive) &&
CompareSchemaDateTime(dateTime, maxInclusive) > 0) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too large"));
}
}
if (isValid && !aMinInclusive.IsEmpty()) {
nsSchemaDateTime minInclusive;
if (IsValidSchemaDateTime(aMinInclusive, &minInclusive) &&
CompareSchemaDateTime(dateTime, minInclusive) < 0) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too small"));
}
}
*aResult = isValid;
return NS_OK;
}
NS_IMETHODIMP
nsSchemaValidator::ValidateBuiltinTypeDateTime(const nsAString & aValue,
PRTime *aResult)
{
nsresult rv = NS_OK;
nsSchemaDateTime dateTime;
PRBool isValid = IsValidSchemaDateTime(aValue, &dateTime);
if (isValid) {
char fulldate[100] = "";
nsCAutoString monthShorthand;
nsSchemaValidatorUtils::GetMonthShorthand(dateTime.date.month, monthShorthand);
// 22-AUG-1993 10:59:12.82
sprintf(fulldate, "%d-%s-%u %d:%d:%d.%u",
dateTime.date.day,
monthShorthand.get(),
dateTime.date.year,
dateTime.time.hour,
dateTime.time.minute,
dateTime.time.second,
dateTime.time.millisecond);
PR_ParseTimeString(fulldate, PR_TRUE, aResult);
} else {
*aResult = nsnull;
rv = NS_ERROR_ILLEGAL_VALUE;
}
return rv;
}
int
nsSchemaValidator::CompareSchemaDateTime(nsSchemaDateTime datetime1,
nsSchemaDateTime datetime2)
{
return nsSchemaValidatorUtils::CompareDateTime(datetime1, datetime2);
}
PRBool
nsSchemaValidator::IsValidSchemaDate(const nsAString & aNodeValue,
nsSchemaDate *aResult)
{
PRBool isValid = PR_FALSE;
LOG((" Validating Date:"));
if (aNodeValue.IsEmpty())
return PR_FALSE;
nsAutoString dateString(aNodeValue);
if (dateString.First() == '-') {
aResult->isNegative = PR_TRUE;
}
/*
http://www.w3.org/TR/xmlschema-2/#date
(-)CCYY-MM-DDT
then either: Z
or [+/-]hh:mm
*/
// append 'T' to end to make ParseSchemaDate happy.
dateString.Append('T');
isValid = nsSchemaValidatorUtils::ParseSchemaDate(dateString, aResult);
return isValid;
}
PRBool
nsSchemaValidator::IsValidSchemaDateTime(const nsAString & aNodeValue,
nsSchemaDateTime *aResult)
{
return nsSchemaValidatorUtils::ParseDateTime(aNodeValue, aResult);
}
/* http://w3.org/TR/xmlschema-2/#duration */
nsresult
nsSchemaValidator::ValidateBuiltinTypeDuration(const nsAString & aNodeValue,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
nsCOMPtr<nsISchemaDuration> duration;
isValid = IsValidSchemaDuration(aNodeValue, getter_AddRefs(duration));
if (isValid && !aMaxExclusive.IsEmpty()) {
nsCOMPtr<nsISchemaDuration> maxExclusiveDuration;
if (IsValidSchemaDuration(aMaxExclusive, getter_AddRefs(maxExclusiveDuration)) &&
nsSchemaValidatorUtils::CompareDurations(duration, maxExclusiveDuration) == 1){
isValid = PR_FALSE;
LOG((" Not valid: Value is too large or indeterminate"));
}
}
if (isValid && !aMinExclusive.IsEmpty()) {
nsCOMPtr<nsISchemaDuration> minExclusiveDuration;
if (IsValidSchemaDuration(aMinExclusive, getter_AddRefs(minExclusiveDuration)) &&
nsSchemaValidatorUtils::CompareDurations(minExclusiveDuration, duration) == 1){
isValid = PR_FALSE;
LOG((" Not valid: Value is too small or indeterminate"));
}
}
if (isValid && !aMaxInclusive.IsEmpty()) {
nsCOMPtr<nsISchemaDuration> maxInclusiveDuration;
if (IsValidSchemaDuration(aMaxInclusive, getter_AddRefs(maxInclusiveDuration)) &&
nsSchemaValidatorUtils::CompareDurations(duration, maxInclusiveDuration) == 1){
isValid = PR_FALSE;
LOG((" Not valid: Value is too large or indeterminate"));
}
}
if (isValid && !aMinInclusive.IsEmpty()) {
nsCOMPtr<nsISchemaDuration> minInclusiveDuration;
if (IsValidSchemaDuration(aMinInclusive, getter_AddRefs(minInclusiveDuration)) &&
nsSchemaValidatorUtils::CompareDurations(minInclusiveDuration, duration) == 1){
isValid = PR_FALSE;
LOG((" Not valid: Value is too small or indeterminate"));
}
}
*aResult = isValid;
return NS_OK;
}
// scriptable method for validating and parsing durations
NS_IMETHODIMP
nsSchemaValidator::ValidateBuiltinTypeDuration(const nsAString & aValue,
nsISchemaDuration **aDuration)
{
nsresult rv = NS_OK;
*aDuration = nsnull;
nsCOMPtr<nsISchemaDuration> duration;
if (IsValidSchemaDuration(aValue, getter_AddRefs(duration))) {
duration.swap(*aDuration);
} else {
rv = NS_ERROR_ILLEGAL_VALUE;
}
return rv;
}
PRBool
nsSchemaValidator::IsValidSchemaDuration(const nsAString & aNodeValue,
nsISchemaDuration **aResult)
{
PRBool isValid = PR_FALSE;
nsCOMPtr<nsISchemaDuration> duration;
isValid = nsSchemaValidatorUtils::ParseSchemaDuration(aNodeValue,
getter_AddRefs(duration));
duration.swap(*aResult);
return isValid;
}
/* http://w3.org/TR/xmlschema-2/#integer */
nsresult
nsSchemaValidator::ValidateBuiltinTypeInteger(const nsAString & aNodeValue,
PRUint32 aTotalDigits,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
nsStringArray *aEnumerationList,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
long intValue;
isValid = nsSchemaValidatorUtils::IsValidSchemaInteger(aNodeValue, &intValue);
if (isValid && aTotalDigits) {
PRUint32 length = aNodeValue.Length();
if (aNodeValue.First() == PRUnichar('-'))
length -= 1;
if (length > aTotalDigits) {
isValid = PR_FALSE;
LOG((" Not valid: Too many digits (%d)", length));
}
}
if (isValid && !aMaxExclusive.IsEmpty()){
long maxExclusive;
if (nsSchemaValidatorUtils::IsValidSchemaInteger(aMaxExclusive, &maxExclusive)
&& (nsSchemaValidatorUtils::CompareStrings(aNodeValue, aMaxExclusive) >= 0)) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too large"));
}
}
if (isValid && !aMinExclusive.IsEmpty()){
long minExclusive;
if (nsSchemaValidatorUtils::IsValidSchemaInteger(aMinExclusive, &minExclusive)
&& (nsSchemaValidatorUtils::CompareStrings(aNodeValue, aMinExclusive) <= 0)) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too small"));
}
}
if (isValid && !aMaxInclusive.IsEmpty()){
long maxInclusive;
if (nsSchemaValidatorUtils::IsValidSchemaInteger(aMaxInclusive, &maxInclusive)
&& (nsSchemaValidatorUtils::CompareStrings(aNodeValue, aMaxInclusive) > 0)) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too large"));
}
}
if (isValid && !aMinInclusive.IsEmpty()){
long minInclusive;
if (nsSchemaValidatorUtils::IsValidSchemaInteger(aMinInclusive, &minInclusive)
&& (nsSchemaValidatorUtils::CompareStrings(aNodeValue, aMinInclusive) < 0)) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too small"));
}
}
if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) {
isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue, *aEnumerationList);
}
*aResult = isValid;
return NS_OK;
}
nsresult
nsSchemaValidator::ValidateBuiltinTypeByte(const nsAString & aNodeValue,
PRUint32 aTotalDigits,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
nsStringArray *aEnumerationList,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
long intValue;
isValid = IsValidSchemaByte(aNodeValue, &intValue);
if (aTotalDigits) {
PRUint32 length = aNodeValue.Length();
if (aNodeValue.First() == PRUnichar('-'))
length -= 1;
if (length > aTotalDigits) {
isValid = PR_FALSE;
LOG((" Not valid: Too many digits (%d)", length));
}
}
if (isValid && !aMaxExclusive.IsEmpty()){
long maxExclusive;
if (IsValidSchemaByte(aMaxExclusive, &maxExclusive) &&
(intValue >= maxExclusive)) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too large"));
}
}
if (isValid && !aMinExclusive.IsEmpty()){
long minExclusive;
if (IsValidSchemaByte(aMinExclusive, &minExclusive) &&
(intValue <= minExclusive)) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too small"));
}
}
if (isValid && !aMaxInclusive.IsEmpty()){
long maxInclusive;
if (IsValidSchemaByte(aMaxInclusive, &maxInclusive) &&
(intValue > maxInclusive)) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too large"));
}
}
if (isValid && !aMinInclusive.IsEmpty()){
long minInclusive;
if (IsValidSchemaByte(aMinInclusive, &minInclusive) &&
(intValue < minInclusive)) {
isValid = PR_FALSE;
LOG((" Not valid: Value is too small"));
}
}
if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) {
isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue,
*aEnumerationList);
}
*aResult = isValid;
return NS_OK;
}
/* http://w3.org/TR/xmlschema-2/#byte */
PRBool
nsSchemaValidator::IsValidSchemaByte(const nsAString & aNodeValue, long *aResult)
{
PRBool isValid = PR_FALSE;
long byteValue;
isValid = nsSchemaValidatorUtils::IsValidSchemaInteger(aNodeValue, &byteValue,
PR_TRUE);
if (isValid && ((byteValue > 127) || (byteValue < -128)))
isValid = PR_FALSE;
if (aResult)
*aResult = byteValue;
return isValid;
}
/* http://www.w3.org/TR/xmlschema-2/#float */
nsresult
nsSchemaValidator::ValidateBuiltinTypeFloat(const nsAString & aNodeValue,
PRUint32 aTotalDigits,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
nsStringArray *aEnumerationList,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
float floatValue;
isValid = IsValidSchemaFloat(aNodeValue, &floatValue);
if (isValid && !aMaxExclusive.IsEmpty()){
// If there is a facet and value is NaN,
// then it is not valid.
if (aNodeValue.EqualsLiteral("NaN")) {
isValid = PR_FALSE;
LOG((" Not valid: Value NaN can't be constrained by facets"));
} else {
float maxExclusive;
if (IsValidSchemaFloat(aMaxExclusive, &maxExclusive) &&
(floatValue >= maxExclusive)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%f) is too big", floatValue));
}
}
}
if (isValid && !aMinExclusive.IsEmpty()){
// If there is a facet and value is NaN,
// then it is not valid.
if (aNodeValue.EqualsLiteral("NaN")) {
isValid = PR_FALSE;
LOG((" Not valid: Value NaN can't be constrained by facets"));
} else {
float minExclusive;
if (IsValidSchemaFloat(aMinExclusive, &minExclusive) &&
(floatValue <= minExclusive)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%f) is too small", floatValue));
}
}
}
if (isValid && !aMaxInclusive.IsEmpty()){
// If there is a facet and value is NaN,
// then it is not valid.
if (aNodeValue.EqualsLiteral("NaN")) {
isValid = PR_FALSE;
LOG((" Not valid: Value NaN can't be constrained by facets"));
} else {
float maxInclusive;
if (IsValidSchemaFloat(aMaxInclusive, &maxInclusive) &&
(floatValue > maxInclusive)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%f) is too big", floatValue));
}
}
}
if (isValid && !aMinInclusive.IsEmpty()){
// If there is a facet and value is NaN,
// then it is not valid.
if (aNodeValue.EqualsLiteral("NaN")) {
isValid = PR_FALSE;
LOG((" Not valid: Value NaN can't be constrained by facets"));
} else {
float minInclusive;
if (IsValidSchemaFloat(aMinInclusive, &minInclusive) &&
(floatValue < minInclusive)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%f) is too small", floatValue));
}
}
}
if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) {
isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue,
*aEnumerationList);
}
#ifdef PR_LOGGING
LOG((isValid ? (" Value is valid!") : (" Value is not valid!")));
#endif
*aResult = isValid;
return NS_OK;
}
PRBool
nsSchemaValidator::IsValidSchemaFloat(const nsAString & aNodeValue,
float *aResult)
{
PRBool isValid = PR_TRUE;
nsresult rv = NS_ERROR_ILLEGAL_VALUE;
float floatValue = 0.0f;
// took most of this float conversion code from nsCString::ToFloat
// which isn't frozen so we can't use it directly
NS_LossyConvertUTF16toASCII convertedValue(aNodeValue);
const char *value = convertedValue.get();
PRUint32 length = strlen(value);
if (length > 0) {
char *conv_stopped;
floatValue = (float)PR_strtod(value, &conv_stopped);
if (conv_stopped == value+length) {
rv = NS_OK;
} else {
// the scan failed somewhere before completion
rv = NS_ERROR_ILLEGAL_VALUE;
floatValue = 0.0f;
}
}
if (NS_FAILED(rv)) {
// floats may be INF, -INF and NaN
if (aNodeValue.EqualsLiteral("INF")) {
floatValue = FLT_MAX;
} else if (aNodeValue.EqualsLiteral("-INF")) {
floatValue = - FLT_MAX;
} else if (!aNodeValue.EqualsLiteral("NaN")) {
isValid = PR_FALSE;
}
}
*aResult = floatValue;
return isValid;
}
/* http://www.w3.org/TR/xmlschema-2/#double */
nsresult
nsSchemaValidator::ValidateBuiltinTypeDouble(const nsAString & aNodeValue,
PRUint32 aTotalDigits,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
nsStringArray *aEnumerationList,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
double doubleValue;
isValid = IsValidSchemaDouble(aNodeValue, &doubleValue);
if (isValid && !aMaxExclusive.IsEmpty()) {
// If there is a facet and value is NaN,
// then it is not valid.
if (aNodeValue.EqualsLiteral("NaN")) {
isValid = PR_FALSE;
LOG((" Not valid: Value NaN can't be constrained by facets"));
} else {
double maxExclusive;
if (IsValidSchemaDouble(aMaxExclusive, &maxExclusive) &&
(doubleValue >= maxExclusive)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%f) is too big", doubleValue));
}
}
}
if (isValid && !aMinExclusive.IsEmpty()) {
// If there is a facet and value is NaN,
// then it is not valid.
if (aNodeValue.EqualsLiteral("NaN")) {
isValid = PR_FALSE;
LOG((" Not valid: Value NaN can't be constrained by facets"));
} else {
double minExclusive;
if (IsValidSchemaDouble(aMinExclusive, &minExclusive) &&
(doubleValue <= minExclusive)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%f) is too small", doubleValue));
}
}
}
if (isValid && !aMaxInclusive.IsEmpty()) {
// If there is a facet and value is NaN,
// then it is not valid.
if (aNodeValue.EqualsLiteral("NaN")) {
isValid = PR_FALSE;
LOG((" Not valid: Value NaN can't be constrained by facets"));
} else {
double maxInclusive;
if (IsValidSchemaDouble(aMaxInclusive, &maxInclusive) &&
(doubleValue > maxInclusive)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%f) is too big", doubleValue));
}
}
}
if (isValid && !aMinInclusive.IsEmpty()) {
// If there is a facet and value is NaN,
// then it is not valid.
if (aNodeValue.EqualsLiteral("NaN")) {
isValid = PR_FALSE;
LOG((" Not valid: Value NaN can't be constrained by facets"));
} else {
double minInclusive;
if (IsValidSchemaDouble(aMinInclusive, &minInclusive) &&
(doubleValue < minInclusive)) {
isValid = PR_FALSE;
LOG((" Not valid: Value (%f) is too small", doubleValue));
}
}
}
if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) {
isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue,
*aEnumerationList);
}
#ifdef PR_LOGGING
LOG((isValid ? (" Value is valid!") : (" Value is not valid!")));
#endif
*aResult = isValid;
return NS_OK;
}
PRBool
nsSchemaValidator::IsValidSchemaDouble(const nsAString & aNodeValue,
double *aResult)
{
return nsSchemaValidatorUtils::IsValidSchemaDouble(aNodeValue, aResult);
}
/* http://www.w3.org/TR/xmlschema-2/#decimal */
nsresult
nsSchemaValidator::ValidateBuiltinTypeDecimal(const nsAString & aNodeValue,
PRUint32 aTotalDigits,
PRUint32 aTotalFractionDigits,
PRBool aFractionDigitsSet,
const nsAString & aMaxExclusive,
const nsAString & aMinExclusive,
const nsAString & aMaxInclusive,
const nsAString & aMinInclusive,
nsStringArray *aEnumerationList,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
int strcmp;
nsAutoString wholePart;
nsAutoString wholePartFacet;
nsAutoString fractionPart;
nsAutoString fractionPartFacet;
isValid = IsValidSchemaDecimal(aNodeValue, wholePart, fractionPart);
// totalDigits is supposed to be positiveInteger, 1..n
if (isValid && (aTotalDigits > 0)) {
if (wholePart.Length() > aTotalDigits) {
isValid = PR_FALSE;
LOG((" Not valid: Expected a maximum of %d digits, but found %d.",
aTotalDigits, wholePart.Length()));
}
}
// fractionDigits is nonNegativeInteger, 0..n
if (isValid && aFractionDigitsSet) {
if (fractionPart.Length() > aTotalFractionDigits) {
isValid = PR_FALSE;
LOG((" Not valid: Expected a maximum of %d fraction digits, but found %d.",
aTotalFractionDigits, fractionPart.Length()));
}
}
if (isValid && !aMaxExclusive.IsEmpty() &&
IsValidSchemaDecimal(aMaxExclusive, wholePartFacet, fractionPartFacet)) {
strcmp = nsSchemaValidatorUtils::CompareStrings(wholePart, wholePartFacet);
if (strcmp > 0) {
isValid = PR_FALSE;
} else if (strcmp == 0) {
// if equal check the fraction part
strcmp = CompareFractionStrings(fractionPart, fractionPartFacet);
if (strcmp >= 0)
isValid = PR_FALSE;
}
if (!isValid)
LOG((" Not valid: Value is too big"));
}
if (isValid && !aMinExclusive.IsEmpty() &&
IsValidSchemaDecimal(aMinExclusive, wholePartFacet, fractionPartFacet)) {
strcmp = nsSchemaValidatorUtils::CompareStrings(wholePart, wholePartFacet);
if (strcmp < 0) {
isValid = PR_FALSE;
} else if (strcmp == 0) {
// if equal check the fraction part
strcmp = CompareFractionStrings(fractionPart, fractionPartFacet);
if (strcmp <= 0)
isValid = PR_FALSE;
}
if (!isValid)
LOG((" Not valid: Value is too small"));
}
if (isValid && !aMaxInclusive.IsEmpty() &&
IsValidSchemaDecimal(aMaxInclusive, wholePartFacet, fractionPartFacet)) {
strcmp = nsSchemaValidatorUtils::CompareStrings(wholePart, wholePartFacet);
if (strcmp > 0) {
isValid = PR_FALSE;
} else if (strcmp == 0) {
// if equal check the fraction part
strcmp = CompareFractionStrings(fractionPart, fractionPartFacet);
if (strcmp > 0)
isValid = PR_FALSE;
}
if (!isValid)
LOG((" Not valid: Value is too big"));
}
if (isValid && !aMinInclusive.IsEmpty() &&
IsValidSchemaDecimal(aMinInclusive, wholePartFacet, fractionPartFacet)) {
strcmp = nsSchemaValidatorUtils::CompareStrings(wholePart, wholePartFacet);
if (strcmp < 0) {
isValid = PR_FALSE;
} else if (strcmp == 0) {
// if equal check the fraction part
strcmp = CompareFractionStrings(fractionPart, fractionPartFacet);
if (strcmp < 0)
isValid = PR_FALSE;
}
if (!isValid)
LOG((" Not valid: Value is too small"));
}
if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) {
isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue,
*aEnumerationList);
}
#ifdef PR_LOGGING
LOG((isValid ? (" Value is valid!") : (" Value is not valid!")));
#endif
*aResult = isValid;
return NS_OK;
}
PRBool
nsSchemaValidator::IsValidSchemaDecimal(const nsAString & aNodeValue,
nsAString & aWholePart, nsAString & aFractionPart)
{
PRBool isValid = PR_FALSE;
int findString = aNodeValue.FindChar('.');
if (findString == kNotFound) {
aWholePart.Assign(aNodeValue);
} else {
aWholePart = Substring(aNodeValue, 0, findString);
aFractionPart = Substring(aNodeValue, findString+1,
aNodeValue.Length() - findString - 1);
}
// to make test easier for example
// nsAutoString wh1, wh2, fr1, fr2;
// IsValidSchemaDecimal(NS_LITERAL_STRING("123.456"), wh1,fr1);
// IsValidSchemaDecimal(NS_LITERAL_STRING("000123.4560000"), wh2,fr2);
// if(wh1.Equals(wh2) && fr1.Equals(fr2)){ /* number are equal */ }
nsSchemaValidatorUtils::RemoveLeadingZeros(aWholePart);
nsSchemaValidatorUtils::RemoveTrailingZeros(aFractionPart);
long temp;
isValid = nsSchemaValidatorUtils::IsValidSchemaInteger(aWholePart, &temp);
if (isValid && (findString != kNotFound)) {
// XXX: assuming "2." is not valid
if (aFractionPart.IsEmpty())
isValid = PR_FALSE;
else if ((aFractionPart.First() == '-') || (aFractionPart.First() == '+'))
isValid = PR_FALSE;
else
isValid = nsSchemaValidatorUtils::IsValidSchemaInteger(aFractionPart,
&temp);
}
return isValid;
}
/* compares 2 strings that contain fraction parts of a decimal
-1 - aString1 < aString2
0 - equal
1 - aString1 > aString2
*/
int
nsSchemaValidator::CompareFractionStrings(const nsAString & aString1,
const nsAString & aString2)
{
int cmpresult = 0;
// are the equal?
if (aString1.Equals(aString2))
return 0;
nsAutoString compareString1;
nsAutoString compareString2;
// put the shorter string in compareString1
if (aString1.Length() < aString2.Length()) {
compareString1.Assign(aString1);
compareString2.Assign(aString2);
} else {
compareString1.Assign(aString2);
compareString2.Assign(aString1);
}
const PRUnichar *start1, *end1, *start2, *end2;
compareString1.BeginReading(&start1, &end1);
compareString2.BeginReading(&start2, &end2);
PRBool done = PR_FALSE;
while ((start1 != end1) && !done)
{
if (*++start1 != *++start2)
done = PR_TRUE;
}
// first string has been iterated through, and all matched
// we know the 2 cannot be the same due to the .Equals() check above,
// so this means that string2 is equal to string1 and then has some more
// numbers, meaning string2 is bigger
if ((start1 == end1) && !done) {
cmpresult = -1;
} else if (done) {
// *start1 is != *start2
if (*start1 < *start2)
cmpresult = 1;
else
cmpresult = -1;
}
return cmpresult;
}
/* http://w3c.org/TR/xmlschema-2/#anyURI */
nsresult
nsSchemaValidator::ValidateBuiltinTypeAnyURI(const nsAString & aNodeValue,
PRUint32 aLength,
PRUint32 aMinLength,
PRUint32 aMaxLength,
nsStringArray *aEnumerationList,
PRBool *aResult)
{
PRUint32 length = aNodeValue.Length();
PRBool isValid = PR_FALSE;
isValid = (IsValidSchemaAnyURI(aNodeValue)) &&
(!aLength || length == aLength) &&
(!aMinLength || length >= aMinLength) &&
(!aMaxLength || length <= aMaxLength);
if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) {
isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue,
*aEnumerationList);
}
*aResult = isValid;
return NS_OK;
}
PRBool
nsSchemaValidator::IsValidSchemaAnyURI(const nsAString & aString)
{
PRBool isValid = PR_FALSE;
if (aString.IsEmpty()) {
isValid = PR_TRUE;
} else {
nsCOMPtr<nsIURI> uri, absUri;
// Need to supply a baseURI to allow relative URIs
NS_NewURI(getter_AddRefs(absUri), NS_LITERAL_STRING("http://a"));
nsresult rv = NS_NewURI(getter_AddRefs(uri), aString,
(const char*)nsnull, absUri);
if (rv == NS_OK)
isValid = PR_TRUE;
}
return isValid;
}
// http://w3c.org/TR/xmlschema-2/#base64Binary
nsresult
nsSchemaValidator::ValidateBuiltinTypeBase64Binary(const nsAString & aNodeValue,
PRUint32 aLength,
PRBool aLengthDefined,
PRUint32 aMinLength,
PRBool aMinLengthDefined,
PRUint32 aMaxLength,
PRBool aMaxLengthDefined,
nsStringArray *aEnumerationList,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
PRUint32 length;
char* decodedString;
isValid = IsValidSchemaBase64Binary(aNodeValue, &decodedString);
if (isValid) {
length = strlen(decodedString);
if (aLengthDefined && (length != aLength)) {
isValid = PR_FALSE;
LOG((" Not valid: Not the right length (%d)", length));
}
if (aMinLengthDefined && (length < aMinLength)) {
isValid = PR_FALSE;
LOG((" Not valid: Length (%d) is too small", length));
}
if (aMaxLengthDefined && (length > aMaxLength)) {
isValid = PR_FALSE;
LOG((" Not valid: Length (%d) is too large", length));
}
}
if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) {
isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue,
*aEnumerationList);
}
#ifdef PR_LOGGING
LOG((isValid ? (" Value is valid!") : (" Value is not valid!")));
#endif
nsMemory::Free(decodedString);
*aResult = isValid;
return NS_OK;
}
PRBool
nsSchemaValidator::IsValidSchemaBase64Binary(const nsAString & aString,
char** aDecodedString)
{
*aDecodedString = PL_Base64Decode(NS_ConvertUTF16toUTF8(aString).get(),
aString.Length(), nsnull);
return (*aDecodedString != nsnull);
}
// http://www.w3.org/TR/xmlschema-2/#QName
nsresult
nsSchemaValidator::ValidateBuiltinTypeQName(const nsAString & aNodeValue,
PRUint32 aLength,
PRBool aLengthDefined,
PRUint32 aMinLength,
PRBool aMinLengthDefined,
PRUint32 aMaxLength,
PRBool aMaxLengthDefined,
nsStringArray *aEnumerationList,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
PRUint32 length;
isValid = IsValidSchemaQName(aNodeValue);
if (isValid) {
length = aNodeValue.Length();
if (aLengthDefined && (length != aLength)) {
isValid = PR_FALSE;
LOG((" Not valid: Not the right length (%d)", length));
}
if (aMinLengthDefined && (length < aMinLength)) {
isValid = PR_FALSE;
LOG((" Not valid: Length (%d) is too small", length));
}
if (aMaxLengthDefined && (length > aMaxLength)) {
isValid = PR_FALSE;
LOG((" Not valid: Length (%d) is too large", length));
}
}
if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) {
isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue,
*aEnumerationList);
}
#ifdef PR_LOGGING
LOG((isValid ? (" Value is valid!") : (" Value is not valid!")));
#endif
*aResult = isValid;
return NS_OK;
}
PRBool
nsSchemaValidator::IsValidSchemaQName(const nsAString & aString)
{
PRBool isValid = PR_FALSE;
nsresult rv;
// split type (ns:type) into namespace and type.
nsCOMPtr<nsIParserService> parserService =
do_GetService("@mozilla.org/parser/parser-service;1", &rv);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
const PRUnichar *colon;
const nsString& qName = PromiseFlatString(aString);
rv = parserService->CheckQName(qName, PR_TRUE, &colon);
if (NS_SUCCEEDED(rv)) {
isValid = PR_TRUE;
}
return isValid;
}
// http://www.w3.org/TR/xmlschema-2/#hexBinary
nsresult
nsSchemaValidator::ValidateBuiltinTypeHexBinary(const nsAString & aNodeValue,
PRUint32 aLength,
PRBool aLengthDefined,
PRUint32 aMinLength,
PRBool aMinLengthDefined,
PRUint32 aMaxLength,
PRBool aMaxLengthDefined,
nsStringArray *aEnumerationList,
PRBool *aResult)
{
PRBool isValid = IsValidSchemaHexBinary(aNodeValue);
if (isValid) {
// For hexBinary, length is measured in octets (8 bits) of binary data. So
// one byte of binary data is represented by two hex digits, so we
// divide by 2 to get the binary data length.
PRUint32 binaryDataLength = aNodeValue.Length() / 2;
if (aLengthDefined && (binaryDataLength != aLength)) {
isValid = PR_FALSE;
LOG((" Not valid: Not the right length (%d)", binaryDataLength));
}
if (isValid && aMinLengthDefined && (binaryDataLength < aMinLength)) {
isValid = PR_FALSE;
LOG((" Not valid: Length (%d) is too small", binaryDataLength));
}
if (isValid && aMaxLengthDefined && (binaryDataLength > aMaxLength)) {
isValid = PR_FALSE;
LOG((" Not valid: Length (%d) is too large", binaryDataLength));
}
if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) {
isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue,
*aEnumerationList);
}
}
LOG((isValid ? (" Value is valid!") : (" Value is not valid!")));
*aResult = isValid;
return NS_OK;
}
PRBool
nsSchemaValidator::IsValidSchemaHexBinary(const nsAString & aString)
{
// hex binary length has to be even
PRUint32 length = aString.Length();
if (length % 2 != 0)
return PR_FALSE;
const PRUnichar *start, *end;
aString.BeginReading(&start, &end);
PRBool isValid = PR_TRUE;
// each character has to be in [0-9a-fA-F]
while (start != end) {
PRUnichar temp = *start++;
if (!isxdigit(temp)) {
isValid = PR_FALSE;
break;
}
}
return isValid;
}
#ifdef DEBUG
void
nsSchemaValidator::DumpBaseType(nsISchemaBuiltinType *aBuiltInType)
{
nsAutoString typeName;
aBuiltInType->GetName(typeName);
PRUint16 foo;
aBuiltInType->GetBuiltinType(&foo);
LOG((" Base Type is %s (%d)", NS_ConvertUTF16toUTF8(typeName).get(),foo));
}
#endif
/****************************************************
* Complex Type Validation *
****************************************************/
// http://w3c.org/TR/xmlschema-1/#Complex_Type_Definitions
nsresult
nsSchemaValidator::ValidateComplextype(nsIDOMNode* aNode,
nsISchemaComplexType *aSchemaComplexType,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
*aResult = PR_FALSE;
PRUint16 contentModel;
nsresult rv = aSchemaComplexType->GetContentModel(&contentModel);
NS_ENSURE_SUCCESS(rv, rv);
switch(contentModel) {
case nsISchemaComplexType::CONTENT_MODEL_EMPTY: {
LOG((" complex type, empty content"));
rv = ValidateComplexModelEmpty(aNode, aSchemaComplexType, &isValid);
break;
}
case nsISchemaComplexType::CONTENT_MODEL_SIMPLE: {
LOG((" complex type, simple content"));
rv = ValidateComplexModelSimple(aNode, aSchemaComplexType, &isValid);
break;
}
case nsISchemaComplexType::CONTENT_MODEL_ELEMENT_ONLY: {
LOG((" xsd:element only"));
rv = ValidateComplexModelElement(aNode, aSchemaComplexType, &isValid);
break;
}
case nsISchemaComplexType::CONTENT_MODEL_MIXED: {
LOG((" complex type, mixed content"));
rv = ValidateComplexModelElement(aNode, aSchemaComplexType, &isValid);
break;
}
}
if (!isValid) {
*aResult = PR_FALSE;
return NS_OK;
}
// if we are valid, validate the attributes
nsCOMPtr<nsIDOMNamedNodeMap> attrMap;
rv = aNode->GetAttributes(getter_AddRefs(attrMap));
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 attrCount = 0, ignoreAttrCount = 0, totalAttr = 0, foundAttrCount = 0;
// Get total attributes on node.
// We need to walk all nodes and count attribute nodes who don't get used
// in schema validation - namespace declarations (xmlns=, xmlns:foo=) and
// attributes nodes in the schema instance namespace
attrMap->GetLength(&totalAttr);
nsCOMPtr<nsIDOMNode> attrNode;
nsCOMPtr<nsIDOMAttr> attr;
nsAutoString name;
for (PRUint32 i = 0; i < totalAttr; ++i) {
rv = attrMap->Item(i, getter_AddRefs(attrNode));
NS_ENSURE_SUCCESS(rv, rv);
attr = do_QueryInterface(attrNode);
if (attr) {
// XXX: we need to make this per spec (such as Part 1 3.2.7)
attr->GetName(name);
if (StringBeginsWith(name, NS_LITERAL_STRING("xmlns"))) {
// is the entire string xmlns or does : come right after xmlns?
if (name.Length() == 5 || name.CharAt(5) == ':')
ignoreAttrCount++;
} else {
// check if the namespace is in the schema instance namespace
nsAutoString nsuri;
rv = attr->GetNamespaceURI(nsuri);
NS_ENSURE_SUCCESS(rv, rv);
if (nsuri.EqualsLiteral(NS_SCHEMA_INSTANCE_NAMESPACE))
ignoreAttrCount++;
}
}
}
// we subtract namespace declaration attributes
totalAttr -= ignoreAttrCount;
LOG((" Number of attributes on node: %d (%d omitted)", totalAttr,
ignoreAttrCount));
// get total defined attributes on schema
aSchemaComplexType->GetAttributeCount(&attrCount);
LOG((" Number of schema-defined attributes found: %d", attrCount));
// iterate through all the attribute definitions on the schema and check
// if they pass
PRUint32 count = 0;
nsCOMPtr<nsISchemaAttributeComponent> attrComp;
while (isValid && (count < attrCount)) {
rv = aSchemaComplexType->GetAttributeByIndex(count,
getter_AddRefs(attrComp));
NS_ENSURE_SUCCESS(rv, rv);
rv = ValidateAttributeComponent(aNode, attrComp, &foundAttrCount,
&isValid);
NS_ENSURE_SUCCESS(rv, rv);
++count;
}
// we walked all the attribute definitions. If foundAttrCount is smaller
// than the attributes on the node, we have undefined attributes.
if (isValid && (totalAttr > foundAttrCount)) {
isValid = PR_FALSE;
LOG((" -- Node contains attributes not defined in its schema!"));
}
*aResult = isValid;
return rv;
}
// http://w3c.org/TR/xmlschema-1/#cElement_Declarations
nsresult
nsSchemaValidator::ValidateComplexModelElement(nsIDOMNode* aNode,
nsISchemaComplexType *aSchemaComplexType,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
nsCOMPtr<nsISchemaModelGroup> modelGroup;
nsresult rv = aSchemaComplexType->GetModelGroup(getter_AddRefs(modelGroup));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> leftOvers, startNode;
aNode->GetFirstChild(getter_AddRefs(startNode));
rv = ValidateComplexModelGroup(startNode, modelGroup,
getter_AddRefs(leftOvers), &isValid);
if (isValid && leftOvers) {
// unexpected node
isValid = PR_FALSE;
#ifdef PR_LOGGING
nsAutoString name;
leftOvers->GetNodeName(name);
LOG((" -- Expected end but found more nodes (%s)!",
NS_ConvertUTF16toUTF8(name).get()));
#endif
}
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::ValidateComplexModelEmpty(nsIDOMNode* aNode,
nsISchemaComplexType *aSchemaComplexType,
PRBool *aResult)
{
PRBool isValid = PR_TRUE;
nsresult rv = NS_OK;
nsCOMPtr<nsIDOMNode> currentNode;
rv = aNode->GetFirstChild(getter_AddRefs(currentNode));
NS_ENSURE_SUCCESS(rv, rv);
while (isValid && currentNode) {
PRUint16 nodeType;
currentNode->GetNodeType(&nodeType);
if (nodeType == nsIDOMNode::ELEMENT_NODE ||
nodeType == nsIDOMNode::TEXT_NODE) {
LOG((" -- Empty content model contains element or text!"));
isValid = PR_FALSE;
break;
}
nsCOMPtr<nsIDOMNode> node;
currentNode->GetNextSibling(getter_AddRefs(node));
currentNode.swap(node);
}
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::ValidateComplexModelSimple(nsIDOMNode* aNode,
nsISchemaComplexType *aSchemaComplexType,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
PRUint16 derivation;;
nsresult rv = aSchemaComplexType->GetDerivation(&derivation);
NS_ENSURE_SUCCESS(rv, rv);
// two choices for complex model with simple content: derivation through
// extension or through restriction
switch(derivation) {
case nsISchemaComplexType::DERIVATION_EXTENSION_SIMPLE:
case nsISchemaComplexType::DERIVATION_RESTRICTION_SIMPLE: {
#ifdef PR_LOGGING
if (derivation == nsISchemaComplexType::DERIVATION_EXTENSION_SIMPLE)
LOG((" -- deriviation by extension of a simple type"));
else
LOG((" -- deriviation by restriction of a simple type"));
#endif
nsCOMPtr<nsISchemaSimpleType> simpleBaseType;
rv = aSchemaComplexType->GetSimpleBaseType(getter_AddRefs(simpleBaseType));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString nodeValue;
nsCOMPtr<nsIDOM3Node> domNode3 = do_QueryInterface(aNode);
domNode3->GetTextContent(nodeValue);
rv = ValidateSimpletype(nodeValue, simpleBaseType, &isValid);
break;
}
default:
rv = NS_ERROR_UNEXPECTED;
}
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::ValidateComplexModelGroup(nsIDOMNode* aNode,
nsISchemaModelGroup *aSchemaModelGroup,
nsIDOMNode **aLeftOvers,
PRBool *aResult)
{
nsresult rv = NS_OK;
PRBool notFound = PR_FALSE, isValid = PR_FALSE;
// a model group can be of type All, Sequence or Choice
// http://w3c.org/TR/xmlschema-1/#Model_Groups
PRUint16 compositor;
rv = aSchemaModelGroup->GetCompositor(&compositor);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 validatedNodes = 0;
PRUint32 minOccurs;
aSchemaModelGroup->GetMinOccurs(&minOccurs);
PRUint32 maxOccurs;
aSchemaModelGroup->GetMaxOccurs(&maxOccurs);
PRUint32 particleCount;
aSchemaModelGroup->GetParticleCount(&particleCount);
nsCOMPtr<nsIDOMNode> currentNode(aNode), leftOvers;
switch(compositor) {
case nsISchemaModelGroup::COMPOSITOR_ALL: {
LOG((" - It is a All Compositor (%d)", particleCount));
// xsd:all has several limitations:
// - order does not matter
// - xsd:all can only occur a maximum of once
// - it can contain only xsd:elements, who may only occur 0 or 1 times.
// since an xsd:all can only happen once or never, validate it once
// and return.
rv = ValidateComplexAll(currentNode, aSchemaModelGroup,
getter_AddRefs(leftOvers), &notFound, &isValid);
currentNode = leftOvers;
// if it wasn't found but is required to happen once, it is invalid
if (isValid && notFound && minOccurs == 1) {
isValid = PR_FALSE;
#ifdef PR_LOGGING
nsCOMPtr<nsISchemaParticle> particle;
aSchemaModelGroup->GetParticle(0, getter_AddRefs(particle));
if (particle) {
nsAutoString name;
particle->GetName(name);
LOG((" - Expected one occurance of %s, but found none!",
NS_ConvertUTF16toUTF8(name).get()));
}
#endif
}
break;
}
case nsISchemaModelGroup::COMPOSITOR_SEQUENCE: {
LOG((" - It is a Sequence (%d)", particleCount));
PRUint32 iterations = 0;
isValid = PR_TRUE;
while (currentNode && isValid && (iterations < maxOccurs) && !notFound) {
rv = ValidateComplexSequence(currentNode, aSchemaModelGroup,
getter_AddRefs(leftOvers), &notFound,
&isValid, &validatedNodes);
if (isValid && !notFound) {
iterations++;
}
currentNode = leftOvers;
}
// Special case of found nothing and expected nothing, so it's easy
if (validatedNodes == 0 && iterations == 0 && minOccurs == 0) {
isValid = PR_TRUE;
} else if (isValid && (iterations < minOccurs) && (validatedNodes > 0)) {
// if we didn't hit minOccurs and not empty sequence, invalid
isValid = PR_FALSE;
#ifdef PR_LOGGING
nsCOMPtr<nsISchemaParticle> particle;
nsAutoString name;
aSchemaModelGroup->GetParticle(0, getter_AddRefs(particle));
if (particle) {
particle->GetName(name);
LOG((" - Expected at least %d iterations of %s, only found %d",
minOccurs, NS_ConvertUTF16toUTF8(name).get(), iterations));
}
#endif
}
break;
}
case nsISchemaModelGroup::COMPOSITOR_CHOICE: {
LOG((" - It is a Choice"));
PRUint32 iterations = 0;
isValid = PR_TRUE;
while (currentNode && isValid && (iterations < maxOccurs)) {
rv = ValidateComplexChoice(currentNode, aSchemaModelGroup,
getter_AddRefs(leftOvers), &notFound,
&isValid);
if (isValid) {
iterations++;
}
currentNode = leftOvers;
}
// if we didn't hit minOccurs, invalid
if (isValid && (iterations < minOccurs)) {
isValid = PR_FALSE;
#ifdef PR_LOGGING
nsCOMPtr<nsISchemaParticle> particle;
nsAutoString name;
aSchemaModelGroup->GetParticle(0, getter_AddRefs(particle));
if (particle) {
particle->GetName(name);
LOG((" - Expected at least %d iterations of %s, only found %d",
minOccurs, NS_ConvertUTF16toUTF8(name).get(), iterations));
}
#endif
}
break;
}
}
leftOvers.swap(*aLeftOvers);
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::ValidateComplexSequence(nsIDOMNode* aStartNode,
nsISchemaModelGroup *aSchemaModelGroup,
nsIDOMNode **aLeftOvers,
PRBool *aNotFound, PRBool *aResult,
PRUint32 *aValidatedNodes)
{
if (!aStartNode || !aSchemaModelGroup)
return NS_ERROR_UNEXPECTED;
PRBool isValid = PR_FALSE;
PRBool notFound = PR_FALSE;
// get the model group details
PRUint32 minOccurs;
nsresult rv = aSchemaModelGroup->GetMinOccurs(&minOccurs);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 maxOccurs;
rv = aSchemaModelGroup->GetMaxOccurs(&maxOccurs);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 particleCount;
rv = aSchemaModelGroup->GetParticleCount(&particleCount);
NS_ENSURE_SUCCESS(rv, rv);
// xsd:sequence means that the order of the particles matters
PRUint32 validatedNodes = 0;
PRUint32 particleCounter = 0;
PRUint16 nodeType;
PRBool done = PR_FALSE;
nsCOMPtr<nsISchemaParticle> particle;
nsCOMPtr<nsIDOMNode> currentNode(aStartNode), leftOvers, tmpNode;
LOG(("====================== New Sequence ==========================="));
// while valid and not done
// we are done when we hit a node that doesn't fit our schema
while (!done && currentNode && (particleCounter < particleCount)) {
// get node type
currentNode->GetNodeType(&nodeType);
// if not an element node, skip
if (nodeType != nsIDOMNode::ELEMENT_NODE) {
currentNode->GetNextSibling(getter_AddRefs(tmpNode));
currentNode = tmpNode;
continue;
}
// get the particle
rv = aSchemaModelGroup->GetParticle(particleCounter,
getter_AddRefs(particle));
NS_ENSURE_SUCCESS(rv, rv);
rv = ValidateComplexParticle(currentNode, particle,
getter_AddRefs(leftOvers), &notFound, &isValid);
NS_ENSURE_SUCCESS(rv, rv);
// if we found the node, increment the amount of validated nodes.
if (!notFound)
validatedNodes++;
if (isValid) {
particleCounter++;
} else {
done = PR_TRUE;
}
// valid and not found means it was optional, so ok
if (isValid && notFound) {
notFound = PR_FALSE;
}
// not valid and not found means we finish this sequence. If any particles
// are left that are required, that will be handled below.
if (!isValid && notFound) {
isValid = PR_TRUE;
particleCounter++;
}
currentNode = leftOvers;
}
*aValidatedNodes = validatedNodes;
if (validatedNodes == 0) {
// we didn't walk through any nodes, thus empty sequence. The caller
// will check if enough occurances (minOccurs) on the sequence happened.
// We need to continue to make sure we don't have all element
// declarations with each having minOccurs=0, thus empty content is allowed.
isValid = PR_TRUE;
notFound = PR_TRUE;
}
// check if any of the remaining particles are required
while (isValid && (particleCounter < particleCount)) {
nsCOMPtr<nsISchemaParticle> tmpParticle;
rv = aSchemaModelGroup->GetParticle(particleCounter,
getter_AddRefs(tmpParticle));
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 tmpMinOccurs;
rv = tmpParticle->GetMinOccurs(&tmpMinOccurs);
NS_ENSURE_SUCCESS(rv, rv);
if (tmpMinOccurs == 0) {
// this particle isn't required
particleCounter++;
} else {
isValid = PR_FALSE;
#ifdef PR_LOGGING
nsAutoString particleName;
tmpParticle->GetName(particleName);
LOG((" - Nodelist missing required element (%s)",
NS_ConvertUTF16toUTF8(particleName).get()));
#endif
}
}
// make sure aLeftOvers points to null or an element node
nsSchemaValidatorUtils::SetToNullOrElement(currentNode, aLeftOvers);
*aNotFound = notFound;
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::ValidateComplexParticle(nsIDOMNode* aNode,
nsISchemaParticle *aSchemaParticle,
nsIDOMNode **aLeftOvers,
PRBool *aNotFound,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
PRBool notFound = PR_FALSE;
PRUint16 particleType;
nsresult rv = aSchemaParticle->GetParticleType(&particleType);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 minOccurs;
rv = aSchemaParticle->GetMinOccurs(&minOccurs);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 maxOccurs;
rv = aSchemaParticle->GetMaxOccurs(&maxOccurs);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> leftOvers, tmpNode;
switch(particleType) {
case nsISchemaParticle::PARTICLE_TYPE_ELEMENT: {
LOG((" -- Particle is an Element"));
PRUint32 iterations = 0;
PRBool done = PR_FALSE;
nsAutoString nodeName, particleName;
leftOvers = aNode;
while (leftOvers && !done && (iterations < maxOccurs)) {
// get node type
PRUint16 nodeType;
leftOvers->GetNodeType(&nodeType);
// if not an element node, skip
if (nodeType != nsIDOMNode::ELEMENT_NODE) {
rv = leftOvers->GetNextSibling(getter_AddRefs(tmpNode));
NS_ENSURE_SUCCESS(rv, rv);
leftOvers = tmpNode;
continue;
}
// use localname since SchemaLoader has already resolved all namespace
// references for us.
leftOvers->GetLocalName(nodeName);
rv = aSchemaParticle->GetName(particleName);
NS_ENSURE_SUCCESS(rv, rv);
if (nodeName.Equals(particleName)) {
LOG(("<%s>", NS_ConvertUTF16toUTF8(nodeName).get()));
rv = ValidateComplexElement(leftOvers, aSchemaParticle, &isValid);
NS_ENSURE_SUCCESS(rv, rv);
LOG(("</%s> (Valid = %s)",
NS_ConvertUTF16toUTF8(nodeName).get(),
(isValid ? "true" : "false")));
// set rest to the next element if node is valid
if (isValid) {
rv = leftOvers->GetNextSibling(getter_AddRefs(tmpNode));
NS_ENSURE_SUCCESS(rv, rv);
leftOvers = tmpNode;
}
iterations++;
done = !isValid;
} else {
done = PR_TRUE;
}
}
// optional and not found, so ok
if (!isValid && (iterations == 0) && (minOccurs == 0)) {
isValid = PR_TRUE;
} else if ((iterations > 0) && (iterations < minOccurs)) {
// we stopped finding the element, but haven't met the minOccurs
isValid = PR_FALSE;
LOG((" -- Unexpected Node Found (%s), was expecting %s",
NS_ConvertUTF16toUTF8(nodeName).get(),
NS_ConvertUTF16toUTF8(particleName).get()));
}
notFound = (iterations == 0);
break;
}
case nsISchemaParticle::PARTICLE_TYPE_MODEL_GROUP: {
LOG((" -- Particle is an Model Group"));
nsCOMPtr<nsISchemaModelGroup> modelGroup =
do_QueryInterface(aSchemaParticle);
rv = ValidateComplexModelGroup(aNode, modelGroup,
getter_AddRefs(leftOvers), &isValid);
break;
}
case nsISchemaParticle::PARTICLE_TYPE_ANY:
return Validate(aNode, aResult);
}
leftOvers.swap(*aLeftOvers);
*aNotFound = notFound;
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::GetElementXsiType(nsIDOMNode* aNode,
nsISchemaType** aType)
{
nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(aNode);
NS_ENSURE_STATE(domElement);
PRBool hasTypeAttribute = PR_FALSE;
nsresult rv = domElement->HasAttributeNS(NS_LITERAL_STRING(
NS_SCHEMA_INSTANCE_NAMESPACE),
NS_LITERAL_STRING("type"),
&hasTypeAttribute);
NS_ENSURE_SUCCESS(rv, rv);
/* XXX: This all may need to change when
element.GetSchemaTypeInfo() is implemented from DOM Level 3 Core,
see:
http://www.w3.org/TR/DOM-Level-3-Core/core.html#Attr-schemaTypeInfo
*/
if (hasTypeAttribute) {
LOG((" -- found xsi:type attribute"));
nsAutoString typeAttribute;
rv = domElement->GetAttributeNS(NS_LITERAL_STRING(
NS_SCHEMA_INSTANCE_NAMESPACE),
NS_LITERAL_STRING("type"),
typeAttribute);
NS_ENSURE_SUCCESS(rv, rv);
LOG((" Type is: %s", NS_ConvertUTF16toUTF8(typeAttribute).get()));
if (typeAttribute.IsEmpty())
return NS_ERROR_SCHEMAVALIDATOR_NO_TYPE_FOUND;
// split type (ns:type) into namespace and type.
nsCOMPtr<nsIParserService> parserService =
do_GetService("@mozilla.org/parser/parser-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
const nsString& qName = PromiseFlatString(typeAttribute);
const PRUnichar *colon;
rv = parserService->CheckQName(qName, PR_TRUE, &colon);
NS_ENSURE_SUCCESS(rv, rv);
const PRUnichar* end = qName.EndReading();
nsAutoString schemaTypePrefix, schemaType, schemaTypeNamespace;
if (!colon) {
// colon not found, so no prefix
schemaType.Assign(typeAttribute);
// get namespace from node
aNode->GetNamespaceURI(schemaTypeNamespace);
} else {
schemaTypePrefix.Assign(Substring(qName.get(), colon));
schemaType.Assign(Substring(colon + 1, end));
// get the namespace url from the prefix
nsCOMPtr<nsIDOM3Node> domNode3 = do_QueryInterface(aNode);
rv = domNode3->LookupNamespaceURI(schemaTypePrefix, schemaTypeNamespace);
NS_ENSURE_SUCCESS(rv, rv);
}
LOG((" Type to validate against is %s:%s",
NS_LossyConvertUTF16toASCII(schemaTypePrefix).get(),
NS_LossyConvertUTF16toASCII(schemaType).get()));
// no schemas loaded and type is not builtin, abort
if (!mSchema &&
!schemaTypeNamespace.EqualsLiteral(NS_SCHEMA_1999_NAMESPACE) &&
!schemaTypeNamespace.EqualsLiteral(NS_SCHEMA_2001_NAMESPACE))
return NS_ERROR_SCHEMAVALIDATOR_NO_SCHEMA_LOADED;
// get the type
rv = GetType(schemaType, schemaTypeNamespace, aType);
NS_ENSURE_SUCCESS(rv, rv);
}
return rv;
}
nsresult
nsSchemaValidator::ValidateComplexElement(nsIDOMNode* aNode,
nsISchemaParticle *aSchemaParticle,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
nsCOMPtr<nsISchemaElement> schemaElement(do_QueryInterface(aSchemaParticle));
if (!schemaElement)
return NS_ERROR_UNEXPECTED;
// will hold the type to validate against
nsCOMPtr<nsISchemaType> type;
nsresult rv = GetElementXsiType(aNode, getter_AddRefs(type));
NS_ENSURE_SUCCESS(rv, rv);
if (!type) {
rv = schemaElement->GetType(getter_AddRefs(type));
NS_ENSURE_SUCCESS(rv, rv);
if (!type)
return NS_ERROR_UNEXPECTED;
}
PRUint16 typeValue;
rv = type->GetSchemaType(&typeValue);
NS_ENSURE_SUCCESS(rv, rv);
switch(typeValue) {
case nsISchemaType::SCHEMA_TYPE_SIMPLE: {
nsCOMPtr<nsISchemaSimpleType> simpleType(do_QueryInterface(type));
if (simpleType) {
LOG((" Element is a simple type!"));
rv = ValidateAgainstType(aNode, simpleType, &isValid);
}
break;
}
case nsISchemaType::SCHEMA_TYPE_COMPLEX: {
nsCOMPtr<nsISchemaComplexType> complexType(do_QueryInterface(type));
if (complexType) {
LOG((" Element is a complex type!"));
rv = ValidateAgainstType(aNode, complexType, &isValid);
}
break;
}
case nsISchemaType::SCHEMA_TYPE_PLACEHOLDER: {
rv = NS_ERROR_NOT_IMPLEMENTED;
break;
}
}
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::ValidateComplexChoice(nsIDOMNode* aStartNode,
nsISchemaModelGroup *aSchemaModelGroup,
nsIDOMNode **aLeftOvers,
PRBool *aNotFound, PRBool *aResult)
{
// get the model group details
PRUint32 minOccurs;
nsresult rv = aSchemaModelGroup->GetMinOccurs(&minOccurs);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 maxOccurs;
rv = aSchemaModelGroup->GetMaxOccurs(&maxOccurs);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 particleCount;
rv = aSchemaModelGroup->GetParticleCount(&particleCount);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNodeList> nodeList;
aStartNode->GetChildNodes(getter_AddRefs(nodeList));
NS_ENSURE_SUCCESS(rv, rv);
if (!nodeList)
return NS_ERROR_UNEXPECTED;
PRUint32 childNodesLength;
rv = nodeList->GetLength(&childNodesLength);
NS_ENSURE_SUCCESS(rv, rv);
/*
xsd:choice means one of the particles must validate.
*/
PRBool isValid = PR_FALSE;
PRBool notFound = PR_FALSE;
nsCOMPtr<nsISchemaParticle> particle;
nsCOMPtr<nsIDOMNode> currentNode(aStartNode), leftOvers, tmpNode;
nsAutoString localName, particleName;
PRUint32 particleCounter = 0;
LOG(("======================== New Choice ==============================="));
while (!isValid && currentNode && (particleCounter < particleCount)) {
// get node type
PRUint16 nodeType;
currentNode->GetNodeType(&nodeType);
// if not an element node, skip
if (nodeType != nsIDOMNode::ELEMENT_NODE) {
currentNode->GetNextSibling(getter_AddRefs(tmpNode));
currentNode = tmpNode;
continue;
}
// get the particle
rv = aSchemaModelGroup->GetParticle(particleCounter, getter_AddRefs(particle));
NS_ENSURE_SUCCESS(rv, rv);
particle->GetName(particleName);
currentNode->GetLocalName(localName);
// if the particle has no name (so not an xsd:element) or the name matches
// the node's localname, then we should try to validate.
if (particleName.IsEmpty() || localName.Equals(particleName)) {
rv = ValidateComplexParticle(currentNode, particle,
getter_AddRefs(leftOvers), &notFound,
&isValid);
// if not valid and the names matched, we can bail early
if (!isValid && localName.Equals(particleName)) {
// The schema spec says that you can't have 2 particles with the same name,
// so we know we don't have to continue looking for matching particles.
LOG((" Invalid: We found a matching particle, but it did not validate"));
break;
}
currentNode = leftOvers;
}
particleCounter++;
}
if (!isValid) {
// None of the particles managed to validate.
notFound = PR_TRUE;
}
// make sure aLeftOvers points to null or an element node
nsSchemaValidatorUtils::SetToNullOrElement(currentNode, aLeftOvers);
*aNotFound = notFound;
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::ValidateComplexAll(nsIDOMNode* aStartNode,
nsISchemaModelGroup *aSchemaModelGroup,
nsIDOMNode **aLeftOvers, PRBool *aNotFound,
PRBool *aResult)
{
if (!aStartNode || !aSchemaModelGroup)
return NS_ERROR_UNEXPECTED;
// get the model group details
PRUint32 minOccurs;
nsresult rv = aSchemaModelGroup->GetMinOccurs(&minOccurs);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 maxOccurs;
rv = aSchemaModelGroup->GetMaxOccurs(&maxOccurs);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 particleCount;
rv = aSchemaModelGroup->GetParticleCount(&particleCount);
NS_ENSURE_SUCCESS(rv, rv);
// since xsd:all does not care about order, we need to record how often each
// particle was hit
nsDataHashtable<nsISupportsHashKey,PRUint32> particleHits;
if (!particleHits.Init())
return NS_ERROR_OUT_OF_MEMORY;
// xsd:all means that the order of the particles does not matter, and
// that particles can only occur 0 or 1 time
PRBool isValid = PR_FALSE;
PRBool notFound = PR_FALSE;
PRBool done = PR_FALSE;
nsCOMPtr<nsISchemaParticle> particle;
nsCOMPtr<nsIDOMNode> currentNode(aStartNode), leftOvers, tmpNode;
nsAutoString localName, particleName;
for (PRUint32 i = 0; i < particleCount; ++i) {
rv = aSchemaModelGroup->GetParticle(i, getter_AddRefs(particle));
NS_ENSURE_SUCCESS(rv, rv);
particleHits.Put(particle, 0);
}
LOG(("====================== New All ==========================="));
PRUint32 validatedNodes = 0;
// we are done when we hit a node that doesn't fit our schema
while (!done && currentNode) {
// get node type
PRUint16 nodeType;
currentNode->GetNodeType(&nodeType);
currentNode->GetLocalName(localName);
// if not an element node, skip
if (nodeType != nsIDOMNode::ELEMENT_NODE) {
currentNode->GetNextSibling(getter_AddRefs(tmpNode));
currentNode = tmpNode;
continue;
}
LOG((" - Validating element (%s)",
NS_ConvertUTF16toUTF8(localName).get()));
// walk all the particles until we find one that validates
PRUint32 particleNum = 0;
PRBool foundParticle = PR_FALSE;
while (!foundParticle && particleNum < particleCount) {
rv = aSchemaModelGroup->GetParticle(particleNum, getter_AddRefs(particle));
NS_ENSURE_SUCCESS(rv, rv);
particle->GetName(particleName);
if (particleName.Equals(localName)) {
// try to validate
rv = ValidateComplexParticle(currentNode, particle,
getter_AddRefs(leftOvers), &notFound,
&foundParticle);
NS_ENSURE_SUCCESS(rv, rv);
}
if (foundParticle) {
validatedNodes++;
PRUint32 hitCount = 0;
particleHits.Get(particle, &hitCount);
if (hitCount > 0) {
// particles in an xsd:all can only occur a maximum of once. If we hit
// a particle twice, we finish this iteration and reset the leftover
// to the currentNode. We basically say this xsd:all is done, the current
// node might be the start of another compositor (assuming all required
// particles are hit
foundParticle = PR_TRUE;
leftOvers = currentNode;
done = PR_TRUE;
LOG((" -- Particle (%s) occured more than once, so ending",
NS_ConvertUTF16toUTF8(particleName).get()));
break;
} else {
hitCount++;
particleHits.Put(particle, hitCount);
LOG((" -- Element validated"));
}
} else {
particleNum++;
}
}
// set isvalid
isValid = foundParticle;
if (!isValid) {
done = PR_TRUE;
LOG((" -- Element could not be validated!"));
}
currentNode = leftOvers;
}
if (validatedNodes == 0) {
// we didn't walk through any nodes, thus empty sequence. The caller
// will check if enough occurances (minOccurs) happened.
isValid = PR_TRUE;
notFound = PR_TRUE;
} else {
// check if any of the particles didn't occur enough. We already checked
// if a particle is hit more than once
PRUint32 hits = 0;
PRUint32 particleMinOccurs, particleMaxOccurs;
for (PRUint32 i = 0; i < particleCount; ++i) {
rv = aSchemaModelGroup->GetParticle(i, getter_AddRefs(particle));
NS_ENSURE_SUCCESS(rv, rv);
// we assume the schema is valid and min/max is not larger that 1
particle->GetMinOccurs(&particleMinOccurs);
particle->GetMaxOccurs(&particleMaxOccurs);
particleHits.Get(particle, &hits);
if (hits < particleMinOccurs || hits > particleMaxOccurs) {
isValid = PR_FALSE;
particle->GetName(particleName);
LOG((" - Particle (%s) occured %d times, but should have occured [%d, %d] times",
NS_ConvertUTF16toUTF8(particleName).get(), hits, particleMinOccurs,
particleMaxOccurs));
break;
}
}
}
// make sure aLeftOvers points to null or an element node
nsSchemaValidatorUtils::SetToNullOrElement(currentNode, aLeftOvers);
*aNotFound = notFound;
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::ValidateAttributeComponent(nsIDOMNode* aNode,
nsISchemaAttributeComponent *aAttrComp,
PRUint32 *aFoundAttrCount,
PRBool *aResult)
{
PRBool isValid = PR_FALSE;
PRUint16 componentType;
nsresult rv = aAttrComp->GetComponentType(&componentType);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString name;
rv = aAttrComp->GetName(name);
NS_ENSURE_SUCCESS(rv, rv);
switch(componentType) {
case nsISchemaAttributeComponent::COMPONENT_TYPE_ATTRIBUTE: {
nsCOMPtr<nsISchemaAttribute> attr(do_QueryInterface(aAttrComp, &rv));
NS_ENSURE_SUCCESS(rv, rv);
LOG((" Attribute Component (%s) is an attribute!",
NS_ConvertUTF16toUTF8(name).get()));
rv = ValidateSchemaAttribute(aNode, attr, name, aFoundAttrCount, &isValid);
NS_ENSURE_SUCCESS(rv, rv);
break;
}
case nsISchemaAttributeComponent::COMPONENT_TYPE_GROUP: {
nsCOMPtr<nsISchemaAttributeGroup> attrGroup(do_QueryInterface(aAttrComp, &rv));
NS_ENSURE_SUCCESS(rv, rv);
LOG((" Attribute Component (%s) is an attribute group!",
NS_ConvertUTF16toUTF8(name).get()));
rv = ValidateSchemaAttributeGroup(aNode, attrGroup, name, aFoundAttrCount,
&isValid);
NS_ENSURE_SUCCESS(rv, rv);
break;
}
case nsISchemaAttributeComponent::COMPONENT_TYPE_ANY: {
// for now we just accept this one as being valid.
// should look at the attribute namespace and validate the
// attribute against it
// XXX: implement this
isValid = PR_TRUE;
break;
}
}
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::ValidateSchemaAttribute(nsIDOMNode* aNode,
nsISchemaAttribute *aAttr,
const nsAString & aAttrName,
PRUint32 *aFoundAttrCount,
PRBool *aResult)
{
PRUint16 use;
nsresult rv = aAttr->GetUse(&use);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString fixedValue, attrValue;
rv = aAttr->GetFixedValue(fixedValue);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISchemaSimpleType> simpleType;
rv = aAttr->GetType(getter_AddRefs(simpleType));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMElement> elm(do_QueryInterface(aNode));
// check if the attribute is to be qualified or not
nsAutoString qualifiedNamespace;
aAttr->GetQualifiedNamespace(qualifiedNamespace);
PRBool hasAttr = PR_FALSE;
PRBool isValid = PR_FALSE;
if (!qualifiedNamespace.IsEmpty()) {
rv = elm->HasAttributeNS(qualifiedNamespace, aAttrName, &hasAttr);
NS_ENSURE_SUCCESS(rv, rv);
if (hasAttr) {
rv = elm->GetAttributeNS(qualifiedNamespace, aAttrName, attrValue);
NS_ENSURE_SUCCESS(rv, rv);
}
} else {
rv = elm->HasAttribute(aAttrName, &hasAttr);
NS_ENSURE_SUCCESS(rv, rv);
if (hasAttr) {
rv = elm->GetAttribute(aAttrName, attrValue);
NS_ENSURE_SUCCESS(rv, rv);
}
}
if (!hasAttr) {
// no attribute found
if (use == nsISchemaAttribute::USE_OPTIONAL) {
isValid = PR_TRUE;
LOG((" -- attribute not found, but optional, so fine!"));
} else if (use == nsISchemaAttribute::USE_REQUIRED) {
// we default to invalid
LOG((" -- attribute not found, but required!"));
} else if (use == nsISchemaAttribute::USE_PROHIBITED) {
// prohibited and doesn't exist is valid
isValid = PR_TRUE;
LOG((" -- attribute not found, but prohibited, so fine!"));
}
} else if (!fixedValue.IsEmpty()) {
// XXX: what about default="" ?
// we assume the default or fixed value is valid for now
(*aFoundAttrCount)++;
if (attrValue.Equals(fixedValue)) {
LOG((" -- attribute has fixed value and it equals the attribute value."));
isValid = PR_TRUE;
} else {
LOG((" -- attribute has fixed value, but does not equal the attribute value!"));
}
} else {
(*aFoundAttrCount)++;
if (use == nsISchemaAttribute::USE_PROHIBITED) {
// If it is is prohibited and it exists, we don't have to do anything,
// since we default to invalid.
LOG((" -- attribute prohibited!"));
} else {
if (simpleType) {
// save the type on the attribute
nsCOMPtr<nsIDOMAttr> attrNode;
if (NS_SUCCEEDED(elm->GetAttributeNode(aAttrName,
getter_AddRefs(attrNode)))) {
nsCOMPtr<nsIWritableVariant> holder =
do_CreateInstance("@mozilla.org/variant;1");
NS_ENSURE_STATE(holder);
holder->SetAsInterface(nsISchemaType::GetIID(), simpleType);
// and save on the node
nsCOMPtr<nsIAttribute> pAttribute(do_QueryInterface(attrNode));
if (pAttribute) {
// we have to be really careful to set the destructor function
// correctly. this also has to be a pointer to a variant
nsCOMPtr<nsIAtomService> atomServ =
do_GetService(NS_ATOMSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAtom> key;
rv = atomServ->GetAtomUTF8("xsdtype", getter_AddRefs(key));
NS_ENSURE_SUCCESS(rv, rv);
nsIVariant *pVariant = holder;
NS_IF_ADDREF(pVariant);
rv = pAttribute->SetProperty(key, pVariant, &VariantDTor);
NS_ENSURE_SUCCESS(rv, rv);
}
}
rv = ValidateSimpletype(attrValue, simpleType, &isValid);
} else {
// If no type exists (ergo no simpleType), we should use the
// simple ur-type definition defined at:
// (http://www.w3.org/TR/xmlschema-1/#simple-ur-type-itself).
// XXX: So for now, we default to true.
isValid = PR_TRUE;
}
}
}
*aResult = isValid;
return rv;
}
nsresult
nsSchemaValidator::ValidateSchemaAttributeGroup(nsIDOMNode* aNode,
nsISchemaAttributeGroup *aAttrGroup,
const nsAString & aAttrName,
PRUint32 *aFoundAttrCount,
PRBool *aResult)
{
PRBool isValid = PR_TRUE;
PRUint32 attrCount, count = 0;
nsresult rv = aAttrGroup->GetAttributeCount(&attrCount);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISchemaAttributeComponent> attrComp;
while (isValid && (count < attrCount)) {
rv = aAttrGroup->GetAttributeByIndex(count, getter_AddRefs(attrComp));
NS_ENSURE_SUCCESS(rv, rv);
rv = ValidateAttributeComponent(aNode, attrComp, aFoundAttrCount,
&isValid);
NS_ENSURE_SUCCESS(rv, rv);
++count;
}
*aResult = isValid;
return rv;
}