1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2006-2016. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21-module(xmerl_xsd_type). 22 23-export([check_simpleType/3,facet_fun/2,compare_floats/2, 24 replace_ws/2,collapse_ws/1]). 25 26-export([fQuotient/2,fQuotient/3,modulo/2,modulo/3,maximumDayInMonthFor/2, 27 add_duration2dateTime/2,duration_atoms/1,dateTime_atoms/1, 28 normalize_dateTime/1]). 29 30-export([compare_durations/2,compare_dateTime/2]). 31 32-include("xmerl.hrl"). 33-include("xmerl_internal.hrl"). 34-include("xmerl_xsd.hrl"). 35 36 37-define(catch_exit(_Call_,_Value_,_ErrorCause_), 38 case catch (_Call_) of 39 {'EXIT',_} -> 40 {error,{type,_ErrorCause_,_Value_}}; 41 {error,_} -> 42 {error,{_ErrorCause_,_Value_}}; 43 _ -> 44 {ok,_Value_} 45 end). 46 47-define(is_whitespace(__WS__), 48 __WS__==16#20; __WS__==16#9;__WS__==16#a; __WS__==16#d). 49 50check_simpleType(Name,Value,S) when is_list(Name) -> 51 ?debug("simpleType name a list: "++Name++"~n",[]), 52 check_simpleType(list_to_atom(Name),Value,S); 53check_simpleType(string,Value,_S) -> 54 case [X||X <- Value, 55 xmerl_lib:is_char(X)] of 56 Value -> 57 {ok,Value}; 58 _ -> 59 {error,{value_not_string,Value}} 60 end; 61check_simpleType(normalizedString,Value,_S) -> 62 case [X||X <- Value,xmerl_lib:is_char(X), 63 ns_whitespace(X)==false] of 64 Value -> 65 {ok,Value}; 66 _ -> 67 {error,{value_not_normalizedString,Value}} 68 end; 69check_simpleType(boolean,"true",_S) -> {ok,"true"}; 70check_simpleType(boolean,"false",_S) -> {ok,"false"}; 71check_simpleType(boolean,"1",_S) -> {ok,"1"}; 72check_simpleType(boolean,"0",_S) -> {ok,"0"}; 73check_simpleType(boolean,Other,_S) -> {error,{value_not_boolean,Other}}; 74check_simpleType(decimal,Value,_S) -> 75 ?catch_exit(check_decimal(Value),Value,invalid_decimal); 76check_simpleType(integer,Value,_S) -> 77 ?catch_exit(check_integer(Value),Value,invalid_integer); 78 79% float values: m * 2^e, where m is an integer whose absolute value is 80% less than 2^24, and e is an integer between -149 and 104, inclusive. 81check_simpleType(float,Value,_S) -> 82 ?catch_exit(check_float(Value),Value,invalid_float); 83% double values: m * 2^e, where m is an integer whose absolute value 84% is less than 2^53, and e is an integer between -1075 and 970, 85% inclusive. 86check_simpleType(double,Value,_S) -> 87 ?catch_exit(check_double(Value),Value,invalid_double); 88% extended format PnYnMnDTnHnMnS where n is an integer. The n value 89% before S may include decimal fraction. 90check_simpleType(duration,Value,_S) -> 91 ?catch_exit(check_duration(Value),Value,invalid_duration); 92check_simpleType(dateTime,Value,_S) -> 93 ?catch_exit(check_dateTime(Value),Value,invalid_dateTime); 94check_simpleType(time,Value,_S) -> 95 ?catch_exit(check_time(Value),Value,invalid_time); 96check_simpleType(date,Value,_S) -> 97 ?catch_exit(check_date(Value),Value,invalid_date); 98check_simpleType(gYearMonth,Value,_S) -> 99 ?catch_exit(check_gYearMonth(Value),Value,invalid_gYearMonth); 100check_simpleType(gYear,Value,_S) -> 101 ?catch_exit(check_gYear(Value),Value,invalid_gYear); 102check_simpleType(gMonthDay,Value,_S) -> 103 ?catch_exit(check_gMonthDay(Value),Value,invalid_gMonthDay); 104check_simpleType(gDay,Value,_S) -> 105 ?catch_exit(check_gDay(Value),Value,invalid_gDay); 106check_simpleType(gMonth,Value,_S) -> 107 ?catch_exit(check_gMonth(Value),Value,invalid_gMonth); 108check_simpleType(hexBinary,Value,_S) -> 109 IsEven = fun(X) -> 110 case X rem 2 of 111 0 -> true; 112 _ -> false 113 end 114 end, 115 IsHex = fun(X) when X >= $A, X =< $F -> true; 116 (X) when X >= $a, X =< $f -> true; 117 (X) when X >= $0, X =< $9 -> true; 118 (_) -> false 119 end, 120 case [X|| X<-Value, 121 IsEven(length(Value)), 122 IsHex(X)] of 123 Value -> 124 {ok,Value}; 125 _ -> {error,{value_not_hexBinary,Value}} 126 end; 127check_simpleType(base64Binary,Value,_S) -> 128 check_base64Binary(Value); 129check_simpleType(anyURI,Value,S) -> 130 case xmerl_uri:parse(Value) of 131 {error,_} -> 132 %% might be a relative uri, then it has to be a path in the context 133 case catch file:read_file_info(filename:join(S#xsd_state.xsd_base,Value)) of 134 {ok,_} -> 135 {ok,Value}; 136 _ -> 137 {error,{value_not_anyURI,Value}} 138 end; 139 _ -> 140 {ok,Value} 141 end; 142check_simpleType('QName',Value,_S) -> 143 case xmerl_lib:is_name(Value) of 144 true -> 145 {ok,Value}; 146 _ -> 147 {error,{value_not_QName,Value}} 148 end; 149check_simpleType('NOTATION',Value,_S) -> 150 {ok,Value}; %% Must provide for check of all QNames in schema. 151check_simpleType(token,Value,_S) -> 152 ?catch_exit(check_token(Value),Value,invalid_token); 153%% conform to the pattern [a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})* 154check_simpleType(language,Value,_S) -> 155 ?catch_exit(check_language(Value),Value,illegal_language); 156check_simpleType('NMTOKEN',Value,_S) -> 157 ?catch_exit(check_NMTOKEN(Value),Value,illegal_NMTOKEN); 158check_simpleType('NMTOKENS',Value,_S) -> 159 ?catch_exit(check_NMTOKENS(Value),Value,illegal_NMTOKENS); 160check_simpleType('Name',Value,_S) -> 161 ?catch_exit(check_Name(Value),Value,illegal_name); 162check_simpleType('NCName',Value,_S) -> 163 ?catch_exit(check_NCName(Value),Value,illegal_name); 164check_simpleType('ID',Value,_S) -> 165 ?catch_exit(check_ID(Value),Value,illegal_ID); 166check_simpleType('IDREF',Value,_S) -> 167 ?catch_exit(check_IDREF(Value),Value,illegal_IDREF); 168check_simpleType('IDREFS',Value,_S) -> 169 ?catch_exit(check_IDREFS(Value),Value,illegal_IDREFS); 170check_simpleType('ENTITY',Value,_S) -> 171 ?catch_exit(check_ENTITY(Value),Value,illegal_ENTITY); 172check_simpleType('ENTITIES',Value,_S) -> 173 ?catch_exit(check_ENTITIES(Value),Value,illegal_ENTITIES); 174check_simpleType(nonPositiveInteger,Value,_S) -> 175 ?catch_exit(check_nonPositiveInteger(Value),Value, 176 illegal_nonPositiveInteger); 177check_simpleType(negativeInteger,Value,_S) -> 178 ?catch_exit(check_negativeInteger(Value),Value, 179 illegal_negativeInteger); 180check_simpleType(long,Value,_S) -> 181 ?catch_exit(check_long(Value),Value,illegal_long); 182check_simpleType(int,Value,_S) -> 183 ?catch_exit(check_int(Value),Value,illegal_int); 184check_simpleType(short,Value,_S) -> 185 ?catch_exit(check_short(Value),Value,illegal_short); 186check_simpleType(byte,Value,_S) -> 187 ?catch_exit(check_byte(Value),Value,illegal_byte); 188check_simpleType(nonNegativeInteger,Value,_S) -> 189 ?catch_exit(check_nonNegativeInteger(Value),Value, 190 illegal_nonNegativeInteger); 191check_simpleType(unsignedLong,Value,_S) -> 192 ?catch_exit(check_unsignedLong(Value),Value,illegal_unsignedLong); 193check_simpleType(unsignedInt,Value,_S) -> 194 ?catch_exit(check_unsignedInt(Value),Value,illegal_unsignedInt); 195check_simpleType(unsignedShort,Value,_S) -> 196 ?catch_exit(check_unsignedShort(Value),Value,illegal_unsignedShort); 197check_simpleType(unsignedByte,Value,_S) -> 198 ?catch_exit(check_unsignedByte(Value),Value,illegal_unsignedByte); 199check_simpleType(positiveInteger,Value,_S) -> 200 ?catch_exit(check_positiveInteger(Value),Value,illegal_positiveInteger); 201check_simpleType(Unknown,Value,_S) -> 202 {error,{unknown_type,Unknown,Value}}. 203 204check_decimal(Value) -> 205 case string:tokens(Value,".") of 206 L when length(L) == 1; length(L) == 2 -> 207 _ = [list_to_integer(X)||X <- L], 208 {ok,Value}; 209 _ -> 210 {error,{value_not_decimal,Value}} 211 end. 212%% I=string:chr(Value,$.), 213%% {NumberDot,Decimal}=lists:split(I,Value), 214%% Number=string:strip(NumberDot,right,$.), 215%% case catch {list_to_integer(Number),list_to_integer(Decimal)} of 216%% {'EXIT',_} -> 217%% {error,{value_not_decimal,Value}}; 218%% _ -> {ok,Value} 219%% end. 220 221check_float(V="-INF") -> 222 {ok,V}; 223check_float(V="INF") -> 224 {ok,V}; 225check_float(V="NaN") -> 226 {ok,V}; 227check_float(Value) -> 228%% Pred = fun(X) when X==$e;X==$E -> false;(_) -> true end, 229%% {Mantissa,Exponent}=lists:splitwith(Pred,Value), 230%% SkipEe = fun([]) -> [];(L) -> tl(L) end, 231 case string:tokens(Value,"eE") of 232 [Mantissa,Exponent] -> 233 {ok,_} = check_decimal(Mantissa), 234 {ok,_} = check_integer(Exponent), 235 ok; 236 [Mantissa] -> 237 {ok,_} = check_decimal(Mantissa), 238 ok 239 end, 240 {ok,Value}. 241%% case {check_decimal(Mantissa), 242%% check_simpleType(integer,SkipEe(Exponent))} of 243%% {{ok,_},{ok,_}} -> 244%% {ok,Value}; 245%% _ -> 246%% {error,{value_not_float,Value}} 247%% end. 248 249check_double(Value) -> 250 check_float(Value). 251 252 253%% format PnYnMnDTnHnMnS 254%% P is always present 255%% T is absent iff all time items are absent 256%% At least one duration item must be present 257check_duration("-"++Value) -> 258 check_duration(Value); 259check_duration("P"++Value) -> 260 {Date,Time}=lists:splitwith(fun($T) -> false;(_) -> true end,Value), 261 {ok,_} = check_duration_date(Date,["Y","M","D"]), 262 {ok,_} = check_duration_time(Time,["T","H","M","S"]). 263 264check_duration_date("",_) -> 265 {ok,""}; 266check_duration_date(Date,[H|T]) -> 267 case string:tokens(Date,H) of 268 [Date] -> 269 check_duration_date(Date,T); 270 [DateItem] -> 271 {ok,_} = check_positive_integer(DateItem); 272 [DateItem,Rest] -> 273 {ok,_} = check_positive_integer(DateItem), 274 check_duration_date(Rest,T) 275 end. 276%% Time any combination of TnHnMfS 277%% n unsigned integers and f unsigned decimal 278%%check_duration_time(Time,["T","H","M","S"]) 279check_duration_time("",[_H|_T]) -> 280 {ok,""}; 281check_duration_time(Time,[S]) -> 282 [Sec] = string:tokens(Time,S), 283 {ok,_} = check_decimal(Sec); 284check_duration_time("T"++Time,TTokens) -> 285 [_H|_] = Time, 286 check_duration_time(Time,tl(TTokens)); 287check_duration_time(Time,[H|T]) -> 288 case string:tokens(Time,H) of 289 [Time] -> 290 check_duration_time(Time,T); 291 [TimeItem] -> 292 {ok,_} = check_positive_integer(TimeItem); 293 [TimeItem,Rest] -> 294 {ok,_} = check_positive_integer(TimeItem), 295 check_duration_time(Rest,T) 296 end. 297 298check_positive_integer(Value) -> 299 case catch list_to_integer(Value) of 300 Int when is_integer(Int),Int>=0 -> 301 {ok,Int}; 302 _ -> 303 {error,{value_not_integer,Value}} 304 end. 305 306 307%% check_integer and thereof derived types 308check_integer(Value) -> 309 {ok,list_to_integer(Value)}. 310 311check_nonPositiveInteger(Value) -> 312 check_constr_int(Value,undefined,0,illegal_nonPositiveInteger). 313 314check_negativeInteger(Value) -> 315 check_constr_int(Value,undefined,-1,illegal_negativeInteger). 316 317check_long(Value) -> 318 check_constr_int(Value,-9223372036854775808, 319 9223372036854775807,illegal_long). 320 321check_int(Value) -> 322 check_constr_int(Value,-2147483648,2147483647,illegal_int). 323 324check_short(Value) -> 325 check_constr_int(Value,-32768,32767,illegal_short). 326 327check_byte(Value) -> 328 check_constr_int(Value,-128,127,illegal_byte). 329 330check_nonNegativeInteger(Value) -> 331 check_constr_int(Value,0,undefined,illegal_nonNegativeInteger). 332 333check_unsignedLong(Value) -> 334 check_constr_int(Value,0,18446744073709551615,illegal_unsignedLong). 335 336check_unsignedInt(Value) -> 337 check_constr_int(Value,0,4294967295,illegal_unsignedInt). 338 339check_unsignedShort(Value) -> 340 check_constr_int(Value,0,65535,illegal_unsignedShort). 341 342check_unsignedByte(Value) -> 343 check_constr_int(Value,0,255,illegal_unsignedByte). 344 345check_positiveInteger(Value) -> 346 check_constr_int(Value,1,undefined,illegal_positiveInteger). 347 348check_constr_int(Value,undefined,Max,ErrMsg) -> 349 case check_integer(Value) of 350 {ok,Int} when Int =< Max -> 351 {ok,Int}; 352 _ -> 353 {error,{ErrMsg}} 354 end; 355check_constr_int(Value,Min,Max,ErrMsg) -> 356 case check_integer(Value) of 357 {ok,Int} when Int >= Min, Int =< Max -> 358 {ok,Int}; 359 _ -> 360 {error,{ErrMsg}} 361 end. 362 363%% DateTime on form: '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss 364%% ('.' s+)? (zzzzzz)? 365check_dateTime("-"++DateTime) -> 366 check_dateTime(DateTime); 367check_dateTime("+"++_DateTime) -> 368 {error,{invalid_dateTime,plus_sign}}; 369check_dateTime(DateTime) -> 370 [Date,Time] = string:tokens(DateTime,"T"), 371 [Y,M,D] = string:tokens(Date,"-"), 372 {ok,_} = check_year(Y), 373 {ok,_} = check_positive_integer(M), 374 {ok,_} = check_positive_integer(D), 375 check_time(Time). 376 377check_year(Y) when length(Y)>4 -> 378 Y = string:strip(Y,left,$0), 379 {ok,list_to_integer(Y)}; 380check_year(Y) -> 381 case list_to_integer(Y) of 382 Year when Year =/= 0 -> 383 {ok,Year}; 384 _ -> 385 {error,{invalid_year,Y}} 386 end. 387 388check_month(Str) -> 389 case check_positive_integer(Str) of 390 {ok,Int} when Int >= 1,Int =< 12 -> 391 {ok,Int}; 392 _ -> 393 {error,{invalid_month,Str}} 394 end. 395check_day(Str) -> 396 case check_positive_integer(Str) of 397 {ok,Int} when Int >= 1,Int =< 31 -> 398 {ok,Int}; 399 _ -> 400 {error,{invalid_day,Str}} 401 end. 402 403 404check_time(Time) -> 405 %% hh:mm:ss (.s+)? TZ 406 {HMS,TZ} = 407 case lists:split(8,Time) of 408 {T,"."++SecFractionsTZ} -> 409 OnlyDigits = fun(X) when X>=$0,X=<$9 ->true;(_)->false end, 410 {SecFrac,TZone} = lists:splitwith(OnlyDigits,SecFractionsTZ), 411 {ok,_} = check_positive_integer(SecFrac), 412 {T,TZone}; 413 {T,TZone} -> 414 {T,TZone} 415 end, 416 [H,M,S] = string:tokens(HMS,":"), 417 {ok,_} = check_hour(H), 418 {ok,_} = check_minute(M), 419 {ok,_} = check_second(S), 420 case TZ of 421 [] -> 422 {ok,Time}; %% timezone optional 423 _ -> 424 check_timezone(TZ) 425 end. 426 427check_hour(Str) -> 428 case check_positive_integer(Str) of 429 {ok,H} when H >= 0,H =< 24 -> 430 {ok,H}; 431 _ -> 432 {error,{invalid_hour,Str}} 433 end. 434check_minute(Str) -> 435 case check_positive_integer(Str) of 436 {ok,H} when H >= 0,H =< 60 -> 437 {ok,H}; 438 _ -> 439 {error,{invalid_minute,Str}} 440 end. 441check_second(Str) -> 442 case check_positive_integer(Str) of 443 {ok,H} when H >= 0,H =< 60 -> 444 {ok,H}; 445 _ -> 446 {error,{invalid_second,Str}} 447 end. 448 449check_timezone("Z") -> 450 {ok,"Z"}; 451check_timezone(TZ) -> 452 [H,M] = string:tokens(TZ,":"), 453 case check_integer(H) of 454 {ok,H2} when H2 >= -13, H2 =< 13 -> 455 case check_positive_integer(M) of 456 {ok,M2} when M2 >= 0, M2 =< 59 -> 457 {ok,{H2,M2}}; 458 _ -> 459 {error,{invalid_timezone,TZ,M}} 460 end; 461 {ok,H2} when H2==14;H2==-14 -> 462 case check_positive_integer(M) of 463 {ok,0} -> 464 {ok,{H2,0}}; 465 _ -> 466 {error,{invalid_timezone,TZ}} 467 end; 468 _ -> 469 {error,{invalid_timezone,TZ}} 470 end. 471 472 473%% the form: '-'? yyyy '-' mm '-' dd zzzzzz? 474check_date("-"++Date) -> 475 check_date(Date); 476check_date("+"++_Date) -> 477 {error,{invalid_date,plus_sign}}; 478check_date(Date) -> 479 {Year,Month,Day} = 480 case string:tokens(Date,"-+Z") of 481 [Y,M,D,TZ] -> 482 {ok,_}=check_timezone(TZ), 483 {Y,M,D}; 484 [Y,M,D] -> 485 {Y,M,D} 486 end, 487 {ok,_}=check_year(Year), 488 {ok,_}=check_month(Month), 489 {ok,_}=check_day(Day). 490 491%% gYearMonth on the form: '-'? ccyy '-' mm zzzzzz? 492check_gYearMonth("-"++Value) -> 493 check_gYearMonth(Value); 494check_gYearMonth("+"++_Value) -> 495 {error,{invalid_gYearMonth,plus_sign}}; 496check_gYearMonth(Value) -> 497 {Year,Month} = 498 case string:tokens(Value,"-+Z") of 499 [Y,M,TZ] -> 500 {ok,_} = check_timezone(TZ), 501 {Y,M}; 502 [Y,M] -> 503 {Y,M} 504 end, 505 {ok,_} = check_year(Year), 506 {ok,_} = check_month(Month). 507 508%% gYear on the form: '-'? ccyy zzzzzz? 509check_gYear("-"++Value) -> 510 check_gYear(Value); 511check_gYear("+"++_Value) -> 512 {error,{invalid_gYear,plus_sign}}; 513check_gYear(Value) -> 514 Year = 515 case string:tokens(Value,"-+Z") of 516 [Y,TZ] -> 517 {ok,_} = check_timezone(TZ), 518 Y; 519 [Y] -> 520 Y 521 end, 522 {ok,_} = check_year(Year). 523 524%% gMonthDay on the form: mm dd zzzzzz? 525check_gMonthDay("--"++Value) -> 526 {M,"-"++DTZ} = lists:split(2,Value), 527 {ok,_} = check_month(M), 528 {ok,_} = check_gDay2(DTZ). 529 530%% dDay on the form dd zzzzzz? 531check_gDay("---"++Value) -> 532 check_gDay2(Value). 533check_gDay2(Value) -> 534 {D,TZ} = lists:split(2,Value), 535 {ok,_} = check_day(D), 536 case TZ of 537 [] -> 538 {ok,Value}; 539 _ -> 540 {ok,_} = check_timezone(TZ) 541 end. 542%% dMonth on the form mm zzzzzz? 543check_gMonth("--"++Value) -> 544 {M,TZ} = lists:split(2,Value), 545 {ok,_} = check_month(M), 546 case TZ of 547 [] -> 548 {ok,Value}; 549 _ -> 550 {ok,_} = check_timezone(TZ) 551 end. 552 553check_base64Binary(Value) -> 554 case catch xmerl_b64Bin:parse(xmerl_b64Bin_scan:scan(Value)) of 555 {ok,_} -> 556 {ok,Value}; 557 Err = {error,_} -> 558 Err; 559 {'EXIT',{error,Reason}} -> %% scanner failed on character 560 {error,Reason}; 561 {'EXIT',Reason} -> 562 {error,{internal_error,Reason}} 563 end. 564 565%% tokens may not contain the carriage return (#xD), line feed (#xA) 566%% nor tab (#x9) characters, that have no leading or trailing spaces 567%% (#x20) and that have no internal sequences of two or more spaces. 568check_token(V=[32|_]) -> 569 {error,{invalid_token,leading_space,V}}; 570check_token(Value) -> 571 check_token(Value,Value). 572check_token([],Value) -> 573 {ok,Value}; 574check_token([32],V) -> 575 {error,{invalid_token,trailing_space,V}}; 576check_token([9|_T],V) -> 577 {error,{invalid_token,tab,V}}; 578check_token([10|_T],V) -> 579 {error,{invalid_token,line_feed,V}}; 580check_token([13|_T],V) -> 581 {error,{invalid_token,carriage_return,V}}; 582check_token([32,32|_T],V) -> 583 {error,{invalid_token,double_space,V}}; 584check_token([_H|T],V) -> 585 check_token(T,V). 586 587%% conform to the pattern [a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})* 588check_language(Value) -> 589 check_language(Value,0). 590check_language([H|T],N) when H>=$A,H=<$Z -> 591 check_language(T,N+1); 592check_language([H|T],N) when H>=$a,H=<$z -> 593 check_language(T,N+1); 594check_language([$-|T],N) when N>=1,N=<8 -> 595 check_language2(T,0); 596check_language([],N) when N>=1,N=<8 -> 597 {ok,[]}. 598check_language2([H|T],N) when H>=$A,H=<$Z -> 599 check_language2(T,N+1); 600check_language2([H|T],N) when H>=$a,H=<$z -> 601 check_language2(T,N+1); 602check_language2([H|T],N) when H>=$0,H=<$9 -> 603 check_language2(T,N+1); 604check_language2([$-|T],N) when N>=1,N=<8 -> 605 check_language2(T,0); 606check_language2([],N) when N>=1,N=<8 -> 607 {ok,[]}. 608 609check_NMTOKEN([H|T]) -> 610 true = xmerl_lib:is_namechar(H), 611 check_NMTOKEN2(T). 612check_NMTOKEN2([]) -> 613 {ok,[]}; 614check_NMTOKEN2([H|T]) -> 615 true = xmerl_lib:is_namechar(H), 616 check_NMTOKEN2(T). 617 618check_NMTOKENS(Value) -> 619 TokList = string:tokens(Value," \n\t\r"), 620 lists:foreach(fun check_NMTOKEN/1,TokList), 621 {ok,Value}. 622 623check_Name(Value) -> 624 true = xmerl_lib:is_name(Value), 625 {ok,Value}. 626 627check_NCName(Value) -> 628 true = xmerl_lib:is_ncname(Value), 629 {ok,Value}. 630 631check_ID(Value) -> 632 %% ID must be a NCName and uniquely identify the element which 633 %% bear it. Only one ID per element. 634 true = xmerl_lib:is_ncname(Value), 635 {ok,Value}. 636 637check_IDREF(Value) -> 638 true = xmerl_lib:is_name(Value), 639 {ok,Value}. 640 641check_IDREFS(Value) -> 642 check_list_type(Value,fun check_IDREF/1). 643 644check_ENTITY(Value) -> 645 true = xmerl_lib:is_ncname(Value), 646 {ok,Value}. 647 648check_ENTITIES(Value) -> 649 check_list_type(Value,fun check_ENTITY/1). 650 651check_list_type(Value,BaseTypeFun) -> 652 Tokens = string:tokens(Value," \n\t\r"), 653 lists:foreach(BaseTypeFun,Tokens), 654 {ok,Value}. 655 656ns_whitespace(WS) when WS==16#9;WS==16#A;WS==16#D -> 657 true; 658ns_whitespace(_) -> 659 false. 660 661%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 662%% facet functions 663%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 664 665facet_fun(Type,{length,V}) -> 666 length_fun(Type,list_to_integer(V)); 667facet_fun(Type,{minLength,V}) -> 668 minLength_fun(Type,list_to_integer(V)); 669facet_fun(Type,{maxLength,V}) -> 670 maxLength_fun(Type,list_to_integer(V)); 671facet_fun(Type,{pattern,V}) -> 672%% fun(Val) -> 673%% {ok,Val} 674%% end; 675 pattern_fun(Type,V); 676facet_fun(Type,{enumeration,V}) -> 677 enumeration_fun(Type,V); 678facet_fun(Type,{whiteSpace,V}) -> 679 whiteSpace_fun(Type,V); 680facet_fun(Type,{maxInclusive,V}) -> 681 maxInclusive_fun(Type,V); 682facet_fun(Type,{maxExclusive,V}) -> 683 maxExclusive_fun(Type,V); 684facet_fun(Type,{minExclusive,V}) -> 685 minExclusive_fun(Type,V); 686facet_fun(Type,{minInclusive,V}) -> 687 minInclusive_fun(Type,V); 688facet_fun(Type,{totalDigits,V}) -> 689 totalDigits_fun(Type,list_to_integer(V)); 690facet_fun(Type,{fractionDigits,V}) -> 691 fractionDigits_fun(Type,list_to_integer(V)); 692facet_fun(Type,F) -> 693 fun(_X_) -> 694 error_logger:warning_msg("~w: not valid facet on ~p ~p~n", 695 [?MODULE,Type,F]) 696 end. 697 698 699length_fun(T,V) 700 when T==string;T==normalizedString;T==token; 701 T=='Name';T=='NCName';T==language;T=='ID'; 702 T=='IDREF';T=='IDREFS';T=='ENTITY';T=='ENTITIES'; 703 T=='NMTOKEN';T=='NMTOKENS';T==anyURI -> 704 fun(Val) -> 705 case string:len(Val) == V of 706 true -> {ok,Val}; 707 false -> {error,{length,string:len(Val),should_be,V}} 708 end 709 end; 710length_fun(T,_V) when T=='NOTATION';T=='QName' -> 711 fun(Val) -> 712 {ok,Val} 713 end; 714length_fun(T,V) when T==base64Binary;T==hexBinary -> 715 fun(Val) -> 716 case length(Val)==V of 717 true -> {ok,Val}; 718 false -> {error,{length,length(Val),xhould_be,V}} 719 end 720 end; 721length_fun(T,_V) -> 722 fun(_Val) -> 723 {error,{length_not_applicable_on,T}} 724 end. 725 726minLength_fun(T,V) 727 when T==string;T==normalizedString;T==token; 728 T=='Name';T=='NCName';T==language;T=='ID'; 729 T=='IDREF';T=='IDREFS';T=='ENTITY';T=='ENTITIES'; 730 T=='NMTOKEN';T=='NMTOKENS';T==anyURI -> 731 fun(Val) -> 732 case string:len(Val) >= V of 733 true -> {ok,Val}; 734 false -> {error,{minLength,string:len(Val),should_at_least_be,V}} 735 end 736 end; 737minLength_fun(T,_V) when T=='NOTATION';T=='QName' -> 738 fun(Val) -> 739 {ok,Val} 740 end; 741minLength_fun(T,V) when T==base64Binary;T==hexBinary -> 742 fun(Val) -> 743 case length(Val) >= V of 744 true -> {ok,Val}; 745 false -> {error,{minLength,length(Val),should_at_least_be,V}} 746 end 747 end; 748minLength_fun(T,_V) -> 749 fun(_Val) -> 750 {error,{minLength_not_applicable_on,T}} 751 end. 752 753maxLength_fun(T,V) 754 when T==string;T==normalizedString;T==token; 755 T=='Name';T=='NCName';T==language;T=='ID'; 756 T=='IDREF';T=='IDREFS';T=='ENTITY';T=='ENTITIES'; 757 T=='NMTOKEN';T=='NMTOKENS';T==anyURI -> 758 fun(Val) -> 759 case length(Val) of 760 Len when Len =< V -> {ok,Val}; 761 _ -> {error,{maxLength,string:len(Val),should_not_be_more_than,V}} 762 end 763 end; 764maxLength_fun(T,_V) when T=='NOTATION';T=='QName' -> 765 fun(Val) -> 766 {ok,Val} 767 end; 768maxLength_fun(T,V) when T==base64Binary;T==hexBinary -> 769 fun(Val) -> 770 case length(Val) =< V of 771 true -> {ok,Val}; 772 false -> {error,{maxLength,length(Val),should_not_be_more_than,V}} 773 end 774 end; 775maxLength_fun(T,_V) -> 776 fun(_Val) -> 777 {error,{maxLength_not_applicable_on,T}} 778 end. 779 780pattern_fun(_Type,RegExp) -> 781 case xmerl_regexp:setup(RegExp) of 782 {ok,RE} -> 783 fun(Val) -> 784 case xmerl_regexp:first_match(Val,RE) of 785 {match,_,_} -> {ok,Val}; 786 _ -> {error,{pattern_mismatch,Val,RegExp}} 787 end 788 end; 789 _ -> 790 fun(Val) -> 791 {error,{unsupported_pattern,Val,RegExp}} 792 end 793 end. 794 795enumeration_fun(_Type,V) -> 796 fun(Val) -> 797 case lists:member(Val,V) of 798 true -> {ok,Val}; 799 false -> {error,{enumeration,Val,should_be_one_of,V}} 800 end 801 end. 802 803whiteSpace_fun(_Type,"preserve") -> 804 fun(Val) -> 805 {ok,Val} 806 end; 807whiteSpace_fun(_Type,"replace") -> 808 fun(Val) -> 809 {ok,?MODULE:replace_ws(Val,[])} 810 end; 811whiteSpace_fun(_Type,"collapse") -> 812 fun(Val) -> 813 {ok,?MODULE:collapse_ws(Val)} 814 end. 815 816replace_ws([16#9|T],Acc) -> 817 replace_ws(T,[16#20|Acc]); 818replace_ws([16#a|T],Acc) -> 819 replace_ws(T,[16#20|Acc]); 820replace_ws([16#d|T],Acc) -> 821 replace_ws(T,[16#20|Acc]); 822replace_ws([H|T],Acc) -> 823 replace_ws(T,[H|Acc]); 824replace_ws([],Acc) -> 825 lists:reverse(Acc). 826 827collapse_ws(Val) -> 828 collapse_ws(lists:dropwhile(fun(WS) when ?is_whitespace(WS) ->true;(_) -> false end, 829 replace_ws(Val,[])),[]). 830collapse_ws([16#20,16#20|T],Acc) -> 831 collapse_ws([16#20|T],Acc); 832collapse_ws([H|T],Acc) -> 833 collapse_ws(T,[H|Acc]); 834collapse_ws([],Acc) -> 835 lists:reverse(lists:dropwhile(fun($ ) ->true;(_) -> false end,Acc)). 836 837maxInclusive_fun(T,V) 838 when T==integer;T==positiveInteger;T==negativeInteger; 839 T==nonNegativeInteger;T==nonPositiveInteger;T==long; 840 T==unsignedLong;T==int;T==unsignedInt;T==short; 841 T==unsignedShort;T==byte;T==unsignedByte -> 842 fun(Val) -> 843 case (catch list_to_integer(Val) =< list_to_integer(V)) of 844 true -> 845 {ok,Val}; 846 _ -> 847 {error,{maxInclusive,Val,should_be_less_than_or_equal_with,V}} 848 end 849 end; 850maxInclusive_fun(T,V) when T==decimal;T==float;T==double -> 851 fun(Val) -> 852 case ?MODULE:compare_floats(Val,V) of 853 gt -> 854 {error,{maxInclusive,Val,should_be_less_than_or_equal_with,V}}; 855 Err={error,_} -> Err; 856 _ -> 857 {ok,Val} 858 end 859 end; 860maxInclusive_fun(T,V) when T==duration -> 861 fun(Val) -> 862 case ?MODULE:compare_durations(Val,V) of 863 GT when GT==gt;GT==indefinite -> 864 {error,{maxInclusive,Val,should_be_less_than_or_equal_with,V}}; 865 _ -> 866 {ok,Val} 867 end 868 end; 869maxInclusive_fun(T,V) when T==dateTime -> 870 fun(Val) -> 871 case ?MODULE:compare_dateTime(Val,V) of 872 GT when GT==gt;GT==indefinite -> 873 {error,{maxInclusive,Val,should_be_less_than_or_equal_with,V}}; 874 _ -> 875 {ok,Val} 876 end 877 end; 878maxInclusive_fun(T,_V) -> 879%% when T==duration;T==dateTime;T==date;T==time;T==gYear;T==gYearMonth; 880%% T==gMonth;T==gMonthDay;T==gDay -> 881 fun(_) -> {error,{maxInclusive,not_implemented_for,T}} end. 882 883maxExclusive_fun(T,V) 884 when T==integer;T==positiveInteger;T==negativeInteger; 885 T==nonNegativeInteger;T==nonPositiveInteger;T==long; 886 T==unsignedLong;T==int;T==unsignedInt;T==short; 887 T==unsignedShort;T==byte;T==unsignedByte -> 888 fun(Val) -> 889 case (catch list_to_integer(Val) < list_to_integer(V)) of 890 true -> 891 {ok,Val}; 892 _ -> 893 {error,{maxExclusive,Val,not_less_than,V}} 894 end 895 end; 896maxExclusive_fun(T,V) when T==decimal;T==float;T==double -> 897 fun(Val) -> 898 case ?MODULE:compare_floats(Val,V) of 899 lt -> 900 {ok,Val}; 901 Err={error,_} -> Err; 902 _ -> 903 {error,{maxExclusive,Val,not_less_than,V}} 904 end 905 end; 906maxExclusive_fun(T,V) when T==duration -> 907 fun(Val) -> 908 case ?MODULE:compare_durations(Val,V) of 909 lt -> 910 {ok,Val}; 911 _ -> 912 {error,{maxExclusive,Val,not_less_than,V}} 913 end 914 end; 915maxExclusive_fun(T,V) when T==dateTime -> 916 fun(Val) -> 917 case ?MODULE:compare_dateTime(Val,V) of 918 lt -> 919 {ok,Val}; 920 _ -> 921 {error,{maxExclusive,Val,not_less_than,V}} 922 end 923 end; 924maxExclusive_fun(T,_V) -> 925 fun(_) -> {error,{maxExclusive,not_implemented_for,T}} end. 926 927minExclusive_fun(T,V) 928 when T==integer;T==positiveInteger;T==negativeInteger; 929 T==nonNegativeInteger;T==nonPositiveInteger;T==long; 930 T==unsignedLong;T==int;T==unsignedInt;T==short; 931 T==unsignedShort;T==byte;T==unsignedByte -> 932 fun(Val) -> 933 case (catch list_to_integer(Val) > list_to_integer(V)) of 934 true -> 935 {ok,Val}; 936 _ -> 937 {error,{minExclusive,Val,not_greater_than,V}} 938 end 939 end; 940minExclusive_fun(T,V) when T==decimal;T==float;T==double -> 941 fun(Val) -> 942 case ?MODULE:compare_floats(Val,V) of 943 gt -> 944 {ok,Val}; 945 Err={error,_} -> Err; 946 _ -> 947 {error,{minExclusive,Val,not_greater_than,V}} 948 end 949 end; 950minExclusive_fun(T,V) when T==duration -> 951 fun(Val) -> 952 case ?MODULE:compare_durations(Val,V) of 953 gt -> 954 {ok,Val}; 955 _ -> 956 {error,{minExclusive,Val,not_greater_than,V}} 957 end 958 end; 959minExclusive_fun(T,V) when T==dateTime -> 960 fun(Val) -> 961 case ?MODULE:compare_dateTime(Val,V) of 962 gt -> 963 {ok,Val}; 964 _ -> 965 {error,{minExclusive,Val,not_greater_than,V}} 966 end 967 end; 968minExclusive_fun(T,_V) -> 969 fun(_) -> {error,{minExclusive,not_implemented_for,T}} end. 970 971minInclusive_fun(T,V) 972 when T==integer;T==positiveInteger;T==negativeInteger; 973 T==nonNegativeInteger;T==nonPositiveInteger;T==long; 974 T==unsignedLong;T==int;T==unsignedInt;T==short; 975 T==unsignedShort;T==byte;T==unsignedByte -> 976 fun(Val) -> 977 case (catch list_to_integer(Val) >= list_to_integer(V)) of 978 true -> 979 {ok,Val}; 980 _ -> 981 {error,{minInclusive,Val,not_greater_than_or_equal_with,V}} 982 end 983 end; 984minInclusive_fun(T,V) when T==decimal;T==float;T==double -> 985 fun(Val) -> 986 case ?MODULE:compare_floats(Val,V) of 987 lt -> 988 {error,{minInclusive,Val,not_greater_than_or_equal_with,V}}; 989 Err={error,_} -> Err; 990 _ -> 991 {ok,Val} 992 end 993 end; 994minInclusive_fun(T,V) when T==duration -> 995 fun(Val) -> 996 case ?MODULE:compare_durations(Val,V) of 997 lt -> 998 {error,{minInclusive,Val,not_greater_than_or_equal_with,V}}; 999 _ -> 1000 {ok,Val} 1001 end 1002 end; 1003minInclusive_fun(T,V) when T==dateTime -> 1004 fun(Val) -> 1005 case ?MODULE:compare_dateTime(Val,V) of 1006 lt -> 1007 {error,{minInclusive,Val,not_greater_than_or_equal_with,V}}; 1008 _ -> 1009 {ok,Val} 1010 end 1011 end; 1012minInclusive_fun(T,_V) -> 1013 fun(_) -> {error,{minInclusive,not_implemented_for,T}} end. 1014 1015totalDigits_fun(T,V) 1016 when T==integer;T==positiveInteger;T==negativeInteger;T==nonNegativeInteger; 1017 T==nonPositiveInteger;T==long;T==unsignedLong;T==int;T==unsignedInt; 1018 T==short;T==unsignedShort;T==byte;T==unsignedByte;T==decimal -> 1019 %% Val is expressible as i * 10^-n where i and n are integers 1020 %% such that |i| < 10^Val and 0 =< n =< Val. 1021 fun(Val) -> 1022 Pred = fun($0)-> true; 1023 (_) -> false 1024 end, 1025 Val2 = lists:dropwhile(Pred,Val), 1026 Length = 1027 case lists:member($.,Val2) of 1028 true -> 1029 length(lists:dropwhile(Pred,lists:reverse(Val2))) -1; 1030 _ -> 1031 length(Val2) 1032 end, 1033 if 1034 Length =< V -> 1035 {ok,Val}; 1036 true -> 1037 {error,{totalDigits,Length,to_many_digits}} 1038 end 1039 end; 1040totalDigits_fun(T,_V) -> 1041 fun(_) -> {error,{totalDigits,not_applicable,T}} end. 1042 1043fractionDigits_fun(T,V) 1044 when T==integer;T==positiveInteger;T==negativeInteger;T==nonNegativeInteger; 1045 T==nonPositiveInteger;T==long;T==unsignedLong;T==int;T==unsignedInt; 1046 T==short;T==unsignedShort;T==byte;T==unsignedByte;T==decimal -> 1047 fun(Val) -> 1048 Len = 1049 case string:tokens(Val,".") of 1050 [_I,Frc] when T==decimal -> 1051 Pred = fun($0)-> true; 1052 (_) -> false 1053 end, 1054 length(lists:dropwhile(Pred,lists:reverse(Frc))); 1055 _ -> 1056 0 1057 end, 1058 if 1059 Len =< V -> 1060 {ok,Val}; 1061 true -> 1062 {error,{fractionDigits,Len,to_many_digits_in,Val}} 1063 end 1064 end; 1065fractionDigits_fun(T,_V) -> 1066 fun(_) -> {error,{fractionDigits,not_applicable,T}} end. 1067 1068 1069%% The relation between F1 and F2 may be eq,lt or gt. 1070%% lt: F1 < F2 1071%% gt: F1 > F2 1072compare_floats(F1,F2) when F1=="NaN";F2=="NaN" -> 1073 {error,{not_comparable}}; 1074compare_floats(F1,F1) -> 1075 eq; 1076compare_floats(F1,F2) when F1=="INF";F2=="-INF" -> 1077 gt; 1078compare_floats(F1,F2) when F1=="-INF";F2=="INF" -> 1079 lt; 1080compare_floats(Str1,Str2) -> 1081 F1={S1,_B1,_D1,_E1} = str_to_float(Str1), 1082 F2={S2,_B2,_D2,_E2} = str_to_float(Str2), 1083% ?dbg("F1: ~p~nF2: ~p~n",[F1,F2]), 1084 if 1085 S1=='-',S2=='+' -> lt; 1086 S1=='+',S2=='-' -> gt; 1087% B1<0 -> compare_floats2(F2,F1); 1088 true -> compare_floats2(F1,F2) 1089 end. 1090compare_floats2({S1,B1,D1,E1},{_S2,B2,D2,E2}) when B1==0;B2==0 -> 1091 I1 = pow(B1,D1,E1), 1092 I2 = pow(B2,D2,E2), 1093 if I1 > I2 -> 1094 sign(S1,gt); 1095 I1 < I2 -> 1096 sign(S1,lt); 1097 true -> 1098 eq 1099 end; 1100compare_floats2({S1,B1,D1,E1},{_S2,B2,D2,E2}) -> 1101 %% S1 and S2 have same sign. 1102 I1 = pow(B1,E1),% B1 * round(math:pow(10,E1)), 1103 I2 = pow(B2,E2),%B2 * round(math:pow(10,E2)), 1104 if 1105 I1 > I2 -> sign(S1,gt); 1106 I1 < I2 -> sign(S1,lt); 1107 true -> 1108 %% fractions are compared in lexicographic order 1109 if 1110 D1 == D2 -> eq; 1111 D1 < D2 -> sign(S1,lt); 1112 D1 > D2 -> sign(S1,gt) 1113 end 1114 end. 1115 1116str_to_float(String) -> 1117 {Sign,Str} = 1118 case String of 1119 "-"++Str1 -> {'-',Str1}; 1120 _ -> {'+',String} 1121 end, 1122 case string:tokens(Str,".") of 1123 [B,DE] -> 1124 case string:tokens(DE,"Ee") of 1125 [D,E] -> 1126 %% round(math:pow(10,list_to_integer(E))) 1127 {Sign,list_to_integer(B), 1128 remove_trailing_zeros(D), 1129 list_to_integer(E)}; 1130 [D] -> 1131 {Sign,list_to_integer(B), 1132 remove_trailing_zeros(D),0} 1133 end; 1134 [B] -> %% could also be 1E4, but no fraction 1135 case string:tokens(Str,"Ee") of 1136 [I,E] -> 1137 {Sign,list_to_integer(I),"0",list_to_integer(E)}; 1138 _ -> 1139 {Sign,list_to_integer(B),"0",0} 1140 end 1141 end. 1142 1143pow(Mantissa,Exponent) -> 1144 case (Mantissa * math:pow(10,Exponent)) of 1145 I when I<1 -> 1146 I; 1147 I -> round(I) 1148 end. 1149 1150pow(Mantissa,Fraction,Exponent) -> 1151 (Mantissa * math:pow(10,Exponent)) + 1152 (list_to_integer(Fraction) * math:pow(10,Exponent-length(Fraction))). 1153 1154sign('-',gt) -> 1155 lt; 1156sign('-',lt) -> 1157 gt; 1158sign(_,Rel) -> 1159 Rel. 1160 1161remove_trailing_zeros(Str) -> 1162 Pred = fun($0) ->true;(_) ->false end, 1163 case lists:reverse(lists:dropwhile(Pred,lists:reverse(Str))) of 1164 [] -> 1165 "0"; 1166 Fr -> Fr 1167 end. 1168 1169 1170%% when T==duration;T==dateTime;T==date;T==time;T==gYear;T==gYearMonth; 1171%% T==gMonth;T==gMonthDay;T==gDay -> 1172 1173%% compare_duration(V1,V2) compares V1 to V2 1174%% returns gt | lt | eq | indefinite 1175%% ex: V1 > V2 -> gt 1176%% 1177%% V1, V2 on format PnYnMnDTnHnMnS 1178%% P is always present 1179%% T is absent iff all time items are absent 1180%% compare_duration(V1,V2) -> 1181%% {Y1,M1,D1,H1,M1,S1} = duration_atoms(V1), 1182%% {Y2,M2,D2,H2,M2,S2} = duration_atoms(V2), 1183%% YearDiff = Y1 - Y2, 1184%% MonthsDiff = M1 - M2, 1185%% DaysDiff = D1 - D2, 1186compare_durations(V1,V2) -> 1187 %% Four reference dateTimes are used, see XMLSchema part 2, 1188 %% 3.2.6.2. 1189 %% "The order-relation of two duration values x and y is x < y iff 1190 %% s+x < s+y for each qualified dateTime s in the list below." 1191 Ref1_dateTime = {1696,9,1,0,0,0,{pos,0,0}},%1696-09-01T00:00:00Z 1192 Ref2_dateTime = {1697,2,1,0,0,0,{pos,0,0}},%1697-02-01T00:00:00Z 1193 Ref3_dateTime = {1903,3,1,0,0,0,{pos,0,0}},%1903-03-01T00:00:00Z 1194 Ref4_dateTime = {1903,7,1,0,0,0,{pos,0,0}},%1903-07-01T00:00:00Z 1195 CmpRes1=compare_dateTime(normalize_dateTime(add_duration2dateTime(Ref1_dateTime,V1)), 1196 normalize_dateTime(add_duration2dateTime(Ref1_dateTime,V2))), 1197 CmpRes2=compare_dateTime(normalize_dateTime(add_duration2dateTime(Ref2_dateTime,V1)), 1198 normalize_dateTime(add_duration2dateTime(Ref2_dateTime,V2))), 1199 CmpRes3=compare_dateTime(normalize_dateTime(add_duration2dateTime(Ref3_dateTime,V1)), 1200 normalize_dateTime(add_duration2dateTime(Ref3_dateTime,V2))), 1201 CmpRes4=compare_dateTime(normalize_dateTime(add_duration2dateTime(Ref4_dateTime,V1)), 1202 normalize_dateTime(add_duration2dateTime(Ref4_dateTime,V2))), 1203 if 1204 CmpRes1==CmpRes2, 1205 CmpRes1==CmpRes3, 1206 CmpRes1==CmpRes4 -> 1207 CmpRes1; 1208 true -> indefinite 1209 end. 1210 1211 1212compare_dateTime(DT1={_,_,_,_,_,_,Z},DT2={_,_,_,_,_,_,Z}) -> 1213 case DT1<DT2 of 1214 true -> lt; 1215 _ -> 1216 case DT1>DT2 of 1217 true -> 1218 gt; 1219 _ -> eq 1220 end 1221 end; 1222%% If P contains a time zone and Q does not, compare as follows: 1223%% 1. P < Q if P < (Q with time zone +14:00) 1224%% 2. P > Q if P > (Q with time zone -14:00) 1225%% 3. P <> Q otherwise, that is, if (Q with time zone +14:00) < P < 1226%% (Q with time zone -14:00) 1227compare_dateTime(P={_,_,_,_,_,_,{_,_,_}},_Q={Y,M,D,H,Min,S,none}) -> 1228 case compare_dateTime(P,normalize_dateTime({Y,M,D,H,Min,S,{pos,14,0}})) of 1229 lt -> 1230 lt; 1231 _ -> 1232 case compare_dateTime(P,normalize_dateTime({Y,M,D,H,Min,S,{neg,14,0}})) of 1233 gt -> 1234 gt; 1235 _ -> 1236 indefinite 1237 end 1238 end; 1239%% If P does not contain a time zone and Q does, compare as follows: 1240%% 1. P < Q if (P with time zone -14:00) < Q. 1241%% 2. P > Q if (P with time zone +14:00) > Q. 1242%% 3. P <> Q otherwise, that is, if (P with time zone +14:00) < Q < 1243%% (P with time zone -14:00) 1244compare_dateTime(_P={Y,M,D,H,Min,S,none},Q={_,_,_,_,_,_,{_,_,_}}) -> 1245 case compare_dateTime(normalize_dateTime({Y,M,D,H,Min,S,{neg,14,0}}),Q) of 1246 lt -> 1247 lt; 1248 _ -> 1249 case compare_dateTime(normalize_dateTime({Y,M,D,H,Min,S,{pos,14,0}}),Q) of 1250 gt -> 1251 gt; 1252 _ -> 1253 indefinite 1254 end 1255 end; 1256compare_dateTime(P,Q) when is_list(P) -> 1257 compare_dateTime(normalize_dateTime(dateTime_atoms(P)),Q); 1258compare_dateTime(P,Q) when is_list(Q) -> 1259 compare_dateTime(P,normalize_dateTime(dateTime_atoms(Q))); 1260compare_dateTime(_P,_Q) -> 1261 indefinite. 1262 1263fQuotient(A,B) when is_float(A) -> 1264 fQuotient(erlang:floor(A),B); 1265fQuotient(A,B) when is_float(B) -> 1266 fQuotient(A,erlang:floor(B)); 1267fQuotient(A,B) when A >= 0, B >= 0 -> 1268 A div B; 1269fQuotient(A,B) when A < 0, B < 0 -> 1270 A div B; 1271fQuotient(A,B) -> 1272 case A rem B of 1273 0 -> 1274 A div B; 1275 _ -> 1276 (A div B) -1 1277 end. 1278 1279fQuotient(A, Low, High) -> 1280 fQuotient(A - Low, High - Low). 1281 1282modulo(A,B) -> 1283 A - (fQuotient(A,B) * B). 1284 1285modulo(A, Low, High) -> 1286 modulo(A - Low, High - Low) + Low. 1287 1288maximumDayInMonthFor(YearValue, MonthValue) -> 1289 M = modulo(MonthValue, 1, 13), 1290 Y = YearValue + fQuotient(MonthValue, 1, 13), 1291 monthValue(M,Y). 1292 1293monthValue(M,_Y) when M==1;M==3;M==5;M==7;M==8;M==10;M==12 -> 1294 31; 1295monthValue(M,_Y) when M==4;M==6;M==9;M==11 -> 1296 30; 1297monthValue(_M,Y) -> 1298 case modulo(Y,400) of 1299 0 -> 1300 29; 1301 _ -> 1302 case {modulo(Y,100) /= 0,modulo(Y,4)} of 1303 {true,0} -> 1304 29; 1305 _ -> 1306 28 1307 end 1308 end. 1309 1310%% S dateTime, D duration 1311%% result is E dateTime, end of time period with start S and duration 1312%% D. E = S + D. 1313add_duration2dateTime(S,D) when is_list(S),is_list(D) -> 1314 Satoms = dateTime_atoms(S), 1315 case duration_atoms(D) of 1316 Datoms = {_,_,_,_,_,_} -> 1317 add_duration2dateTime2(Satoms,Datoms); 1318 Err -> 1319 {error,Err} 1320 end; 1321add_duration2dateTime(S={_,_,_,_,_,_,_},D) -> 1322 case duration_atoms(D) of 1323 Datoms = {_,_,_,_,_,_} -> 1324 add_duration2dateTime2(S,Datoms); 1325 Err -> 1326 {error,Err} 1327 end. 1328 1329add_duration2dateTime2({Syear,Smonth,Sday,Shour,Sminute,Ssec,Szone}, 1330 {Dyears,Dmonths,Ddays,Dhours,Dminutes,Dsecs}) -> 1331 1332 %% months 1333 Temp1 = Smonth + Dmonths, 1334 Emonth = modulo(Temp1,1,13), 1335 Carry1 = fQuotient(Temp1,1,13), 1336 1337 %% years 1338 Eyear = Syear + Dyears + Carry1, 1339 1340 %% seconds 1341 Temp2 = Ssec + Dsecs, 1342 Esecs = modulo(Temp2,60), 1343 Carry2 = fQuotient(Temp2,60), 1344 1345 %% minutes 1346 Temp3 = Sminute + Dminutes + Carry2, 1347 Eminute = modulo(Temp3,60), 1348 Carry3 = fQuotient(Temp3,60), 1349 1350 %% hours 1351 Temp4 = Shour + Dhours + Carry3, 1352 Ehour = modulo(Temp4,24), 1353 Carry4 = fQuotient(Temp4,24), 1354 1355 %% days 1356 TempDays = 1357 case maximumDayInMonthFor(Eyear,Emonth) of 1358 MaxDay when Sday > MaxDay -> 1359 MaxDay; 1360 _ -> 1361 case Sday < 1 of 1362 true -> 1363 1; 1364 _ -> 1365 Sday 1366 end 1367 end, 1368 {Eyear2,Emonth2,Eday} = 1369 carry_loop(TempDays+Ddays+Carry4,Emonth,Eyear), 1370 {Eyear2,Emonth2,Eday,Ehour,Eminute,Esecs,Szone}. 1371 1372carry_loop(Eday,Emonth,Eyear) when Eday < 1 -> 1373 carry_loop(Eday + maximumDayInMonthFor(Eyear,Emonth - 1), 1374 modulo(Emonth - 1,1,13), 1375 Eyear + fQuotient(Emonth - 1,1,13)); 1376carry_loop(Eday,Emonth,Eyear) -> 1377 case maximumDayInMonthFor(Eyear,Emonth) of 1378 MaxD when Eday > MaxD -> 1379 carry_loop(Eday - maximumDayInMonthFor(Eyear,Emonth), 1380 modulo(Emonth + 1,1,13), 1381 Eyear + fQuotient(Emonth+1,1,13)); 1382 _ -> 1383 {Eyear,Emonth,Eday} 1384 end. 1385 1386%% Format: '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)? 1387dateTime_atoms("-" ++ DT) -> 1388 dateTime_atoms(DT,neg); 1389dateTime_atoms(DT) -> 1390 dateTime_atoms(DT,pos). 1391dateTime_atoms(S,Sign) -> 1392 [Date,TimeZone] = string:tokens(S,"T"), 1393 [YY,MM,DD] = string:tokens(Date,"-"), 1394 {Zone,ZoneSign,[Hour,Min,Sec]} = 1395 case lists:reverse(TimeZone) of 1396 "Z"++_ -> 1397 {"Z",pos,string:tokens(TimeZone,"Z:")}; 1398 _ -> 1399 ZS = zone_sign(TimeZone), 1400 case string:tokens(TimeZone,"-+") of 1401 [Time,Z] -> 1402 {Z,ZS,string:tokens(Time,":")}; 1403 [Time] -> 1404 {none,ZS,string:tokens(Time,":")} 1405 end 1406 end, 1407 {set_sign(Sign,YY),list_to_integer(MM),list_to_integer(DD), 1408 list_to_integer(Hour),list_to_integer(Min),sign_sec(pos,Sec), 1409 zone_atoms(ZoneSign,Zone)}. 1410 1411zone_sign(TimeZone) -> 1412 case lists:member($-,TimeZone) of 1413 true -> 1414 neg; 1415 _ -> 1416 pos 1417 end. 1418 1419zone_atoms(_Sign,"Z") -> 1420 {pos,0,0}; 1421zone_atoms(Sign,Zone) when is_list(Zone) -> 1422 case string:tokens(Zone,":") of 1423 [H,M] -> 1424 {Sign,list_to_integer(H),list_to_integer(M)}; 1425 _ -> none 1426 end; 1427zone_atoms(_Sign,Zone) -> 1428 Zone. 1429 1430 1431%% Format: '-'? PnYnMnDTnHnMnS 1432duration_atoms("-P"++Dur) -> 1433 duration_atoms2(Dur,neg); 1434duration_atoms("P"++Dur) -> 1435 duration_atoms2(Dur,pos); 1436duration_atoms(Dur) -> 1437 {illegal_duration,Dur}. 1438duration_atoms2(Dur,Sign) -> 1439 case lists:member($T,Dur) of 1440 true -> %% time atoms exists 1441 case string:tokens(Dur,"T") of 1442 [Date,Time] -> 1443 case duration_atoms_date(Date) of 1444 {Y,M,D} -> 1445 case duration_atoms_time(Time) of 1446 {Hour,Min,Sec} -> 1447 {set_sign(Sign,Y),set_sign(Sign,M), 1448 set_sign(Sign,D),set_sign(Sign,Hour), 1449 set_sign(Sign,Min),sign_sec(Sign,Sec)}; 1450 Err -> 1451 Err 1452 end; 1453 Err -> 1454 Err 1455 end; 1456 [Time] -> 1457 case duration_atoms_time(Time) of 1458 {Hour,Min,Sec} -> 1459 {0,0,0,set_sign(Sign,Hour),set_sign(Sign,Min), 1460 sign_sec(Sign,Sec)}; 1461 Err -> 1462 Err 1463 end; 1464 Err -> 1465 {illegal_duration,Err} 1466 end; 1467 _ -> %% only date coomponents 1468 {Y,M,D} = duration_atoms_date(Dur), 1469 {set_sign(Sign,Y),set_sign(Sign,M),set_sign(Sign,D),0,0,0} 1470 end. 1471 1472duration_atoms_date(Date) -> 1473 {Y,Date2} = get_digit(Date,$Y), 1474 {M,Date3} = get_digit(Date2,$M), 1475 {D,Rest} = get_digit(Date3,$D), 1476 case Rest of 1477 "" -> {Y,M,D}; 1478 Err -> {illegal_duration,Err} 1479 end. 1480duration_atoms_time(Time) -> 1481 {H,Time2} = get_digit(Time,$H), 1482 {M,Time3} = get_digit(Time2,$M), 1483 {S,Rest} = get_sec(Time3), 1484 case Rest of 1485 "" -> 1486 {H,M,S}; 1487 Err -> 1488 {illegal_duration,Err} 1489 end. 1490 1491get_digit(Str,Delim) -> 1492 get_digit(Str,Delim,[],Str). 1493get_digit([Delim|T],Delim,Acc,_Str) -> 1494 {lists:reverse(Acc),T}; 1495get_digit([H|T],Delim,Acc,Str) when H>=$0,H=<$9 -> 1496 get_digit(T,Delim,[H|Acc],Str); 1497get_digit([],_,[],_Str) -> 1498 {"0",[]}; 1499get_digit([],_,_,Str) -> 1500 {"0",Str}; 1501get_digit(_,_,_,Str) -> 1502 %% this matches both the case when reaching another delimeter and 1503 %% when the string already are emptied. 1504 {"0",Str}. 1505 1506get_sec([]) -> 1507 {"0",[]}; 1508get_sec(Str) -> 1509 get_sec(Str,[],Str). 1510get_sec([H|T],Acc,Str) when H>=$0,H=<$9 -> 1511 get_sec(T,[H|Acc],Str); 1512get_sec([$.|T],Acc,Str) -> 1513 get_sec(T,[$.|Acc],Str); 1514get_sec([$S|T],Acc,_) -> 1515 {lists:reverse(Acc),T}; 1516get_sec(_,_,Str) -> 1517 {"0",Str}. 1518 1519 1520set_sign(pos,Istr) -> 1521 list_to_integer(Istr); 1522set_sign(_,Istr) -> 1523 list_to_integer("-"++Istr). 1524sign_sec(pos,Sec) -> 1525 case lists:member($.,Sec) of 1526 true -> 1527 list_to_float(Sec); 1528 _ -> 1529 list_to_integer(Sec) 1530 end; 1531sign_sec(_,Sec) -> 1532 sign_sec(pos,"-"++Sec). 1533 1534invert_sign(pos) -> 1535 neg; 1536invert_sign(neg) -> 1537 pos; 1538invert_sign(S) -> 1539 S. 1540 1541normalize_dateTime({Y,M,D,Hour,Min,Sec,{Sign,ZH,ZM}}) -> 1542 %% minutes 1543 TmpMin = Min + set_sign(invert_sign(Sign),integer_to_list(ZM)), 1544 NMin = modulo(TmpMin,60), 1545 Carry1 = fQuotient(TmpMin,60), 1546 1547 %% hours 1548 TmpHour = Hour + set_sign(invert_sign(Sign),integer_to_list(ZH)) + Carry1, 1549 NHour = modulo(TmpHour,24), 1550 Carry2 = fQuotient(TmpHour,24), 1551 1552 {NY,NM,ND} = 1553 carry_loop(D+Carry2,M,Y), 1554 {NY,NM,ND,NHour,NMin,Sec,{pos,0,0}}; 1555normalize_dateTime(DT) -> 1556 DT. 1557