4884 lines
159 KiB
C++
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), ¬Found, &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), ¬Found,
|
|
&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), ¬Found,
|
|
&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), ¬Found, &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), ¬Found,
|
|
&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), ¬Found,
|
|
&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;
|
|
}
|
|
|