[extra] Import Firefox 3.0 beta 5 tarball
[mozilla-extra.git] / extensions / schema-validation / src / nsSchemaValidatorUtils.cpp
blob040582d63a05d01e8bafcfefb044a89bd0bb6b16
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Schema Validation.
17 * The Initial Developer of the Original Code is
18 * IBM Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2004
20 * IBM Corporation. All Rights Reserved.
22 * Contributor(s):
23 * Doron Rosenberg <doronr@us.ibm.com> (original author)
24 * Laurent Jouanneau <laurent@xulfr.org>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 // string includes
41 #include "nsStringAPI.h"
42 #include "nsUnicharUtils.h"
44 #include "nsISVSchema.h"
45 #include "nsSchemaValidator.h"
46 #include "nsSchemaValidatorUtils.h"
47 #include "nsISchemaValidatorRegexp.h"
48 #include "nsSchemaDuration.h"
49 #include "nsServiceManagerUtils.h"
51 #include <stdlib.h>
52 #include <math.h>
53 #include <errno.h>
54 #include <limits.h>
55 #include <float.h>
56 #include "prlog.h"
57 #include "prprf.h"
58 #include "prdtoa.h"
60 #ifdef PR_LOGGING
61 PRLogModuleInfo *gSchemaValidationUtilsLog = PR_NewLogModule("schemaValidation");
63 #define LOG(x) PR_LOG(gSchemaValidationUtilsLog, PR_LOG_DEBUG, x)
64 #define LOG_ENABLED() PR_LOG_TEST(gSchemaValidationUtilsLog, PR_LOG_DEBUG)
65 #else
66 #define LOG(x)
67 #endif
69 PRBool
70 nsSchemaValidatorUtils::IsValidSchemaInteger(const nsAString & aNodeValue,
71 long *aResult, PRBool aOverFlowCheck)
73 return !aNodeValue.IsEmpty() &&
74 IsValidSchemaInteger(NS_ConvertUTF16toUTF8(aNodeValue).get(),
75 aResult, aOverFlowCheck);
78 // overloaded, for char* rather than nsAString
79 PRBool
80 nsSchemaValidatorUtils::IsValidSchemaInteger(const char* aString,
81 long *aResult,
82 PRBool aOverFlowCheck)
84 PRBool isValid = PR_FALSE;
86 if (*aString == 0)
87 return PR_FALSE;
89 char * pEnd;
90 long intValue = strtol(aString, &pEnd, 10);
92 if (aResult)
93 *aResult = intValue;
95 if (aOverFlowCheck) {
96 isValid = (!((intValue == LONG_MAX || intValue == LONG_MIN) && errno == ERANGE))
97 && *pEnd == '\0';
98 } else {
99 isValid = (*pEnd == '\0');
102 return isValid;
105 PRBool
106 nsSchemaValidatorUtils::IsValidSchemaDouble(const nsAString & aNodeValue,
107 double *aResult)
109 return !aNodeValue.IsEmpty() &&
110 IsValidSchemaDouble(NS_ConvertUTF16toUTF8(aNodeValue).get(), aResult);
113 // overloaded, for char* rather than nsAString
114 PRBool
115 nsSchemaValidatorUtils::IsValidSchemaDouble(const char* aString,
116 double *aResult)
118 PRBool isValid = PR_TRUE;
120 if (*aString == 0)
121 return PR_FALSE;
123 char * pEnd;
124 double value = PR_strtod(aString, &pEnd);
126 // If the end pointer desn't point at the end, it wasn't a true double (could
127 // be INF, -INF or NaN though)
128 if (*pEnd != '\0') {
129 nsCAutoString temp(aString);
131 // doubles may be INF, -INF or NaN
132 if (temp.EqualsLiteral("INF")) {
133 value = DBL_MAX;
134 } else if (temp.EqualsLiteral("-INF")) {
135 value = - DBL_MAX;
136 } else if (!temp.EqualsLiteral("NaN")) {
137 isValid = PR_FALSE;
141 if (aResult)
142 *aResult = value;
144 return isValid;
147 PRBool
148 nsSchemaValidatorUtils::ParseDateTime(const nsAString & aNodeValue,
149 nsSchemaDateTime *aResult)
151 PRBool isValid = PR_FALSE;
153 nsAutoString datetimeString(aNodeValue);
155 aResult->date.isNegative = (datetimeString.First() == '-');
158 http://www.w3.org/TR/xmlschema-2/#dateTime
159 (-)CCYY-MM-DDThh:mm:ss(.sss...)
160 then either: Z
161 or [+/-]hh:mm
164 // first handle the date part
165 // search for 'T'
167 LOG((" Validating DateTime:"));
169 int findString = datetimeString.FindChar('T');
171 // if no T, invalid
172 if (findString >= 0) {
173 // we get the date part (from 0 to before 'T')
174 isValid = ParseSchemaDate(Substring(aNodeValue, 0, findString), PR_FALSE, &aResult->date);
176 if (isValid) {
177 // we get the time part (from after the 'T' till the end)
178 isValid = ParseSchemaTime(
179 Substring(aNodeValue, findString + 1, aNodeValue.Length()),
180 &aResult->time);
184 return isValid;
187 PRBool
188 nsSchemaValidatorUtils::ParseSchemaDate(const nsAString & aStrValue,
189 PRBool aAllowTimeZone,
190 nsSchemaDate *aDate)
192 PRBool isValid = PR_FALSE;
195 http://www.w3.org/TR/xmlschema-2/#date
196 (-)CCYY-MM-DD
197 then optionally: Z
198 or [+/-]hh:mm
201 const PRUnichar *start, *end, *buffStart;
202 aStrValue.BeginReading(&start, &end);
203 aStrValue.BeginReading(&buffStart);
204 PRUint32 state = 0;
205 PRUint32 buffLength = 0;
206 PRBool done = PR_FALSE;
207 PRUnichar currentChar;
209 nsAutoString year;
210 char month[3] = "";
211 char day[3] = "";
212 char timezoneHour[3] = "";
213 char timezoneMinute[3] = "";
215 // if year is negative, skip it
216 if (aStrValue.First() == '-') {
217 start++;
218 buffStart = start;
221 while ((start != end) && !done) {
222 currentChar = *start++;
223 switch (state) {
224 case 0: {
225 // year
226 if (currentChar == '-') {
227 if (buffLength < 4) {
228 done = PR_TRUE;
229 } else {
230 year.Assign(Substring(buffStart, --start));
231 state = 1;
232 buffLength = 0;
233 buffStart = ++start;
235 } else {
236 // has to be a numerical character or else abort
237 if ((currentChar > '9') || (currentChar < '0'))
238 done = PR_TRUE;
239 buffLength++;
241 break;
244 case 1: {
245 // month
246 if (buffLength > 2) {
247 done = PR_TRUE;
248 } else if (currentChar == '-') {
249 if (strcmp(month, "12") == 1 || buffLength < 2) {
250 done = PR_TRUE;
251 } else {
252 state = 2;
253 buffLength = 0;
254 buffStart = start;
256 } else {
257 // has to be a numerical character or else abort
258 if ((currentChar > '9') || (currentChar < '0'))
259 done = PR_TRUE;
260 else
261 month[buffLength] = currentChar;
262 buffLength++;
264 break;
267 case 2: {
268 // day
269 if (buffLength > 2) {
270 done = PR_TRUE;
271 } else if (currentChar == 'Z') {
272 if (aAllowTimeZone) {
273 if ((start == end) && (buffLength == 2) && (strcmp(day, "31") < 1)) {
274 isValid = PR_TRUE;
278 done = PR_TRUE;
279 } else if ((currentChar == '+') || (currentChar == '-')) {
280 // timezone
281 if (aAllowTimeZone) {
282 state = 3;
283 buffLength = 0;
284 buffStart = start;
285 } else {
286 // no timezones allowed
287 done = PR_TRUE;
289 } else {
290 // has to be a numerical character or else abort
291 if ((currentChar > '9') || (currentChar < '0'))
292 done = PR_TRUE;
293 else {
294 day[buffLength] = currentChar;
296 buffLength++;
298 // are we at the end?
299 if (start == end && buffLength == 2) {
300 isValid = PR_TRUE;
301 done = PR_TRUE;
304 break;
307 case 3: {
308 // timezone hh:mm
309 if (end-buffStart == 5) {
310 isValid = ParseSchemaTimeZone(Substring(buffStart, end), timezoneHour,
311 timezoneMinute);
314 done = PR_TRUE;
315 break;
320 if (isValid) {
321 char * pEnd;
323 PRUint32 yearval = strtoul(NS_ConvertUTF16toUTF8(year).get(), &pEnd, 10);
324 if (yearval == 0 || yearval == ULONG_MAX) {
325 isValid = PR_FALSE;
326 } else {
327 PRUint8 monthval = strtol(month, &pEnd, 10);
328 if (monthval < 1 || monthval > 12) {
329 isValid = PR_FALSE;
330 } else {
331 PRUint8 dayval = strtol(day, &pEnd, 10);
332 if (dayval < 1) {
333 isValid = PR_FALSE;
334 } else {
335 // check for leap years
336 PRUint8 maxDay = GetMaximumDayInMonthFor(yearval, monthval);
337 if (maxDay >= dayval) {
338 aDate->year = yearval;
340 // month/day are validated in the parsing code above
341 aDate->month = monthval;
342 aDate->day = dayval;
343 } else {
344 isValid = PR_FALSE;
351 LOG((" Date is %s", ((isValid) ? "Valid" : "Not Valid")));
353 return isValid;
356 // parses a string as a schema time type and returns the parsed
357 // hour/minute/second/fraction seconds as well as if its a valid
358 // schema time type.
359 PRBool
360 nsSchemaValidatorUtils::ParseSchemaTime(const nsAString & aStrValue,
361 nsSchemaTime* aTime)
363 PRBool isValid = PR_FALSE;
365 // time looks like this: HH:MM:SS(.[S]+)(+/-HH:MM)
367 char hour[3] = "";
368 char minute[3] = "";
369 char second[3] = "";
371 char timezoneHour[3] = "";
372 char timezoneMinute[3] = "";
373 // we store the fraction seconds because PR_ExplodeTime seems to skip them.
374 nsAutoString usec;
376 const PRUnichar *start, *end, *buffStart;
377 aStrValue.BeginReading(&start, &end);
378 aStrValue.BeginReading(&buffStart);
379 PRUint32 state = 0;
380 PRUint32 buffLength = 0;
381 PRBool done = PR_FALSE;
382 PRUnichar currentChar;
383 PRUnichar tzSign = PRUnichar(' ');
385 while ((start != end) && !done) {
386 currentChar = *start++;
388 switch (state) {
389 case 0: {
390 // hour
391 if (buffLength > 2) {
392 done = PR_TRUE;
393 } else if (currentChar == ':') {
394 // validate hour
395 if (strcmp(hour, "24") == 1) {
396 done = PR_TRUE;
397 } else {
398 state = 1;
399 buffLength = 0;
400 buffStart = start;
402 } else {
403 // has to be a numerical character or else abort
404 if ((currentChar > '9') || (currentChar < '0'))
405 done = PR_TRUE;
406 else
407 hour[buffLength] = currentChar;
408 buffLength++;
410 break;
413 case 1: {
414 // minute
415 if (buffLength > 2) {
416 done = PR_TRUE;
417 } else if (currentChar == ':') {
418 // validate minute
419 if (strcmp(minute, "59") == 1) {
420 done = PR_TRUE;
421 } else {
422 state = 2;
423 buffLength = 0;
424 buffStart = start;
426 } else {
427 // has to be a numerical character or else abort
428 if ((currentChar > '9') || (currentChar < '0'))
429 done = PR_TRUE;
430 else
431 minute[buffLength] = currentChar;
432 buffLength++;
434 break;
437 case 2: {
438 // seconds
439 if (buffLength > 2) {
440 done = PR_TRUE;
441 } else if (currentChar == 'Z') {
442 // if its Z, has to be the last character
443 if ((start == end) && (strcmp(second, "59") != 1)) {
444 isValid = PR_TRUE;
446 done = PR_TRUE;
447 tzSign = currentChar;
448 } else if ((currentChar == '+') || (currentChar == '-')) {
449 // timezone exists
450 if (strcmp(second, "59") == 1) {
451 done = PR_TRUE;
452 } else {
453 state = 4;
454 buffLength = 0;
455 buffStart = start;
456 tzSign = currentChar;
458 } else if (currentChar == '.') {
459 // fractional seconds exist
460 if (strcmp(second, "59") == 1) {
461 done = PR_TRUE;
462 } else {
463 state = 3;
464 buffLength = 0;
465 buffStart = start;
467 } else {
468 // has to be a numerical character or else abort
469 if ((currentChar > '9') || (currentChar < '0'))
470 done = PR_TRUE;
471 else {
472 second[buffLength] = currentChar;
473 if (start == end) {
474 isValid = PR_TRUE;
475 done = PR_TRUE;
478 buffLength++;
481 break;
484 case 3: {
485 // fractional seconds
487 if (currentChar == 'Z') {
488 // if its Z, has to be the last character
489 if (start == end)
490 isValid = PR_TRUE;
491 else
492 done = PR_TRUE;
493 tzSign = currentChar;
494 usec.Assign(Substring(buffStart, start - 1));
495 } else if ((currentChar == '+') || (currentChar == '-')) {
496 // timezone exists
497 usec.Assign(Substring(buffStart, start - 1));
498 state = 4;
499 buffLength = 0;
500 buffStart = start;
501 tzSign = currentChar;
502 } else {
503 // has to be a numerical character or else abort
504 if ((currentChar > '9') || (currentChar < '0'))
505 done = PR_TRUE;
506 else if (start == end) {
507 usec.Assign(Substring(buffStart, end));
508 isValid = PR_TRUE;
509 done = PR_TRUE;
511 buffLength++;
513 break;
516 case 4: {
517 // timezone hh:mm
518 if (end-buffStart == 5)
519 isValid = ParseSchemaTimeZone(Substring(buffStart, end), timezoneHour,
520 timezoneMinute);
522 done = PR_TRUE;
523 break;
528 if (isValid) {
529 char * pEnd;
531 PRUint32 usecval = strtoul(NS_ConvertUTF16toUTF8(usec).get(), &pEnd, 10);
532 // be carefull, empty usec returns 0
533 if (!usec.IsEmpty() && (usecval == 0 || usecval == ULONG_MAX)) {
534 isValid = PR_FALSE;
535 } else {
536 aTime->hour = strtol(hour, &pEnd, 10);
537 aTime->minute = strtol(minute, &pEnd, 10);
538 aTime->second = strtol(second, &pEnd, 10);
539 aTime->millisecond = usecval;
541 if (tzSign == '+')
542 aTime->tzIsNegative = PR_FALSE;
543 else
544 aTime->tzIsNegative = PR_TRUE;
546 aTime->tzhour = strtol(timezoneHour, &pEnd, 10);
547 aTime->tzminute = strtol(timezoneMinute, &pEnd, 10);
551 LOG((" Time is %s", ((isValid) ? "Valid" : "Not Valid")));
553 return isValid;
556 PRBool
557 nsSchemaValidatorUtils::ParseSchemaTimeZone(const nsAString & aStrValue,
558 char *rv_tzhour, char *rv_tzminute)
560 PRBool isValid = PR_FALSE;
561 char timezoneHour[3] = "";
562 char timezoneMinute[3] = "";
564 const PRUnichar *start, *end, *buffStart;
565 aStrValue.BeginReading(&start, &end);
566 aStrValue.BeginReading(&buffStart);
567 PRUint32 state = 0;
568 PRUint32 buffLength = 0;
569 PRBool done = PR_FALSE;
570 PRUnichar currentChar;
572 LOG(("\n Validating TimeZone"));
574 while ((start != end) && !done) {
575 currentChar = *start++;
577 switch (state) {
578 case 0: {
579 // hour
580 if (buffLength > 2) {
581 done = PR_TRUE;
582 } else if (currentChar == ':') {
583 timezoneHour[2] = '\0';
584 if (strcmp(timezoneHour, "24") == 1) {
585 done = PR_TRUE;
586 } else {
587 state = 1;
588 buffLength = 0;
589 buffStart = start;
591 } else {
592 // has to be a numerical character or else abort
593 if ((currentChar > '9') || (currentChar < '0'))
594 done = PR_TRUE;
595 else {
596 timezoneHour[buffLength] = currentChar;
598 buffLength++;
600 break;
603 case 1: {
604 // minute
605 if (buffLength > 2) {
606 done = PR_TRUE;
607 } else if (start == end) {
608 if (buffLength == 1) {
609 if ((currentChar > '9') || (currentChar < '0')) {
610 done = PR_TRUE;
611 } else {
612 timezoneMinute[buffLength] = currentChar;
614 timezoneMinute[2] = '\0';
615 if (strcmp(timezoneMinute, "59") == 1) {
616 done = PR_TRUE;
617 } else {
618 isValid = PR_TRUE;
621 } else {
622 done = PR_FALSE;
624 } else {
625 // has to be a numerical character or else abort
626 if ((currentChar > '9') || (currentChar < '0')) {
627 done = PR_TRUE;
628 } else {
629 timezoneMinute[buffLength] = currentChar;
631 buffLength++;
633 break;
639 if (isValid) {
640 strncpy(rv_tzhour, timezoneHour, 3);
641 strncpy(rv_tzminute, timezoneMinute, 3);
644 return isValid;
648 -1 - aDateTime1 < aDateTime2
649 0 - equal
650 1 - aDateTime1 > aDateTime2
653 nsSchemaValidatorUtils::CompareDateTime(nsSchemaDateTime aDateTime1,
654 nsSchemaDateTime aDateTime2)
656 int result;
658 nsSchemaDateTime dateTime1, dateTime2;
659 AddTimeZoneToDateTime(aDateTime1, &dateTime1);
660 AddTimeZoneToDateTime(aDateTime2, &dateTime2);
662 if (!dateTime1.date.isNegative && dateTime2.date.isNegative) {
663 // positive year is always bigger than negative year
664 result = 1;
665 } else if (dateTime1.date.isNegative && !dateTime2.date.isNegative) {
666 result = -1;
667 } else {
668 result = CompareDate(dateTime1.date, dateTime2.date);
670 if (result == 0)
671 result = CompareTime(dateTime1.time, dateTime2.time);
673 if (dateTime1.date.isNegative && dateTime2.date.isNegative) {
674 // -20 is smaller than -21
675 if (result == -1)
676 result = 1;
677 else if (result == 1)
678 result = -1;
682 return result;
686 -1 - aDateTime1 < aDateTime2
687 0 - equal
688 1 - aDateTime1 > aDateTime2
691 nsSchemaValidatorUtils::CompareDate(nsSchemaDate aDate1, nsSchemaDate aDate2)
693 int result;
695 if (aDate1.year < aDate2.year) {
696 result = -1;
697 } else if (aDate1.year > aDate2.year) {
698 result = 1;
699 } else {
700 if (aDate1.month < aDate2.month) {
701 result = -1;
702 } else if (aDate1.month > aDate2.month) {
703 result = 1;
704 } else {
705 if (aDate1.day < aDate2.day) {
706 result = -1;
707 } else if (aDate1.day > aDate2.day) {
708 result = 1;
709 } else {
710 result = 0;
715 return result;
719 -1 - aDateTime1 < aDateTime2
720 0 - equal
721 1 - aDateTime1 > aDateTime2
724 nsSchemaValidatorUtils::CompareTime(nsSchemaTime aTime1, nsSchemaTime aTime2)
726 int result;
728 if (aTime1.hour < aTime2.hour) {
729 result = -1;
730 } else if (aTime1.hour > aTime2.hour) {
731 result = 1;
732 } else {
733 if (aTime1.minute < aTime2.minute) {
734 result = -1;
735 } else if (aTime1.minute > aTime2.minute) {
736 result = 1;
737 } else {
738 if (aTime1.second < aTime2.second) {
739 result = -1;
740 } else if (aTime1.second > aTime2.second) {
741 result = 1;
742 } else {
743 if (aTime1.millisecond < aTime2.millisecond) {
744 result = -1;
745 } else if (aTime1.millisecond > aTime2.millisecond) {
746 result = 1;
747 } else {
748 result = 0;
754 return result;
757 void
758 nsSchemaValidatorUtils::AddTimeZoneToDateTime(nsSchemaDateTime aDateTime,
759 nsSchemaDateTime* aDestDateTime)
761 // With timezones, you subtract the timezone difference. So for example,
762 // 2002-10-10T12:00:00+05:00 is 2002-10-10T07:00:00Z
763 PRUint32 year = aDateTime.date.year;
764 PRUint8 month = aDateTime.date.month;
765 PRUint8 day = aDateTime.date.day;
766 int hour = aDateTime.time.hour;
767 int minute = aDateTime.time.minute;
768 PRUint8 second = aDateTime.time.second;
769 PRUint32 millisecond = aDateTime.time.millisecond;
771 if (aDateTime.time.tzIsNegative) {
772 hour = hour + aDateTime.time.tzhour;
773 minute = minute + aDateTime.time.tzminute;
774 } else {
775 hour = hour - aDateTime.time.tzhour;
776 minute = minute - aDateTime.time.tzminute;
779 div_t divresult;
781 if (minute > 59) {
782 divresult = div(minute, 60);
783 hour += divresult.quot;
784 minute = divresult.rem;
785 } else if (minute < 0) {
786 minute = 60 + minute;
787 hour--;
790 // hour
791 if (hour == 24 && (minute > 0 || second > 0)) {
792 // can only be 24:0:0 - need to increment day
793 day++;
794 hour = 0;
795 } else if (hour > 23) {
796 divresult = div(hour, 24);
797 day += divresult.quot;
798 hour = divresult.rem;
799 } else if (hour < 0) {
800 hour = 24 + hour;
801 day--;
804 // day
805 if (day == 0) {
806 // if day is 0, go back a month and make sure we handle month 0 (ie back a year).
807 month--;
809 if (month == 0) {
810 month = 12;
811 year--;
814 day = GetMaximumDayInMonthFor(month, year);
815 } else {
816 int maxDay = GetMaximumDayInMonthFor(month, year);
817 while (day > maxDay) {
818 day -= maxDay;
819 month++;
821 // since we are a valid datetime, month has to be 12 before the ++, so will
822 // be 13
823 if (month == 13) {
824 month = 1;
825 year++;
828 maxDay = GetMaximumDayInMonthFor(month, year);
832 aDestDateTime->date.year = year;
833 aDestDateTime->date.month = month;
834 aDestDateTime->date.day = day;
835 aDestDateTime->date.isNegative = aDateTime.date.isNegative;
836 aDestDateTime->time.hour = hour;
837 aDestDateTime->time.minute = minute;
838 aDestDateTime->time.second = second;
839 aDestDateTime->time.millisecond = millisecond;
840 aDestDateTime->time.tzIsNegative = aDateTime.time.tzIsNegative;
843 void
844 nsSchemaValidatorUtils::GetMonthShorthand(PRUint8 aMonth, nsACString & aReturn)
846 aReturn.Assign(monthShortHand[aMonth - 1].shortHand);
850 -1 - aYearMonth1 < aYearMonth2
851 0 - equal
852 1 - aYearMonth1 > aYearMonth2
855 nsSchemaValidatorUtils::CompareGYearMonth(nsSchemaGYearMonth aYearMonth1,
856 nsSchemaGYearMonth aYearMonth2)
858 int rv;
860 if (aYearMonth1.gYear.year > aYearMonth2.gYear.year) {
861 rv = 1;
862 } else if (aYearMonth1.gYear.year < aYearMonth2.gYear.year) {
863 rv = -1;
864 } else {
865 // both have the same year
866 if (aYearMonth1.gMonth.month > aYearMonth2.gMonth.month)
867 rv = 1;
868 else if (aYearMonth1.gMonth.month < aYearMonth2.gMonth.month)
869 rv = -1;
870 else
871 rv = 0;
874 return rv;
878 -1 - aMonthDay1 < aMonthDay2
879 0 - equal
880 1 - aMonthDay1 > aMonthDay2
883 nsSchemaValidatorUtils::CompareGMonthDay(nsSchemaGMonthDay aMonthDay1,
884 nsSchemaGMonthDay aMonthDay2)
886 int rv;
888 if (aMonthDay1.gMonth.month > aMonthDay2.gMonth.month) {
889 rv = 1;
890 } else if (aMonthDay1.gMonth.month < aMonthDay2.gMonth.month) {
891 rv = -1;
892 } else {
893 // both have the same year
894 if (aMonthDay1.gDay.day > aMonthDay2.gDay.day)
895 rv = 1;
896 else if (aMonthDay1.gDay.day < aMonthDay2.gDay.day)
897 rv = -1;
898 else
899 rv = 0;
902 return rv;
905 PRBool
906 nsSchemaValidatorUtils::ParseSchemaDuration(const nsAString & aStrValue,
907 nsISchemaDuration **aDuration)
909 PRBool isValid = PR_FALSE;
911 const PRUnichar *start, *end, *buffStart;
912 aStrValue.BeginReading(&start, &end);
913 aStrValue.BeginReading(&buffStart);
914 PRUint32 state = 0;
915 PRUint32 buffLength = 0;
916 PRBool done = PR_FALSE;
917 PRUnichar currentChar;
919 PRBool isNegative = PR_FALSE;
921 // make sure leading P is present. Take negative durations into consideration.
922 if (*start == '-') {
923 ++start;
924 if (*start != 'P') {
925 return PR_FALSE;
926 } else {
927 isNegative = PR_TRUE;
928 buffStart = ++start;
930 } else {
931 if (*start != 'P')
932 return PR_FALSE;
933 else
934 ++start;
937 nsAutoString parseBuffer;
938 PRBool timeSeparatorFound = PR_FALSE;
940 // designators may not repeat, so keep track of those we find.
941 PRBool yearFound = PR_FALSE;
942 PRBool monthFound = PR_FALSE;
943 PRBool dayFound = PR_FALSE;
944 PRBool hourFound = PR_FALSE;
945 PRBool minuteFound = PR_FALSE;
946 PRBool secondFound = PR_FALSE;
947 PRBool fractionSecondFound = PR_FALSE;
949 PRUint32 year = 0;
950 PRUint32 month = 0;
951 PRUint32 day = 0;
952 PRUint32 hour = 0;
953 PRUint32 minute = 0;
954 PRUint32 second = 0;
955 double fractionSecond = 0;
957 /* durations look like this:
958 (-)PnYnMnDTnHnMn(.n)S
959 - P is required, plus sign is invalid
960 - order is important, so day after year is invalid (PnDnY)
961 - Y,M,D,H,M,S are called designators
962 - designators are not allowed without a number before them
963 - T is the date/time seperator and is only allowed given a time part
966 while ((start != end) && !done) {
967 currentChar = *start++;
968 // not a number - so it has to be a type designator (YMDTHMS)
969 // it can also be |.| for fractional seconds
970 if ((currentChar > '9') || (currentChar < '0')) {
971 // first check if the buffer is bigger than what long can store
972 // which is 11 digits, as we convert to long
973 if ((parseBuffer.Length() == 10) &&
974 (CompareStrings(parseBuffer, NS_LITERAL_STRING("2147483647")) == 1)) {
975 done = PR_TRUE;
976 } else if (currentChar == 'Y') {
977 if (yearFound || monthFound || dayFound || timeSeparatorFound) {
978 done = PR_TRUE;
979 } else {
980 state = 0;
981 yearFound = PR_TRUE;
983 } else if (currentChar == 'M') {
984 // M is used twice - Months and Minutes
985 if (!timeSeparatorFound)
986 if (monthFound || dayFound || timeSeparatorFound) {
987 done = PR_TRUE;
988 } else {
989 state = 1;
990 monthFound = PR_TRUE;
992 else {
993 if (!timeSeparatorFound) {
994 done = PR_TRUE;
995 } else {
996 if (minuteFound || secondFound) {
997 done = PR_TRUE;
998 } else {
999 state = 4;
1000 minuteFound = PR_TRUE;
1004 } else if (currentChar == 'D') {
1005 if (dayFound || timeSeparatorFound) {
1006 done = PR_TRUE;
1007 } else {
1008 state = 2;
1009 dayFound = PR_TRUE;
1011 } else if (currentChar == 'T') {
1012 // can't have the time seperator more than once
1013 if (timeSeparatorFound)
1014 done = PR_TRUE;
1015 else
1016 timeSeparatorFound = PR_TRUE;
1017 } else if (currentChar == 'H') {
1018 if (!timeSeparatorFound || hourFound || secondFound ) {
1019 done = PR_TRUE;
1020 } else {
1021 state = 3;
1022 hourFound = PR_TRUE;
1024 } else if (currentChar == 'S') {
1025 if (!timeSeparatorFound)
1026 done = PR_TRUE;
1027 else
1028 if (secondFound) {
1029 done = PR_TRUE;
1030 } else {
1031 state = 5;
1032 secondFound = PR_TRUE;
1034 } else if (currentChar == '.') {
1035 // fractional seconds
1036 if (fractionSecondFound) {
1037 done = PR_TRUE;
1038 } else {
1039 parseBuffer.Append(currentChar);
1040 buffLength++;
1041 fractionSecondFound = PR_TRUE;
1043 } else {
1044 done = PR_TRUE;
1047 // if its a designator and no buffer, invalid per spec. Rule doesn't apply
1048 // if T or '.' (fractional seconds), as we need to parse on for those.
1049 // so P200YM is invalid as there is no number before M
1050 if ((currentChar != 'T') && (currentChar != '.') && (parseBuffer.Length() == 0)) {
1051 done = PR_TRUE;
1052 } else if ((currentChar == 'T') && (start == end)) {
1053 // if 'T' is found but no time data after it, invalid
1054 done = PR_TRUE;
1057 if (!done && (currentChar != 'T') && (currentChar != '.')) {
1058 long temp;
1059 switch (state) {
1060 case 0: {
1061 // years
1062 if (!IsValidSchemaInteger(parseBuffer, &temp, PR_TRUE))
1063 done = PR_TRUE;
1064 else
1065 year = temp;
1066 break;
1069 case 1: {
1070 // months
1071 if (!IsValidSchemaInteger(parseBuffer, &temp, PR_TRUE))
1072 done = PR_TRUE;
1073 else
1074 month = temp;
1075 break;
1078 case 2: {
1079 // days
1080 if (!IsValidSchemaInteger(parseBuffer, &temp, PR_TRUE))
1081 done = PR_TRUE;
1082 else
1083 day = temp;
1084 break;
1087 case 3: {
1088 // hours
1089 if (!IsValidSchemaInteger(parseBuffer, &temp, PR_TRUE))
1090 done = PR_TRUE;
1091 else
1092 hour = temp;
1093 break;
1096 case 4: {
1097 // minutes
1098 if (!IsValidSchemaInteger(parseBuffer, &temp, PR_TRUE))
1099 done = PR_TRUE;
1100 else
1101 minute = temp;
1102 break;
1105 case 5: {
1106 // seconds - we have to handle optional fraction seconds as well
1107 if (fractionSecondFound) {
1108 double temp2, intpart;
1110 if (!IsValidSchemaDouble(parseBuffer, &temp2)) {
1111 done = PR_TRUE;
1112 } else {
1113 fractionSecond = modf(temp2, &intpart);
1114 second = static_cast<PRUint32>(intpart);
1116 } else {
1117 if (!IsValidSchemaInteger(parseBuffer, &temp, PR_TRUE))
1118 done = PR_TRUE;
1119 else
1120 second = temp;
1122 break;
1127 // clear buffer unless we are at fraction seconds, since we want to parse
1128 // the seconds and fraction seconds into the same buffer.
1129 if (!fractionSecondFound) {
1130 parseBuffer.AssignLiteral("");
1131 buffLength = 0;
1133 } else {
1134 if (buffLength > 11) {
1135 done = PR_TRUE;
1136 } else {
1137 parseBuffer.Append(currentChar);
1138 buffLength++;
1143 if ((start == end) && (!done)) {
1144 isValid = PR_TRUE;
1147 if (isValid) {
1148 nsISchemaDuration* duration = new nsSchemaDuration(year, month, day, hour,
1149 minute, second,
1150 fractionSecond,
1151 isNegative);
1153 *aDuration = duration;
1154 NS_IF_ADDREF(*aDuration);
1157 return isValid;
1160 /* compares 2 strings that contain integers.
1161 Schema Integers have no limit, thus converting the strings
1162 into numbers won't work.
1164 -1 - aString1 < aString2
1165 0 - equal
1166 1 - aString1 > aString2
1170 nsSchemaValidatorUtils::CompareStrings(const nsAString & aString1,
1171 const nsAString & aString2)
1173 int rv;
1175 PRBool isNegative1 = (aString1.First() == PRUnichar('-'));
1176 PRBool isNegative2 = (aString2.First() == PRUnichar('-'));
1178 if (isNegative1 && !isNegative2) {
1179 // negative is always smaller than positive
1180 return -1;
1181 } else if (!isNegative1 && isNegative2) {
1182 // positive is always bigger than negative
1183 return 1;
1186 const PRUnichar *start1, *start2, *end1, *end2;
1187 aString1.BeginReading(&start1, &end1);
1188 aString2.BeginReading(&start2, &end2);
1190 // skip negative sign
1191 if (isNegative1)
1192 start1++;
1194 if (isNegative2)
1195 start2++;
1197 // jump over leading zeros
1198 PRBool done = PR_FALSE;
1199 while ((start1 != end1) && !done) {
1200 if (*start1 != '0')
1201 done = PR_TRUE;
1202 else
1203 ++start1;
1206 done = PR_FALSE;
1207 while ((start2 != end2) && !done) {
1208 if (*start2 != '0')
1209 done = PR_TRUE;
1210 else
1211 ++start2;
1214 nsAutoString compareString1, compareString2;
1215 compareString1.Assign(Substring(start1, end1));
1216 compareString2.Assign(Substring(start2, end2));
1218 // after removing leading 0s, check if they are the same
1219 if (compareString1.Equals(compareString2)) {
1220 return 0;
1223 if (compareString1.Length() > compareString2.Length())
1224 rv = 1;
1225 else if (compareString1.Length() < compareString2.Length())
1226 rv = -1;
1227 else
1228 rv = strcmp(NS_ConvertUTF16toUTF8(compareString1).get(),
1229 NS_ConvertUTF16toUTF8(compareString2).get());
1231 // 3>2, but -2>-3
1232 if (isNegative1 && isNegative2) {
1233 if (rv == 1)
1234 rv = -1;
1235 else
1236 rv = 1;
1238 return rv;
1241 // For xsd:duration support, the the maximum day for a month/year combo as
1242 // defined in http://w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes.
1244 nsSchemaValidatorUtils::GetMaximumDayInMonthFor(PRUint32 aYearValue, PRUint8 aMonthValue)
1246 PRUint8 maxDay = 28;
1247 PRUint8 month = ((aMonthValue - 1) % 12) + 1;
1248 PRUint32 year = aYearValue + ((aMonthValue - 1) / 12);
1251 Return Value Condition
1252 31 month is either 1, 3, 5, 7, 8, 10, 12
1253 30 month is either 4, 6, 9, 11
1254 29 month is 2 AND either ((year % 4 == 0) AND (year % 100 != 0))
1255 OR (year % 400 == 0)
1256 28 Otherwise
1259 if ((month == 1) || (month == 3) || (month == 5) || (month == 7) ||
1260 (month == 8) || (month == 10) || (month == 12))
1261 maxDay = 31;
1262 else if ((month == 4) || (month == 6) || (month == 9) || (month == 11))
1263 maxDay = 30;
1264 else if ((month == 2) && (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)))
1265 maxDay = 29;
1267 return maxDay;
1271 * compares 2 durations using the algorithm defined in
1272 * http://w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes by adding them to
1273 * the 4 datetimes specified in http://w3.org/TR/xmlschema-2/#duration-order.
1274 * If not all 4 result in x<y, we return indeterminate per
1275 * http://www.w3.org/TR/xmlschema-2/#facet-comparison-for-durations.
1277 * 0 - aDuration1 < aDuration2
1278 * 1 - indeterminate
1282 nsSchemaValidatorUtils::CompareDurations(nsISchemaDuration *aDuration1,
1283 nsISchemaDuration *aDuration2)
1285 int cmp = 0, tmpcmp, i = 0;
1287 nsSchemaDateTime dateTime, newDateTime1, newDateTime2;
1289 char* datetimeArray[] = { "1696-09-01T00:00:00Z", "1697-02-01T00:00:00Z",
1290 "1903-03-01T00:00:00Z", "1903-07-01T00:00:00Z" };
1291 PRBool indeterminate = PR_FALSE;
1293 while (!indeterminate && (i < 4)) {
1294 ParseDateTime(NS_ConvertASCIItoUTF16(datetimeArray[i]), &dateTime);
1296 AddDurationToDatetime(dateTime, aDuration1, &newDateTime1);
1297 AddDurationToDatetime(dateTime, aDuration2, &newDateTime2);
1299 tmpcmp = CompareDateTime(newDateTime1, newDateTime2);
1301 if (i > 0) {
1302 if (tmpcmp != cmp || tmpcmp > -1) {
1303 indeterminate = PR_TRUE;
1307 cmp = tmpcmp;
1308 ++i;
1311 return indeterminate ? 1 : 0;
1315 * This method implements the algorithm described at:
1316 * http://w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes
1318 void
1319 nsSchemaValidatorUtils::AddDurationToDatetime(nsSchemaDateTime aDatetime,
1320 nsISchemaDuration *aDuration,
1321 nsSchemaDateTime* aResultDateTime)
1323 // first handle months
1324 PRUint32 temp = 0;
1325 aDuration->GetMonths(&temp);
1326 temp += aDatetime.date.month;
1327 aResultDateTime->date.month = ((temp - 1) % 12) + 1;
1328 PRInt32 carry = (temp - 1) / 12;
1330 // years
1331 aDuration->GetYears(&temp);
1332 aResultDateTime->date.year = aDatetime.date.year + carry + temp;
1334 // reset the carry
1335 carry = 0;
1337 /* fraction seconds
1338 * XXX: Since the 4 datetimes we add durations to don't have fraction seconds
1339 * we can just add the duration's fraction second (stored as an float),
1340 * which will be < 1.0.
1342 double dblValue;
1343 aDuration->GetFractionSeconds(&dblValue);
1344 aResultDateTime->time.millisecond = (int) dblValue * 1000000;
1346 // seconds
1347 aDuration->GetSeconds(&temp);
1348 temp += aDatetime.time.second + carry;
1349 aResultDateTime->time.second = temp % 60;
1350 carry = temp / 60;
1352 // minutes
1353 aDuration->GetMinutes(&temp);
1354 temp += aDatetime.time.minute + carry;
1355 aResultDateTime->time.minute = temp % 60;
1356 carry = temp / 60;
1358 // hours
1359 aDuration->GetHours(&temp);
1360 temp += aDatetime.time.hour + carry;
1361 aResultDateTime->time.hour = temp % 24;
1362 carry = temp / 24;
1364 // days
1365 int maxDay = GetMaximumDayInMonthFor(aResultDateTime->date.year,
1366 aResultDateTime->date.month);
1367 int tempDays = 0;
1368 if (aDatetime.date.day > maxDay)
1369 tempDays = maxDay;
1370 else if (aDatetime.date.day < 1)
1371 tempDays = 1;
1372 else
1373 tempDays = aDatetime.date.day;
1375 aDuration->GetDays(&temp);
1376 aResultDateTime->date.day = tempDays + carry + temp;
1378 PRBool done = PR_FALSE;
1379 while (!done) {
1380 maxDay = GetMaximumDayInMonthFor(aResultDateTime->date.year,
1381 aResultDateTime->date.month);
1382 if (aResultDateTime->date.day < 1) {
1383 aResultDateTime->date.day +=
1384 GetMaximumDayInMonthFor(aResultDateTime->date.year,
1385 aResultDateTime->date.month - 1);
1386 carry = -1;
1387 } else if (aResultDateTime->date.day > maxDay) {
1388 aResultDateTime->date.day -= maxDay;
1389 carry = 1;
1390 } else {
1391 done = PR_TRUE;
1394 if (!done) {
1395 temp = aResultDateTime->date.month + carry;
1396 aResultDateTime->date.month = ((temp - 1) % 12) + 1;
1397 aResultDateTime->date.year += (temp - 1) / 12;
1401 // copy over negative and tz data
1402 aResultDateTime->date.isNegative = aDatetime.date.isNegative;
1404 aResultDateTime->time.tzIsNegative = aDatetime.time.tzIsNegative;
1405 aResultDateTime->time.tzhour = aDatetime.time.tzhour;
1406 aResultDateTime->time.tzminute = aDatetime.time.tzminute;
1408 LOG(("\n New datetime is %d-%d-%d %d:%d:%d\n", aResultDateTime->date.day,
1409 aResultDateTime->date.month, aResultDateTime->date.year,
1410 aResultDateTime->time.hour, aResultDateTime->time.minute,
1411 aResultDateTime->time.second));
1414 // http://www.w3.org/TR/xmlschema-2/#normalizedString
1415 PRBool
1416 nsSchemaValidatorUtils::IsValidSchemaNormalizedString(const nsAString &aStrValue)
1418 PRBool isValid = PR_FALSE;
1419 nsAutoString string(aStrValue);
1421 // may not contain carriage return, line feed nor tab characters
1422 if (FindCharInSet(string, "\t\r\n") == kNotFound)
1423 isValid = PR_TRUE;
1424 return isValid;
1427 // http://www.w3.org/TR/xmlschema-2/#token
1428 PRBool
1429 nsSchemaValidatorUtils::IsValidSchemaToken(const nsAString &aStrValue)
1431 PRBool isValid = PR_FALSE;
1432 nsAutoString string(aStrValue);
1434 // may not contain carriage return, line feed, tab characters. Also can
1435 // not contain leading/trailing whitespace and no internal sequences of
1436 // two or more spaces.
1437 if ((FindCharInSet(string, "\t\r\n") == kNotFound) &&
1438 (string.Find(NS_LITERAL_STRING(" ")) == kNotFound) &&
1439 (string.First() != ' ') &&
1440 (string.CharAt(string.Length() - 1) != ' '))
1441 isValid = PR_TRUE;
1443 return isValid;
1446 // http://www.w3.org/TR/xmlschema-2/#language
1447 PRBool
1448 nsSchemaValidatorUtils::IsValidSchemaLanguage(const nsAString &aStrValue)
1450 PRBool isValid = PR_FALSE;
1452 // pattern is defined in spec
1453 nsAutoString pattern;
1454 pattern.AssignLiteral("[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*");
1456 nsCOMPtr<nsISchemaValidatorRegexp> regexp = do_GetService(kREGEXP_CID);
1457 nsresult rv = regexp->RunRegexp(aStrValue, pattern, "g", &isValid);
1458 NS_ENSURE_SUCCESS(rv, rv);
1460 return isValid;
1463 // http://www.w3.org/TR/xmlschema-2/#name
1464 PRBool
1465 nsSchemaValidatorUtils::IsValidSchemaName(const nsAString &aStrValue)
1467 PRBool isValid = PR_FALSE;
1469 // xsd:Name is restriction on xsd:token
1470 if (IsValidSchemaToken(aStrValue)) {
1471 /* http://www.w3.org/TR/2000/WD-xml-2e-20000814
1472 [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
1473 CombiningChar | Extender
1474 [5] Name ::= (Letter | '_' | ':') ( NameChar)*
1476 // XXX Need to handling CombiningChar and Extender as well
1477 // XXX Additional Unicode testing needed?
1478 nsAutoString pattern;
1479 pattern.AssignLiteral("^[a-zA-Z_:][\\w\\.\\-:]*$");
1480 nsCOMPtr<nsISchemaValidatorRegexp> regexp = do_GetService(kREGEXP_CID);
1481 nsresult rv = regexp->RunRegexp(aStrValue, pattern, "g", &isValid);
1482 NS_ENSURE_SUCCESS(rv, rv);
1485 return isValid;
1488 // http://www.w3.org/TR/xmlschema-2/#ncname
1489 PRBool
1490 nsSchemaValidatorUtils::IsValidSchemaNCName(const nsAString &aStrValue)
1492 PRBool isValid = PR_FALSE;
1494 // xsd:NCNAME is a restriction on xsd:Name
1495 if (IsValidSchemaToken(aStrValue)) {
1496 /* http://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName
1497 NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
1498 CombiningChar | Extender
1499 NCName ::= (Letter | '_') (NCNameChar)*
1501 nsAutoString pattern;
1502 // XXX Need to handle Combining|Extender and Unicode Letters
1503 // xsd:Name minus the ":"
1504 pattern.AssignLiteral("^[a-zA-Z_][\\w\\.\\-]*$");
1505 nsCOMPtr<nsISchemaValidatorRegexp> regexp = do_GetService(kREGEXP_CID);
1506 nsresult rv = regexp->RunRegexp(aStrValue, pattern, "g", &isValid);
1507 NS_ENSURE_SUCCESS(rv, rv);
1510 return isValid;
1513 // http://www.w3.org/TR/xmlschema-2/#id
1514 PRBool
1515 nsSchemaValidatorUtils::IsValidSchemaID(const nsAString &aStrValue)
1517 PRBool isValid = PR_FALSE;
1519 // xsd:ID is a restriction of xsd:NCNAME
1520 if (IsValidSchemaNCName(aStrValue)) {
1521 isValid = PR_TRUE;
1522 // XXX Uniqueness tests per
1523 // http://www.w3.org/TR/2000/WD-xml-2e-20000814#NT-TokenizedType
1526 return isValid;
1529 // http://www.w3.org/TR/xmlschema-2/#idref
1530 PRBool
1531 nsSchemaValidatorUtils::IsValidSchemaIDRef(const nsAString &aStrValue)
1533 PRBool isValid = PR_FALSE;
1535 // xsd:IDREF is a restriction of xsd:NCName
1536 if (IsValidSchemaNCName(aStrValue)) {
1537 isValid = PR_TRUE;
1538 // XXX Ensure IDREF really references an ID,
1539 // http://www.w3.org/TR/2000/WD-xml-2e-20000814#idref
1542 return isValid;
1545 // http://www.w3.org/TR/xmlschema-2/#idrefs
1546 PRBool
1547 nsSchemaValidatorUtils::IsValidSchemaIDRefs(const nsAString &aStrValue)
1549 PRBool isValid = PR_FALSE;
1551 // Need to validate each IDREF
1552 const PRUnichar *iter, *end, *tokenStart;
1553 nsAutoString idref;
1554 aStrValue.BeginReading(&iter, &end);
1555 aStrValue.BeginReading(&tokenStart);
1556 while (iter != end) {
1557 for (;IsWhitespace(*iter) && iter != end; ++iter);
1558 tokenStart = iter;
1560 // Find end of token
1561 for (;!IsWhitespace(*iter) && iter != end; ++iter);
1563 // Get the token/idref and validate
1564 idref = Substring(tokenStart, iter);
1565 isValid = IsValidSchemaIDRef(idref);
1566 if (!isValid) break; // No need to continue
1568 if (iter != end) ++iter;
1571 return isValid;
1574 PRBool
1575 nsSchemaValidatorUtils::IsWhitespace(PRUnichar aChar)
1577 return aChar == ' ' || aChar == '\t' || aChar == '\n' ||
1578 aChar == '\r' || aChar == '\v';
1581 // http://www.w3.org/TR/xmlschema-2/#nmtoken
1582 PRBool
1583 nsSchemaValidatorUtils::IsValidSchemaNMToken(const nsAString &aStrValue)
1585 PRBool isValid = PR_FALSE;
1587 // xsd:NMTOKEN is a restriction on xsd:token
1588 if (IsValidSchemaToken(aStrValue)) {
1590 NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
1591 CombiningChar | Extender
1592 Nmtoken ::= (NameChar)+
1594 nsAutoString pattern;
1595 // XXX Need to handle Combining|Extender and possibly unicode letters
1596 pattern.AssignLiteral("^[\\w\\.\\-_:]*$");
1597 nsCOMPtr<nsISchemaValidatorRegexp> regexp = do_GetService(kREGEXP_CID);
1598 nsresult rv = regexp->RunRegexp(aStrValue, pattern, "g", &isValid);
1599 NS_ENSURE_SUCCESS(rv, rv);
1602 return isValid;
1605 // http://www.w3.org/TR/xmlschema-2/#nmtokens
1606 PRBool
1607 nsSchemaValidatorUtils::IsValidSchemaNMTokens(const nsAString &aStrValue)
1609 PRBool isValid = PR_FALSE;
1611 // Need to validate each NNTOKEN
1612 const PRUnichar *iter, *end, *tokenStart;
1613 nsAutoString idref;
1614 aStrValue.BeginReading(&iter, &end);
1615 aStrValue.BeginReading(&tokenStart);
1616 while (iter != end) {
1617 for (;IsWhitespace(*iter) && iter != end; ++iter);
1618 tokenStart = iter;
1620 // Find end of token
1621 for (;!IsWhitespace(*iter) && iter != end; ++iter);
1623 // Get the token/idref and validate
1624 idref = Substring(tokenStart, iter);
1625 isValid = IsValidSchemaNMToken(idref);
1626 if (!isValid) break; // No need to continue
1628 if (iter != end) ++iter;
1631 return isValid;
1634 PRBool
1635 nsSchemaValidatorUtils::HandleEnumeration(const nsAString &aStrValue,
1636 const nsStringArray &aEnumerationList)
1638 PRBool isValid = PR_FALSE;
1640 // check enumeration
1641 PRInt32 count = aEnumerationList.Count();
1642 for (PRInt32 i = 0; i < count; ++i) {
1643 if (aEnumerationList[i]->Equals(aStrValue)) {
1644 isValid = PR_TRUE;
1645 LOG((" Valid: Value matched enumeration #%d", i));
1646 break;
1650 if (!isValid) {
1651 LOG((" Not valid: Value doesn't match any of the enumerations"));
1654 return isValid;
1657 void
1658 nsSchemaValidatorUtils::RemoveLeadingZeros(nsAString & aString)
1660 const PRUnichar *start, *end;
1661 aString.BeginReading(&start, &end);
1663 PRBool done = PR_FALSE;
1664 PRUint32 count = 0, indexstart = 0;
1666 if (*start == '+' || *start == '-') {
1667 start++;
1668 indexstart = 1;
1671 while ((start != end) && !done)
1673 if (*start++ == '0') {
1674 ++count;
1675 } else {
1676 done = PR_TRUE;
1680 PRUint32 length = aString.Length() - indexstart;
1682 // if the entire string is composed of zeros, set it to one zero
1683 if (length == count) {
1684 aString.AssignLiteral("0");
1685 } else {
1686 // finally, remove the leading zeros
1687 aString.Cut(indexstart, count);
1691 void
1692 nsSchemaValidatorUtils::RemoveTrailingZeros(nsAString & aString)
1694 const PRUnichar *start, *end;
1695 aString.BeginReading(&start, &end);
1697 PRUint32 length = aString.Length();
1699 PRBool done = PR_FALSE;
1700 PRUint32 count = 0;
1701 if(start != end)
1702 end --;
1704 while ((start != end) && !done)
1706 if (*end-- == '0') {
1707 ++count;
1708 } else {
1709 done = PR_TRUE;
1713 // finally, remove the trailing zeros
1714 aString.Cut(length - count, count);
1717 // Walks the inheritance tree until it finds a type that isn't a restriction
1718 // type. While it finds restriction types, it collects restriction facets and
1719 // places them into the nsSchemaDerivedSimpleType. Once a facet has been found,
1720 // it makes sure that it won't be overwritten by the same facet defined in one
1721 // of the inherited types.
1722 nsresult
1723 nsSchemaValidatorUtils::GetDerivedSimpleType(nsISVSchemaSimpleType *aSimpleType,
1724 nsSchemaDerivedSimpleType *aDerived)
1726 PRBool done = PR_FALSE, hasEnumerations = PR_FALSE;
1727 nsCOMPtr<nsISVSchemaSimpleType> simpleType(aSimpleType);
1728 PRUint16 simpleTypeValue;
1729 PRUint32 facetCount;
1731 nsAutoString enumeration;
1732 nsresult rv = NS_OK;
1734 while(simpleType && !done) {
1735 // get the type of the simpletype
1736 rv = simpleType->GetSimpleType(&simpleTypeValue);
1737 NS_ENSURE_SUCCESS(rv, rv);
1739 switch (simpleTypeValue) {
1740 case nsISVSchemaSimpleType::SIMPLE_TYPE_RESTRICTION: {
1741 // handle the facets
1743 nsCOMPtr<nsISVSchemaRestrictionType> restrictionType =
1744 do_QueryInterface(simpleType);
1746 nsCOMPtr<nsISVSchemaFacet> facet;
1747 PRUint32 facetCounter;
1748 PRUint16 facetType;
1750 // get the amount of restriction facet defined.
1751 rv = restrictionType->GetFacetCount(&facetCount);
1752 NS_ENSURE_SUCCESS(rv, rv);
1753 LOG((" %d facet(s) defined.", facetCount));
1755 // if we had enumerations, we may not add new ones, since we are
1756 // being restricted. So if x restricts y, x defines the possible
1757 // enumerations and any enumerations on y are skipped
1758 hasEnumerations = (aDerived->enumerationList.Count() > 0);
1760 for (facetCounter = 0; facetCounter < facetCount; ++facetCounter) {
1761 rv = restrictionType->GetFacet(facetCounter, getter_AddRefs(facet));
1762 NS_ENSURE_SUCCESS(rv, rv);
1763 facet->GetFacetType(&facetType);
1765 switch (facetType) {
1766 case nsISVSchemaFacet::FACET_TYPE_LENGTH: {
1767 nsSchemaIntFacet *length = &aDerived->length;
1768 if (!length->isDefined) {
1769 length->isDefined = PR_TRUE;
1770 facet->GetLengthValue(&length->value);
1771 LOG((" - Length Facet found (value is %d)",
1772 length->value));
1774 break;
1777 case nsISVSchemaFacet::FACET_TYPE_MINLENGTH: {
1778 nsSchemaIntFacet *minLength = &aDerived->minLength;
1779 if (!minLength->isDefined) {
1780 minLength->isDefined = PR_TRUE;
1781 facet->GetLengthValue(&minLength->value);
1782 LOG((" - Min Length Facet found (value is %d)",
1783 minLength->value));
1785 break;
1788 case nsISVSchemaFacet::FACET_TYPE_MAXLENGTH: {
1789 nsSchemaIntFacet *maxLength = &aDerived->maxLength;
1790 if (!maxLength->isDefined) {
1791 maxLength->isDefined = PR_TRUE;
1792 facet->GetLengthValue(&maxLength->value);
1793 LOG((" - Max Length Facet found (value is %d)",
1794 maxLength->value));
1796 break;
1799 case nsISVSchemaFacet::FACET_TYPE_PATTERN: {
1800 nsSchemaStringFacet *pattern = &aDerived->pattern;
1801 if (!pattern->isDefined) {
1802 pattern->isDefined = PR_TRUE;
1803 facet->GetValue(pattern->value);
1804 LOG((" - Pattern Facet found (value is %s)",
1805 NS_ConvertUTF16toUTF8(pattern->value).get()));
1807 break;
1810 case nsISVSchemaFacet::FACET_TYPE_ENUMERATION: {
1811 if (!hasEnumerations) {
1812 facet->GetValue(enumeration);
1813 aDerived->enumerationList.AppendString(enumeration);
1814 LOG((" - Enumeration found (%s)",
1815 NS_ConvertUTF16toUTF8(enumeration).get()));
1817 break;
1820 case nsISVSchemaFacet::FACET_TYPE_WHITESPACE: {
1821 if (!aDerived->isWhitespaceDefined)
1822 facet->GetWhitespaceValue(&aDerived->whitespace);
1823 break;
1826 case nsISVSchemaFacet::FACET_TYPE_MAXINCLUSIVE: {
1827 nsSchemaStringFacet *maxInclusive = &aDerived->maxInclusive;
1828 if (!maxInclusive->isDefined) {
1829 maxInclusive->isDefined = PR_TRUE;
1830 facet->GetValue(maxInclusive->value);
1831 LOG((" - Max Inclusive Facet found (value is %s)",
1832 NS_ConvertUTF16toUTF8(maxInclusive->value).get()));
1834 break;
1837 case nsISVSchemaFacet::FACET_TYPE_MININCLUSIVE: {
1838 nsSchemaStringFacet *minInclusive = &aDerived->minInclusive;
1839 if (!minInclusive->isDefined) {
1840 minInclusive->isDefined = PR_TRUE;
1841 facet->GetValue(minInclusive->value);
1842 LOG((" - Min Inclusive Facet found (value is %s)",
1843 NS_ConvertUTF16toUTF8(minInclusive->value).get()));
1845 break;
1848 case nsISVSchemaFacet::FACET_TYPE_MAXEXCLUSIVE: {
1849 nsSchemaStringFacet *maxExclusive = &aDerived->maxExclusive;
1850 if (!maxExclusive->isDefined) {
1851 maxExclusive->isDefined = PR_TRUE;
1852 facet->GetValue(aDerived->maxExclusive.value);
1853 LOG((" - Max Exclusive Facet found (value is %s)",
1854 NS_ConvertUTF16toUTF8(maxExclusive->value).get()));
1856 break;
1859 case nsISVSchemaFacet::FACET_TYPE_MINEXCLUSIVE: {
1860 nsSchemaStringFacet *minExclusive = &aDerived->minExclusive;
1861 if (!minExclusive->isDefined) {
1862 minExclusive->isDefined = PR_TRUE;
1863 facet->GetValue(minExclusive->value);
1864 LOG((" - Min Exclusive Facet found (value is %s)",
1865 NS_ConvertUTF16toUTF8(minExclusive->value).get()));
1867 break;
1870 case nsISVSchemaFacet::FACET_TYPE_TOTALDIGITS: {
1871 nsSchemaIntFacet *totalDigits = &aDerived->totalDigits;
1872 if (!totalDigits->isDefined) {
1873 totalDigits->isDefined = PR_TRUE;
1874 facet->GetDigitsValue(&totalDigits->value);
1875 LOG((" - Totaldigits Facet found (value is %d)",
1876 totalDigits->value));
1878 break;
1881 case nsISVSchemaFacet::FACET_TYPE_FRACTIONDIGITS: {
1882 nsSchemaIntFacet *fractionDigits = &aDerived->fractionDigits;
1883 if (!fractionDigits->isDefined) {
1884 fractionDigits->isDefined = PR_TRUE;
1885 facet->GetDigitsValue(&fractionDigits->value);
1886 LOG((" - FractionDigits Facet found (value is %d)",
1887 fractionDigits->value));
1889 break;
1894 // get base type
1895 nsresult rv = restrictionType->GetBaseType(getter_AddRefs(simpleType));
1896 NS_ENSURE_SUCCESS(rv, rv);
1897 break;
1900 case nsISVSchemaSimpleType::SIMPLE_TYPE_BUILTIN: {
1901 // we are done
1902 aDerived->mBaseType = simpleType;
1903 done = PR_TRUE;
1904 break;
1907 case nsISVSchemaSimpleType::SIMPLE_TYPE_LIST: {
1908 // set as base type
1909 aDerived->mBaseType = simpleType;
1910 done = PR_TRUE;
1911 break;
1914 case nsISVSchemaSimpleType::SIMPLE_TYPE_UNION: {
1915 // set as base type
1916 aDerived->mBaseType = simpleType;
1917 done = PR_TRUE;
1918 break;
1923 return rv;
1926 // copies the data from aDerivedSrc to aDerivedDest
1927 void
1928 nsSchemaValidatorUtils::CopyDerivedSimpleType(nsSchemaDerivedSimpleType *aDerivedDest,
1929 nsSchemaDerivedSimpleType *aDerivedSrc)
1931 aDerivedDest->mBaseType = aDerivedSrc->mBaseType;
1933 aDerivedDest->length.value = aDerivedSrc->length.value;
1934 aDerivedDest->length.isDefined = aDerivedSrc->length.isDefined;
1935 aDerivedDest->minLength.value = aDerivedSrc->minLength.value;
1936 aDerivedDest->minLength.isDefined = aDerivedSrc->minLength.isDefined;
1937 aDerivedDest->maxLength.value = aDerivedSrc->maxLength.value;
1938 aDerivedDest->maxLength.isDefined = aDerivedSrc->maxLength.isDefined;
1940 aDerivedDest->pattern.value = aDerivedSrc->pattern.value;
1941 aDerivedDest->pattern.isDefined = aDerivedSrc->pattern.isDefined;
1943 aDerivedDest->isWhitespaceDefined = aDerivedSrc->isWhitespaceDefined;
1944 aDerivedDest->whitespace = aDerivedSrc->whitespace;
1946 aDerivedDest->maxInclusive.value = aDerivedSrc->maxInclusive.value;
1947 aDerivedDest->maxInclusive.isDefined = aDerivedSrc->maxInclusive.isDefined;
1948 aDerivedDest->minInclusive.value = aDerivedSrc->minInclusive.value;
1949 aDerivedDest->minInclusive.isDefined = aDerivedSrc->minInclusive.isDefined;
1950 aDerivedDest->maxExclusive.value = aDerivedSrc->maxExclusive.value;
1951 aDerivedDest->maxExclusive.isDefined = aDerivedSrc->maxExclusive.isDefined;
1952 aDerivedDest->minExclusive.value = aDerivedSrc->minExclusive.value;
1953 aDerivedDest->minExclusive.isDefined = aDerivedSrc->minExclusive.isDefined;
1955 aDerivedDest->totalDigits.value = aDerivedSrc->totalDigits.value;
1956 aDerivedDest->totalDigits.isDefined = aDerivedSrc->totalDigits.isDefined;
1957 aDerivedDest->fractionDigits.value = aDerivedSrc->fractionDigits.value;
1958 aDerivedDest->fractionDigits.isDefined = aDerivedSrc->fractionDigits.isDefined;
1960 aDerivedDest->enumerationList = aDerivedSrc->enumerationList;
1963 // sets aResultNode to aNode, making sure it points to null or a dom element
1964 void
1965 nsSchemaValidatorUtils::SetToNullOrElement(nsIDOMNode *aNode,
1966 nsIDOMNode **aResultNode)
1968 nsCOMPtr<nsIDOMNode> currentNode(aNode), tmpNode;
1970 if (currentNode) {
1971 PRUint16 nodeType;
1972 currentNode->GetNodeType(&nodeType);
1974 // if not an element node, skip
1975 while (currentNode && nodeType != nsIDOMNode::ELEMENT_NODE) {
1976 currentNode->GetNextSibling(getter_AddRefs(tmpNode));
1977 currentNode = tmpNode;
1978 if (currentNode)
1979 currentNode->GetNodeType(&nodeType);
1982 currentNode.swap(*aResultNode);
1987 PRInt32
1988 nsSchemaValidatorUtils::FindCharInSet(const nsAString & aString,
1989 const char *aSet, PRInt32 aOffset)
1991 if (aString.IsEmpty()) {
1992 return kNotFound;
1995 if (aOffset < 0) {
1996 aOffset = 0;
1997 } else if (aOffset > (PRInt32)aString.Length()) {
1998 return kNotFound;
2001 const PRUnichar *start, *end;
2002 aString.BeginReading(&start, &end);
2004 for (; start != end; ++start) {
2005 for (const char *temp = aSet; *temp; ++temp) {
2006 if (*start == PRUnichar(*temp)) {
2007 return (temp - aSet + aOffset) ;
2012 return kNotFound;