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