Files
Mozilla/mozilla/extensions/schema-validation/src/nsSchemaValidatorUtils.cpp
doronr%us.ibm.com 2f0b7efc80 Patch for bug 223097 was missing an include that caused windows to not be able to compile schema validation. not part of default build
git-svn-id: svn://10.0.0.236/trunk@175166 18797224-902f-48f8-a5cc-f745e15eee43
2005-06-27 14:12:51 +00:00

1343 lines
36 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
* IBM Corporation. All Rights Reserved.
*
* Contributor(s):
* Doron Rosenberg <doronr@us.ibm.com> (original author)
*
* 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 ***** */
// string includes
#include "nsReadableUtils.h"
#include "nsString.h"
#include "nsUnicharUtils.h"
#include "nsISchema.h"
#include "nsSchemaValidator.h"
#include "nsSchemaValidatorUtils.h"
#include "nsISchemaValidatorRegexp.h"
#include "nsSchemaDuration.h"
#include <stdlib.h>
#include <math.h>
#include <errno.h>
#include <limits.h>
#include "prlog.h"
#include "prprf.h"
#include "prtime.h"
#ifdef PR_LOGGING
PRLogModuleInfo *gSchemaValidationUtilsLog = PR_NewLogModule("schemaValidation");
#define LOG(x) PR_LOG(gSchemaValidationUtilsLog, PR_LOG_DEBUG, x)
#define LOG_ENABLED() PR_LOG_TEST(gSchemaValidationUtilsLog, PR_LOG_DEBUG)
#else
#define LOG(x)
#endif
PRBool
nsSchemaValidatorUtils::IsValidSchemaInteger(const nsAString & aNodeValue,
long *aResult)
{
return !aNodeValue.IsEmpty() &&
IsValidSchemaInteger(NS_ConvertUTF16toUTF8(aNodeValue).get(), aResult);
}
// overloaded, for char* rather than nsAString
PRBool
nsSchemaValidatorUtils::IsValidSchemaInteger(const char* aString, long *aResult)
{
if (strlen(aString) == 0)
return PR_FALSE;
char * pEnd;
long intValue = strtol(aString, &pEnd, 10);
if (aResult)
*aResult = intValue;
return (!((intValue == LONG_MAX || intValue == LONG_MIN) && errno == ERANGE))
&& *pEnd == '\0';
}
PRBool
nsSchemaValidatorUtils::GetPRTimeFromDateTime(const nsAString & aNodeValue,
PRTime *aResult)
{
PRBool isValid = PR_FALSE;
PRBool isNegativeYear = PR_FALSE;
int run = 0;
PRBool doneParsing = PR_FALSE;
char year[80] = "";
char month[3] = "";
char day[3] = "";
char hour[3] = "";
char minute[3] = "";
char second[3] = "";
char fraction_seconds[80] = "";
PRTime dateTime;
char fulldate[100] = "";
nsAutoString datetimeString(aNodeValue);
if (datetimeString.First() == '-') {
isNegativeYear = PR_TRUE;
run = 1;
}
/*
http://www.w3.org/TR/xmlschema-2/#dateTime
(-)CCYY-MM-DDThh:mm:ss(.sss...)
then either: Z
or [+/-]hh:mm
*/
// first handle the date part
// search for 'T'
LOG(("\n Validating DateTime:"));
int findString = datetimeString.FindChar('T');
if (findString >= 0) {
isValid = ParseSchemaDate(Substring(aNodeValue, 0, findString+1), year,
month, day);
if (isValid) {
isValid = ParseSchemaTime(
Substring(aNodeValue, findString + 1, aNodeValue.Length()),
hour, minute, second, fraction_seconds);
}
} else {
// no T, invalid
doneParsing = PR_TRUE;
}
if (isValid && aResult) {
nsCAutoString monthShorthand;
GetMonthShorthand(month, monthShorthand);
// 22-AUG-1993 10:59:12.82
sprintf(fulldate, "%s-%s-%s %s:%s:%s.%s",
day,
monthShorthand.get(),
year,
hour,
minute,
second,
fraction_seconds);
LOG(("\n new date is %s", fulldate));
PRStatus status = PR_ParseTimeString(fulldate, PR_TRUE, &dateTime);
if (status == -1)
isValid = PR_FALSE;
else
*aResult = dateTime;
}
return isValid;
}
PRBool
nsSchemaValidatorUtils::ParseSchemaDate(const nsAString & aStrValue,
char *rv_year, char *rv_month,
char *rv_day)
{
PRBool isValid = PR_FALSE;
/*
http://www.w3.org/TR/xmlschema-2/#date
(-)CCYY-MM-DDT
*/
PRTime dateTime;
nsAString::const_iterator start, end, buffStart;
aStrValue.BeginReading(start);
aStrValue.BeginReading(buffStart);
aStrValue.EndReading(end);
PRUint32 state = 0;
PRUint32 buffLength = 0;
PRBool done = PR_FALSE;
PRUnichar currentChar;
nsAutoString year;
char month[3] = "";
char day[3] = "";
// if year is negative, skip it
if (aStrValue.First() == '-') {
start++;
buffStart = start;
}
while ((start != end) && !done) {
currentChar = *start++;
switch (state) {
case 0: {
// year
if (currentChar == '-') {
year.Assign(Substring(buffStart, --start));
state = 1;
buffLength = 0;
buffStart = ++start;
} else {
// has to be a numerical character or else abort
if ((currentChar > '9') || (currentChar < '0'))
done = PR_TRUE;
buffLength++;
}
break;
}
case 1: {
// month
if (buffLength > 2) {
done = PR_TRUE;
} else if (currentChar == '-') {
if (strcmp(month, "12") == 1) {
done = PR_TRUE;
} else {
state = 2;
buffLength = 0;
buffStart = start;
}
} else {
// has to be a numerical character or else abort
if ((currentChar > '9') || (currentChar < '0'))
done = PR_TRUE;
else
month[buffLength] = currentChar;
buffLength++;
}
break;
}
case 2: {
// day
if (buffLength > 2) {
done = PR_TRUE;
} else if (currentChar == 'T') {
if ((start == end) && (strcmp(day, "31") < 1))
isValid = PR_TRUE;
done = PR_TRUE;
} else {
// has to be a numerical character or else abort
if ((currentChar > '9') || (currentChar < '0'))
done = PR_TRUE;
else {
day[buffLength] = currentChar;
}
buffLength++;
}
break;
}
}
}
if (isValid) {
nsCAutoString monthShorthand;
nsSchemaValidatorUtils::GetMonthShorthand(month, monthShorthand);
// 22-AUG-1993
nsAutoString fullDate;
fullDate.AppendLiteral(day);
fullDate.AppendLiteral("-");
AppendASCIItoUTF16(monthShorthand, fullDate);
fullDate.AppendLiteral("-");
fullDate.Append(year);
LOG(("\n Parsed date is %s", NS_ConvertUTF16toUTF8(fullDate).get()));
PRStatus status = PR_ParseTimeString(NS_ConvertUTF16toUTF8(fullDate).get(),
PR_TRUE, &dateTime);
if (status == -1) {
isValid = PR_FALSE;
} else {
// PRStatus will be 0 for feb 30th, but the returned day will be
// different
char * pEnd;
long val = strtol(day, &pEnd, 10);
PRExplodedTime explodedDateTime;
PR_ExplodeTime(dateTime, PR_GMTParameters, &explodedDateTime);
if (val != explodedDateTime.tm_mday) {
isValid = PR_FALSE;
}
}
if (isValid) {
strcpy(rv_day, day);
strcpy(rv_month, month);
strcpy(rv_year, NS_ConvertUTF16toUTF8(year).get());
}
}
LOG(("\n Date is %s \n", ((isValid) ? "Valid" : "Not Valid")));
return isValid;
}
// parses a string as a schema time type and returns the parsed
// hour/minute/second/fraction seconds as well as if its a valid
// schema time type.
PRBool
nsSchemaValidatorUtils::ParseSchemaTime(const nsAString & aStrValue,
char *rv_hour, char *rv_minute,
char *rv_second, char *rv_fraction_second)
{
PRBool isValid = PR_FALSE;
// time looks like this: HH:MM:SS(.[S]+)(+/-HH:MM)
char timezoneHour[3] = "";
char timezoneMinute[3] = "";
// we store the fraction seconds because PR_ExplodeTime seems to skip them.
nsAutoString usec;
nsAString::const_iterator start, end, buffStart;
aStrValue.BeginReading(start);
aStrValue.BeginReading(buffStart);
aStrValue.EndReading(end);
PRUint32 state = 0;
PRUint32 buffLength = 0;
PRBool done = PR_FALSE;
PRUnichar currentChar;
PRUnichar tzSign = PRUnichar(' ');
while ((start != end) && !done) {
currentChar = *start++;
switch (state) {
case 0: {
// hour
if (buffLength > 2) {
done = PR_TRUE;
} else if (currentChar == ':') {
// validate hour
if (strcmp(NS_ConvertUTF16toUTF8(Substring(buffStart, --start)).get(), "24") == 1) {
done = PR_TRUE;
} else {
state = 1;
buffLength = 0;
buffStart = ++start;
}
} else {
// has to be a numerical character or else abort
if ((currentChar > '9') || (currentChar < '0'))
done = PR_TRUE;
buffLength++;
}
break;
}
case 1 : {
// minute
if (buffLength > 2) {
done = PR_TRUE;
} else if (currentChar == ':') {
// validate minute
if (strcmp(NS_ConvertUTF16toUTF8(Substring(buffStart, --start)).get(), "59") == 1) {
done = PR_TRUE;
} else {
state = 2;
buffLength = 0;
buffStart = ++start;
}
} else {
// has to be a numerical character or else abort
if ((currentChar > '9') || (currentChar < '0'))
done = PR_TRUE;
buffLength++;
}
break;
}
case 2 : {
// seconds
if (buffLength > 2) {
done = PR_TRUE;
} else if (currentChar == 'Z') {
// if its Z, has to be the last character
if ((start == end) &&
(strcmp(NS_ConvertUTF16toUTF8(Substring(buffStart, --start)).get(), "59") != 1)) {
isValid = PR_TRUE;
//sprintf(rv_second, "%s", NS_ConvertUTF16toUTF8(Substring(buffStart, start)).get());
}
done = PR_TRUE;
tzSign = currentChar;
} else if ((currentChar == '+') || (currentChar == '-')) {
// timezone exists
if (strcmp(NS_ConvertUTF16toUTF8(Substring(buffStart, --start)).get(), "59") == 1) {
done = PR_TRUE;
} else {
state = 4;
buffLength = 0;
buffStart = ++start;
tzSign = currentChar;
}
} else if (currentChar == '.') {
// fractional seconds exist
if (strcmp(NS_ConvertUTF16toUTF8(Substring(buffStart, --start)).get(), "59") == 1) {
done = PR_TRUE;
} else {
state = 3;
buffLength = 0;
buffStart = ++start;
}
} else {
// has to be a numerical character or else abort
if ((currentChar > '9') || (currentChar < '0'))
done = PR_TRUE;
buffLength++;
}
break;
}
case 3 : {
// fractional seconds
if (currentChar == 'Z') {
// if its Z, has to be the last character
if (start == end)
isValid = PR_TRUE;
else
done = PR_TRUE;
tzSign = currentChar;
usec.Assign(Substring(buffStart.get(), start.get()-1));
} else if ((currentChar == '+') || (currentChar == '-')) {
// timezone exists
usec.Assign(Substring(buffStart.get(), start.get()-1));
state = 4;
buffLength = 0;
buffStart = start;
tzSign = currentChar;
} else {
// has to be a numerical character or else abort
if ((currentChar > '9') || (currentChar < '0'))
done = PR_TRUE;
buffLength++;
}
break;
}
case 4 : {
// timezone hh:mm
if (buffStart.size_forward() == 5)
isValid = ParseSchemaTimeZone(Substring(buffStart, end), timezoneHour,
timezoneMinute);
done = PR_TRUE;
break;
}
}
}
if (isValid) {
// 10:59:12.82
// use a dummy year to make nspr do the work for us
PRTime dateTime;
nsAutoString fullDate;
fullDate.AppendLiteral("1-1-90 ");
fullDate.Append(aStrValue);
LOG(("\n new time is %s", NS_ConvertUTF16toUTF8(fullDate).get()));
PRStatus status = PR_ParseTimeString(NS_ConvertUTF16toUTF8(fullDate).get(),
PR_TRUE, &dateTime);
if (status == -1) {
isValid = PR_FALSE;
} else {
PRExplodedTime explodedDateTime;
PR_ExplodeTime(dateTime, PR_GMTParameters, &explodedDateTime);
sprintf(rv_hour, "%d", explodedDateTime.tm_hour);
sprintf(rv_minute, "%d", explodedDateTime.tm_min);
sprintf(rv_second, "%d", explodedDateTime.tm_sec);
sprintf(rv_fraction_second, "%s", NS_ConvertUTF16toUTF8(usec).get());
}
}
LOG(("\n Time is %s \n", ((isValid) ? "Valid" : "Not Valid")));
return isValid;
}
PRBool
nsSchemaValidatorUtils::ParseSchemaTimeZone(const nsAString & aStrValue,
char *rv_tzhour, char *rv_tzminute)
{
PRBool isValid = PR_FALSE;
char timezoneHour[3] = "";
char timezoneMinute[3] = "";
nsAString::const_iterator start, end, buffStart;
aStrValue.BeginReading(start);
aStrValue.BeginReading(buffStart);
aStrValue.EndReading(end);
PRUint32 state = 0;
PRUint32 buffLength = 0;
PRBool done = PR_FALSE;
PRUnichar currentChar;
LOG(("\n Validating TimeZone"));
while ((start != end) && !done) {
currentChar = *start++;
switch (state) {
case 0: {
// hour
if (buffLength > 2) {
done = PR_TRUE;
} else if (currentChar == ':') {
timezoneHour[2] = '\0';
if (strcmp(timezoneHour, "24") == 1) {
done = PR_TRUE;
} else {
state = 1;
buffLength = 0;
buffStart = start;
}
} else {
// has to be a numerical character or else abort
if ((currentChar > '9') || (currentChar < '0'))
done = PR_TRUE;
else {
timezoneHour[buffLength] = currentChar;
}
buffLength++;
}
break;
}
case 1: {
// minute
if (buffLength > 1) {
done = PR_TRUE;
} else if (start == end) {
if (buffLength == 1) {
timezoneMinute[2] = '\0';
if (strcmp(timezoneMinute, "59") == 1) {
done = PR_TRUE;
} else {
isValid = PR_TRUE;
}
} else {
done = PR_FALSE;
}
} else {
// has to be a numerical character or else abort
if ((currentChar > '9') || (currentChar < '0')) {
done = PR_TRUE;
} else {
timezoneMinute[buffLength] = currentChar;
}
buffLength++;
}
break;
}
}
}
if (isValid) {
strncpy(rv_tzhour, timezoneHour, 3);
strncpy(rv_tzminute, timezoneMinute, 3);
}
return isValid;
}
/*
-1 - aDateTime1 < aDateTime2
0 - equal
1 - aDateTime1 > aDateTime2
*/
int
nsSchemaValidatorUtils::CompareExplodedDateTime(PRExplodedTime aDateTime1,
PRBool aDateTime1IsNegative,
PRExplodedTime aDateTime2,
PRBool aDateTime2IsNegative)
{
int result;
if (!aDateTime1IsNegative && aDateTime2IsNegative) {
// positive year is always bigger than negative year
result = 1;
} else if (aDateTime1IsNegative && !aDateTime2IsNegative) {
result = -1;
} else {
result = CompareExplodedDate(aDateTime1, aDateTime2);
if (result == 0)
result = CompareExplodedTime(aDateTime1, aDateTime2);
if (aDateTime1IsNegative && aDateTime2IsNegative) {
// -20 is smaller than -21
if (result == -1)
result = 1;
else if (result == 1)
result = -1;
}
}
return result;
}
/*
-1 - aDateTime1 < aDateTime2
0 - equal
1 - aDateTime1 > aDateTime2
*/
int
nsSchemaValidatorUtils::CompareExplodedDate(PRExplodedTime aDateTime1,
PRExplodedTime aDateTime2)
{
int result;
if (aDateTime1.tm_year < aDateTime2.tm_year) {
result = -1;
} else if (aDateTime1.tm_year > aDateTime2.tm_year) {
result = 1;
} else {
if (aDateTime1.tm_month < aDateTime2.tm_month) {
result = -1;
} else if (aDateTime1.tm_month > aDateTime2.tm_month) {
result = 1;
} else {
if (aDateTime1.tm_mday < aDateTime2.tm_mday) {
result = -1;
} else if (aDateTime1.tm_mday > aDateTime2.tm_mday) {
result = 1;
} else {
result = 0;
}
}
}
return result;
}
/*
-1 - aDateTime1 < aDateTime2
0 - equal
1 - aDateTime1 > aDateTime2
*/
int
nsSchemaValidatorUtils::CompareExplodedTime(PRExplodedTime aDateTime1,
PRExplodedTime aDateTime2)
{
int result;
if (aDateTime1.tm_hour < aDateTime2.tm_hour) {
result = -1;
} else if (aDateTime1.tm_hour > aDateTime2.tm_hour) {
result = 1;
} else {
if (aDateTime1.tm_min < aDateTime2.tm_min) {
result = -1;
} else if (aDateTime1.tm_min > aDateTime2.tm_min) {
result = 1;
} else {
if (aDateTime1.tm_sec < aDateTime2.tm_sec) {
result = -1;
} else if (aDateTime1.tm_sec > aDateTime2.tm_sec) {
result = 1;
} else {
if (aDateTime1.tm_usec < aDateTime2.tm_usec) {
result = -1;
} else if (aDateTime1.tm_usec > aDateTime2.tm_usec) {
result = 1;
} else {
result = 0;
}
}
}
}
return result;
}
void
nsSchemaValidatorUtils::GetMonthShorthand(char* aMonth, nsACString & aReturn)
{
PRInt32 i, length = NS_ARRAY_LENGTH(monthShortHand);
for (i = 0; i < length; ++i) {
if (strcmp(aMonth, monthShortHand[i].number) == 0) {
aReturn.AssignASCII(monthShortHand[i].shortHand);
}
}
}
/*
-1 - aYearMonth1 < aYearMonth2
0 - equal
1 - aYearMonth1 > aYearMonth2
*/
int
nsSchemaValidatorUtils::CompareGYearMonth(nsSchemaGYearMonth aYearMonth1,
nsSchemaGYearMonth aYearMonth2)
{
int rv;
if (aYearMonth1.gYear.year > aYearMonth2.gYear.year) {
rv = 1;
} else if (aYearMonth1.gYear.year < aYearMonth2.gYear.year) {
rv = -1;
} else {
// both have the same year
if (aYearMonth1.gMonth.month > aYearMonth2.gMonth.month)
rv = 1;
else if (aYearMonth1.gMonth.month < aYearMonth2.gMonth.month)
rv = -1;
else
rv = 0;
}
return rv;
}
/*
-1 - aMonthDay1 < aMonthDay2
0 - equal
1 - aMonthDay1 > aMonthDay2
*/
int
nsSchemaValidatorUtils::CompareGMonthDay(nsSchemaGMonthDay aMonthDay1,
nsSchemaGMonthDay aMonthDay2)
{
int rv;
if (aMonthDay1.gMonth.month > aMonthDay2.gMonth.month) {
rv = 1;
} else if (aMonthDay1.gMonth.month < aMonthDay2.gMonth.month) {
rv = -1;
} else {
// both have the same year
if (aMonthDay1.gDay.day > aMonthDay2.gDay.day)
rv = 1;
else if (aMonthDay1.gDay.day < aMonthDay2.gDay.day)
rv = -1;
else
rv = 0;
}
return rv;
}
PRBool
nsSchemaValidatorUtils::ParseSchemaDuration(const nsAString & aStrValue,
nsISchemaDuration **aDuration)
{
PRBool isValid = PR_FALSE;
nsAString::const_iterator start, end, buffStart;
aStrValue.BeginReading(start);
aStrValue.BeginReading(buffStart);
aStrValue.EndReading(end);
PRUint32 state = 0;
PRUint32 buffLength = 0;
PRBool done = PR_FALSE;
PRUnichar currentChar;
PRBool isNegative = PR_FALSE;
// make sure leading P is present. Take negative durations into consideration.
if (*start == '-') {
start.advance(1);
if (*start != 'P') {
return PR_FALSE;
} else {
isNegative = PR_TRUE;
buffStart = ++start;
}
} else {
if (*start != 'P')
return PR_FALSE;
else
++start;
}
PRBool errorParsing = PR_FALSE;
PRUint32 length = aStrValue.Length();
nsAutoString parseBuffer, parseBuffer2;
PRBool timeSeparatorFound = PR_FALSE;
// designators may not repeat, so keep track of those we find.
PRBool yearFound = PR_FALSE;
PRBool monthFound = PR_FALSE;
PRBool dayFound = PR_FALSE;
PRBool hourFound = PR_FALSE;
PRBool minuteFound = PR_FALSE;
PRBool secondFound = PR_FALSE;
PRBool fractionSecondFound = PR_FALSE;
PRUint32 year = 0;
PRUint32 month = 0;
PRUint32 day = 0;
PRUint32 hour = 0;
PRUint32 minute = 0;
PRUint32 second = 0;
PRUint32 fractionSecond = 0;
/* durations look like this:
(-)PnYnMnDTnHnMn(.n)S
- P is required, plus sign is invalid
- order is important, so day after year is invalid (PnDnY)
- Y,M,D,H,M,S are called designators
- designators are not allowed without a number before them
- T is the date/time seperator and is only allowed given a time part
*/
while ((start != end) && !done) {
currentChar = *start++;
// not a number - so it has to be a type designator (YMDTHMS)
// it can also be |.| for fractional seconds
if ((currentChar > '9') || (currentChar < '0')){
// first check if the buffer is bigger than what long can store
// which is 11 digits, as we convert to long
if ((parseBuffer.Length() == 10) &&
(CompareStrings(parseBuffer, NS_LITERAL_STRING("2147483647")) == 1)) {
done = PR_TRUE;
} else if (currentChar == 'Y') {
if (yearFound || monthFound || dayFound || timeSeparatorFound) {
done = PR_TRUE;
} else {
state = 0;
yearFound = PR_TRUE;
}
} else if (currentChar == 'M') {
// M is used twice - Months and Minutes
if (!timeSeparatorFound)
if (monthFound || dayFound || timeSeparatorFound) {
done = PR_TRUE;
} else {
state = 1;
monthFound = PR_TRUE;
}
else {
if (!timeSeparatorFound) {
done = PR_TRUE;
} else {
if (minuteFound || secondFound) {
done = PR_TRUE;
} else {
state = 4;
minuteFound = PR_TRUE;
}
}
}
} else if (currentChar == 'D') {
if (dayFound || timeSeparatorFound) {
done = PR_TRUE;
} else {
state = 2;
dayFound = PR_TRUE;
}
} else if (currentChar == 'T') {
// can't have the time seperator more than once
if (timeSeparatorFound)
done = PR_TRUE;
else
timeSeparatorFound = PR_TRUE;
} else if (currentChar == 'H') {
if (!timeSeparatorFound || hourFound || secondFound ) {
done = PR_TRUE;
} else {
state = 3;
hourFound = PR_TRUE;
}
} else if (currentChar == 'S') {
if (!timeSeparatorFound)
done = PR_TRUE;
else
if (secondFound) {
done = PR_TRUE;
} else {
state = 5;
secondFound = PR_TRUE;
}
} else if (currentChar == '.'){
// fractional seconds
if (fractionSecondFound) {
done = PR_TRUE;
} else {
parseBuffer2.Assign(parseBuffer);
parseBuffer.AssignLiteral("");
buffLength = 0;
fractionSecondFound = PR_TRUE;
}
} else {
done = PR_TRUE;
}
// if its a designator and no buffer, invalid per spec. Rule doesn't apply
// if T or '.' (fractional seconds), as we need to parse on for those.
// so P200YM is invalid as there is no number before M
if ((currentChar != 'T') && (currentChar != '.') && (parseBuffer.Length() == 0)) {
done = PR_TRUE;
} else if ((currentChar == 'T') && (start == end)) {
// if 'T' is found but no time data after it, invalid
done = PR_TRUE;
}
if (!done && (currentChar != 'T') && (currentChar != '.')) {
long temp;
switch (state) {
case 0: {
// years
if (!IsValidSchemaInteger(parseBuffer, &temp))
done = PR_TRUE;
else
year = temp;
break;
}
case 1: {
// months
if (!IsValidSchemaInteger(parseBuffer, &temp))
done = PR_TRUE;
else
month = temp;
break;
}
case 2: {
// days
if (!IsValidSchemaInteger(parseBuffer, &temp))
done = PR_TRUE;
else
day = temp;
break;
}
case 3: {
// hours
if (!IsValidSchemaInteger(parseBuffer, &temp))
done = PR_TRUE;
else
hour = temp;
break;
}
case 4: {
// minutes
if (!IsValidSchemaInteger(parseBuffer, &temp))
done = PR_TRUE;
else
minute = temp;
break;
}
case 5: {
// seconds - we have to handle optional fraction seconds as well
if (fractionSecondFound) {
long temp2;
if (!IsValidSchemaInteger(parseBuffer2, &temp) ||
!IsValidSchemaInteger(parseBuffer, &temp2)) {
done = PR_TRUE;
} else {
second = temp;
fractionSecond = temp2;
}
} else {
if (!IsValidSchemaInteger(parseBuffer, &temp))
done = PR_TRUE;
else
second = temp;
}
break;
}
}
}
// clear buffer
parseBuffer.AssignLiteral("");
buffLength = 0;
} else {
if (buffLength > 11) {
done = PR_TRUE;
} else {
parseBuffer.Append(currentChar);
buffLength++;
}
}
}
if ((start == end) && (!done)) {
isValid = PR_TRUE;
}
if (isValid) {
nsISchemaDuration* duration = new nsSchemaDuration(year, month, day, hour, minute, second, fractionSecond, isNegative);
*aDuration = duration;
NS_IF_ADDREF(*aDuration);
}
return isValid;
}
/* compares 2 strings that contain integers.
Schema Integers have no limit, thus converting the strings
into numbers won't work.
-1 - aString1 < aString2
0 - equal
1 - aString1 > aString2
*/
int
nsSchemaValidatorUtils::CompareStrings(const nsAString & aString1,
const nsAString & aString2)
{
int rv;
PRBool isNegative1 = (aString1.First() == PRUnichar('-'));
PRBool isNegative2 = (aString2.First() == PRUnichar('-'));
if (isNegative1 && !isNegative2) {
// negative is always smaller than positive
return -1;
} else if (!isNegative1 && isNegative2) {
// positive is always bigger than negative
return 1;
}
nsAString::const_iterator start1, start2, end1, end2;
aString1.BeginReading(start1);
aString1.EndReading(end1);
aString2.BeginReading(start2);
aString2.EndReading(end2);
// skip negative sign
if (isNegative1)
start1++;
if (isNegative2)
start2++;
// jump over leading zeros
PRBool done = PR_FALSE;
while ((start1 != end1) && !done) {
if (*start1 != '0')
done = PR_TRUE;
else
++start1;
}
done = PR_FALSE;
while ((start2 != end2) && !done) {
if (*start2 != '0')
done = PR_TRUE;
else
++start2;
}
nsAutoString compareString1, compareString2;
compareString1.Assign(Substring(start1, end1));
compareString2.Assign(Substring(start2, end2));
// after removing leading 0s, check if they are the same
if (compareString1.Equals(compareString2)) {
return 0;
}
if (compareString1.Length() > compareString2.Length())
rv = 1;
else if (compareString1.Length() < compareString2.Length())
rv = -1;
else
rv = strcmp(NS_ConvertUTF16toUTF8(compareString1).get(),
NS_ConvertUTF16toUTF8(compareString2).get());
// 3>2, but -2>-3
if (isNegative1 && isNegative2) {
if (rv == 1)
rv = -1;
else
rv = 1;
}
return rv;
}
// For xsd:duration support, the the maximum day for a month/year combo as
// defined in http://w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes.
int
nsSchemaValidatorUtils::GetMaximumDayInMonthFor(int aYearValue, int aMonthValue)
{
int maxDay = 28;
int month = ((aMonthValue - 1) % 12) + 1;
int year = aYearValue + ((aMonthValue - 1) / 12);
/*
Return Value Condition
31 month is either 1, 3, 5, 7, 8, 10, 12
30 month is either 4, 6, 9, 11
29 month is 2 AND either (year % 400 == 0) OR (year % 100 == 0)
AND (year % 4 == 0)
28 Otherwise
*/
if ((month == 1) || (month == 3) || (month == 5) || (month == 7) ||
(month == 8) || (month == 10) || (month == 12))
maxDay = 31;
else if ((month == 4) || (month == 6) || (month == 9) || (month == 11))
maxDay = 30;
else if ((month == 2) && ((year % 400 == 0) || (year % 100 == 0) && (year % 4 == 0)))
maxDay = 29;
return maxDay;
}
/*
* compares 2 durations using the algorithm defined in
* http://w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes by adding them to
* the 4 datetimes specified in http://w3.org/TR/xmlschema-2/#duration-order.
* If not all 4 result in x<y, we return indeterminate per
* http://www.w3.org/TR/xmlschema-2/#facet-comparison-for-durations.
*
* 0 - aDuration1 < aDuration2
* 1 - indeterminate
*
*/
int
nsSchemaValidatorUtils::CompareDurations(nsISchemaDuration *aDuration1,
nsISchemaDuration *aDuration2)
{
int cmp, tmpcmp, i = 0;
PRTime foo;
PRExplodedTime explodedTime, newTime1, newTime2;
char* datetimeArray[] = { "1696-09-01T00:00:00Z", "1697-02-01T00:00:00Z",
"1903-03-01T00:00:00Z", "1903-07-01T00:00:00Z" };
PRBool indeterminate = PR_FALSE;
while (!indeterminate && (i < 4)) {
GetPRTimeFromDateTime(NS_ConvertASCIItoUTF16(datetimeArray[i]), &foo);
PR_ExplodeTime(foo, PR_GMTParameters, &explodedTime);
newTime1 = AddDurationToDatetime(explodedTime, aDuration1);
newTime2 = AddDurationToDatetime(explodedTime, aDuration2);
tmpcmp = CompareExplodedDateTime(newTime1, PR_FALSE, newTime2, PR_FALSE);
if (i > 0) {
if (tmpcmp != cmp || tmpcmp > -1) {
indeterminate = PR_TRUE;
}
}
cmp = tmpcmp;
++i;
}
return indeterminate ? 1 : 0;
}
/*
* This method implements the algorithm described at:
* http://w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes
*/
PRExplodedTime
nsSchemaValidatorUtils::AddDurationToDatetime(PRExplodedTime aDatetime,
nsISchemaDuration *aDuration)
{
PRExplodedTime resultDatetime;
// first handle months
PRUint32 temp;
aDuration->GetMonths(&temp);
temp += aDatetime.tm_month + 1;
resultDatetime.tm_month = ((temp - 1) % 12) + 1;
PRInt32 carry = (temp - 1) / 12;
// years
aDuration->GetYears(&temp);
resultDatetime.tm_year = aDatetime.tm_year + carry + temp;
/* fraction seconds
* XXX: Since the 4 datetimes we add durations to don't have fraction seconds
* we can just add the duration's fraction second (stored as an float),
* which will be < 1.0.
*/
aDuration->GetFractionSeconds(&temp);
resultDatetime.tm_usec = temp * 1000000;
// seconds
aDuration->GetSeconds(&temp);
temp += aDatetime.tm_sec + carry;
resultDatetime.tm_sec = temp % 60;
carry = temp / 60;
// minutes
aDuration->GetMinutes(&temp);
temp += aDatetime.tm_min + carry;
resultDatetime.tm_min = temp % 60;
carry = temp / 60;
// hours
aDuration->GetHours(&temp);
temp += aDatetime.tm_hour + carry;
resultDatetime.tm_hour = temp % 24;
carry = temp / 24;
// days
int maxDay = GetMaximumDayInMonthFor(resultDatetime.tm_year,
resultDatetime.tm_month);
int tempDays = 0;
if (aDatetime.tm_mday > maxDay)
tempDays = maxDay;
else if (aDatetime.tm_mday < 1)
tempDays = 1;
else
tempDays = aDatetime.tm_mday;
aDuration->GetDays(&temp);
resultDatetime.tm_mday = tempDays + carry + temp;
PRBool done = PR_FALSE;
while (!done) {
maxDay = GetMaximumDayInMonthFor(resultDatetime.tm_year,
resultDatetime.tm_month);
if (resultDatetime.tm_mday < 1) {
resultDatetime.tm_mday +=
GetMaximumDayInMonthFor(resultDatetime.tm_year,
resultDatetime.tm_month - 1);
carry = -1;
} else if (resultDatetime.tm_mday > maxDay) {
resultDatetime.tm_mday -= maxDay;
carry = 1;
} else {
done = PR_TRUE;
}
if (!done) {
temp = resultDatetime.tm_month + carry;
resultDatetime.tm_month = ((temp - 1) % 12) + 1;
resultDatetime.tm_year += (temp - 1) / 12;
}
}
LOG(("\n New datetime is %d-%d-%d %d:%d:%d\n", resultDatetime.tm_mday,
resultDatetime.tm_month, resultDatetime.tm_year, resultDatetime.tm_hour,
resultDatetime.tm_min, resultDatetime.tm_sec));
return resultDatetime;
}
PRBool
nsSchemaValidatorUtils::HandleEnumeration(const nsAString &aStrValue,
const nsStringArray &aEnumerationList)
{
PRBool isValid = PR_FALSE;
// check enumeration
PRInt32 count = aEnumerationList.Count();
for (PRInt32 i = 0; i < count; ++i) {
if (aEnumerationList[i]->Equals(aStrValue)) {
isValid = PR_TRUE;
break;
}
}
if (!isValid) {
LOG((" Not valid: Value doesn't match any of the enumerations"));
}
return isValid;
}
void
nsSchemaValidatorUtils::RemoveLeadingZeros(nsAString & aString)
{
nsAString::const_iterator start, end;
aString.BeginReading(start);
aString.EndReading(end);
PRBool done = PR_FALSE;
PRUint32 count = 0, indexstart=0;
if(*start == '+' || *start == '-'){
start++;
indexstart = 1;
}
while ((start != end) && !done)
{
if (*start++ == '0') {
++count;
} else {
done = PR_TRUE;
}
}
// finally, remove the leading zeros
aString.Cut(indexstart, count);
}
void
nsSchemaValidatorUtils::RemoveTrailingZeros(nsAString & aString)
{
nsAString::const_iterator start, end;
aString.BeginReading(start);
aString.EndReading(end);
PRUint32 length = aString.Length();
PRBool done = PR_FALSE;
PRUint32 count = 0;
if(start != end)
end --;
while ((start != end) && !done)
{
if (*end-- == '0') {
++count;
} else {
done = PR_TRUE;
}
}
// finally, remove the leading zeros
aString.Cut(length - count, count);
}