1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: xpathlib.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
32 // MARKER(update_precomp.py): autogen include statement, do not remove
33 #include "precompiled_forms.hxx"
35 #include <sal/types.h>
36 #include <rtl/alloc.h>
37 #include <rtl/ustring.hxx>
38 #include <rtl/string.hxx>
39 #include <rtl/ustrbuf.hxx>
40 #include <rtl/strbuf.hxx>
41 #include <tools/date.hxx>
42 #include <tools/time.hxx>
43 #include <tools/datetime.hxx>
45 #include <com/sun/star/uno/Reference.hxx>
46 #include <com/sun/star/uno/Sequence.hxx>
47 #include <com/sun/star/uno/Any.hxx>
48 #include <com/sun/star/xforms/XModel.hpp>
49 #include <com/sun/star/xml/dom/XNode.hpp>
50 #include <com/sun/star/xml/dom/XDocument.hpp>
51 #include <com/sun/star/lang/XUnoTunnel.hpp>
53 #include "xpathlib.hxx"
55 #include "extension.hxx"
59 using namespace com::sun::star::uno
;
60 using namespace com::sun::star::xml::dom
;
61 using namespace com::sun::star::xforms
;
62 using namespace com::sun::star::lang
;
64 xmlXPathFunction
xforms_lookupFunc(void *, const xmlChar
*xname
, const xmlChar
*)
67 const char *name
= (char *)xname
;
68 if (strcmp("boolean-from-string", name
)==0)
69 return xforms_booleanFromStringFunction
;
70 else if ((strcmp("if", name
))==0)
71 return xforms_ifFunction
;
72 else if ((strcmp("avg", name
))==0)
73 return xforms_avgFunction
;
74 else if ((strcmp("min", name
))==0)
75 return xforms_minFunction
;
76 else if ((strcmp("max", name
))==0)
77 return xforms_maxFunction
;
78 else if ((strcmp("count-non-empty", name
))==0)
79 return xforms_countNonEmptyFunction
;
80 else if ((strcmp("index", name
))==0)
81 return xforms_indexFunction
;
82 else if ((strcmp("property", name
))==0)
83 return xforms_propertyFunction
;
84 else if ((strcmp("now", name
))==0)
85 return xforms_nowFunction
;
86 else if ((strcmp("days-from-date", name
))==0)
87 return xforms_daysFromDateFunction
;
88 else if ((strcmp("seconds-from-dateTime", name
))==0)
89 return xforms_secondsFromDateTimeFunction
;
90 else if ((strcmp("seconds", name
))==0)
91 return xforms_secondsFuction
;
92 else if ((strcmp("months", name
))==0)
93 return xforms_monthsFuction
;
94 else if ((strcmp("instance", name
))==0)
95 return xforms_instanceFuction
;
96 else if ((strcmp("current", name
))==0)
97 return xforms_currentFunction
;
103 void xforms_booleanFromStringFunction(xmlXPathParserContextPtr ctxt
, int nargs
)
105 if (nargs
!= 1) XP_ERROR(XPATH_INVALID_ARITY
);
106 xmlChar
*pString
= xmlXPathPopString(ctxt
);
107 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
108 ::rtl::OUString
aString((char*)pString
, strlen((char*)pString
), RTL_TEXTENCODING_UTF8
);
109 if (aString
.equalsIgnoreAsciiCaseAscii("true") || aString
.equalsIgnoreAsciiCaseAscii("1"))
110 xmlXPathReturnTrue(ctxt
);
111 else if (aString
.equalsIgnoreAsciiCaseAscii("false") || aString
.equalsIgnoreAsciiCaseAscii("0"))
112 xmlXPathReturnFalse(ctxt
);
114 XP_ERROR(XPATH_NUMBER_ERROR
);
117 void xforms_ifFunction(xmlXPathParserContextPtr ctxt
, int nargs
)
119 if (nargs
!= 3) XP_ERROR(XPATH_INVALID_ARITY
);
120 xmlChar
*s2
= xmlXPathPopString(ctxt
);
122 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
123 xmlChar
*s1
= xmlXPathPopString(ctxt
);
124 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
125 bool aBool
= xmlXPathPopBoolean(ctxt
);
126 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
129 xmlXPathReturnString(ctxt
, s1
);
131 xmlXPathReturnString(ctxt
, s2
);
136 void xforms_avgFunction(xmlXPathParserContextPtr ctxt
, int nargs
)
138 // use sum(), div() and count()
139 if (nargs
!= 1) XP_ERROR(XPATH_INVALID_ARITY
);
142 xmlXPathObjectPtr pObject
= valuePop(ctxt
);
143 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
145 valuePush(ctxt
, xmlXPathObjectCopy(pObject
));
147 xmlXPathSumFunction(ctxt
, 1);
148 double nSum
= xmlXPathPopNumber(ctxt
);
149 // push a copy once more
150 valuePush(ctxt
, xmlXPathObjectCopy(pObject
));
151 xmlXPathCountFunction(ctxt
, 1);
152 double nCount
= xmlXPathPopNumber(ctxt
);
153 // push args for div()
154 xmlXPathReturnNumber(ctxt
, nSum
);
155 xmlXPathReturnNumber(ctxt
, nCount
);
156 xmlXPathDivValues(ctxt
);
157 // the result is now on the ctxt stack
158 xmlXPathFreeObject(pObject
);
161 void xforms_minFunction(xmlXPathParserContextPtr ctxt
, int nargs
)
163 if (nargs
!= 1) XP_ERROR(XPATH_INVALID_ARITY
);
164 xmlNodeSetPtr pNodeSet
= xmlXPathPopNodeSet(ctxt
);
165 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
168 for (int i
= 0; i
< xmlXPathNodeSetGetLength(pNodeSet
); i
++)
170 nNumber
= xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet
, i
));
171 if (xmlXPathIsNaN(nNumber
))
173 xmlXPathReturnNumber(ctxt
, xmlXPathNAN
);
178 else if (nNumber
< nMinimum
)
181 xmlXPathReturnNumber(ctxt
, nMinimum
);
184 void xforms_maxFunction(xmlXPathParserContextPtr ctxt
, int nargs
)
186 if (nargs
!= 1) XP_ERROR(XPATH_INVALID_ARITY
);
187 xmlNodeSetPtr pNodeSet
= xmlXPathPopNodeSet(ctxt
);
188 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
191 for (int i
= 0; i
< xmlXPathNodeSetGetLength(pNodeSet
); i
++)
193 nNumber
= xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet
, i
));
194 if (xmlXPathIsNaN(nNumber
))
196 xmlXPathReturnNumber(ctxt
, xmlXPathNAN
);
201 else if (nNumber
> nMaximum
)
204 xmlXPathReturnNumber(ctxt
, nMaximum
);
206 void xforms_countNonEmptyFunction(xmlXPathParserContextPtr ctxt
, int nargs
)
208 if (nargs
!= 1) XP_ERROR(XPATH_INVALID_ARITY
);
209 xmlNodeSetPtr pNodeSet
= xmlXPathPopNodeSet(ctxt
);
210 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
212 sal_Int32 nNotEmpty
= 0;
213 for (int i
= 0; i
< xmlXPathNodeSetGetLength(pNodeSet
); i
++)
215 aString
= xmlXPathCastNodeToString(xmlXPathNodeSetItem(pNodeSet
, i
));
216 if (strlen((char*)aString
) > 0) nNotEmpty
++;
218 xmlXPathReturnNumber(ctxt
, nNotEmpty
);
220 void xforms_indexFunction(xmlXPathParserContextPtr
/*ctxt*/, int /*nargs*/)
222 // function index takes a string argument that is the IDREF of a
223 // 'repeat' and returns the current 1-based position of the repeat
224 // index of the identified repeat -- see xforms/9.3.1
226 // doc.getElementByID
231 static const char* _version
= "1.0";
232 static const char* _conformance
= "conformance";
233 void xforms_propertyFunction(xmlXPathParserContextPtr ctxt
, int nargs
)
235 if (nargs
!= 1) XP_ERROR(XPATH_INVALID_ARITY
);
236 xmlChar
* pString
= xmlXPathPopString(ctxt
);
237 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
238 ::rtl::OUString
aString((char*)pString
, strlen((char*)pString
), RTL_TEXTENCODING_UTF8
);
239 if (aString
.equalsIgnoreAsciiCaseAscii("version"))
240 xmlXPathReturnString(ctxt
, (xmlChar
*)_version
);
241 else if (aString
.equalsIgnoreAsciiCaseAscii("conformance-level"))
242 xmlXPathReturnString(ctxt
, (xmlChar
*)_conformance
);
244 xmlXPathReturnEmptyString(ctxt
);
247 // Date and Time Functions
249 static ::rtl::OString
makeDateTimeString (const DateTime
& aDateTime
, sal_Bool bUTC
= sal_True
)
251 ::rtl::OStringBuffer aDateTimeString
;
252 aDateTimeString
.append((sal_Int32
)aDateTime
.GetYear());
253 aDateTimeString
.append("-");
254 if (aDateTime
.GetMonth()<10) aDateTimeString
.append("0");
255 aDateTimeString
.append((sal_Int32
)aDateTime
.GetMonth());
256 aDateTimeString
.append("-");
257 if (aDateTime
.GetDay()<10) aDateTimeString
.append("0");
258 aDateTimeString
.append((sal_Int32
)aDateTime
.GetDay());
259 aDateTimeString
.append("T");
260 if (aDateTime
.GetHour()<10) aDateTimeString
.append("0");
261 aDateTimeString
.append((sal_Int32
)aDateTime
.GetHour());
262 aDateTimeString
.append(":");
263 if (aDateTime
.GetMin()<10) aDateTimeString
.append("0");
264 aDateTimeString
.append((sal_Int32
)aDateTime
.GetMin());
265 aDateTimeString
.append(":");
266 if (aDateTime
.GetSec()<10) aDateTimeString
.append("0");
267 aDateTimeString
.append((sal_Int32
)aDateTime
.GetSec());
268 if (bUTC
) aDateTimeString
.append("Z");
270 return aDateTimeString
.makeStringAndClear();
273 // returns current system date and time in canonical xsd:dateTime
275 void xforms_nowFunction(xmlXPathParserContextPtr ctxt
, int /*nargs*/)
278 A single lexical representation, which is a subset of the lexical representations
279 allowed by [ISO 8601], is allowed for dateTime. This lexical representation is the
280 [ISO 8601] extended format CCYY-MM-DDThh:mm:ss where "CC" represents the century,
281 "YY" the year, "MM" the month and "DD" the day, preceded by an optional leading "-"
282 sign to indicate a negative number. If the sign is omitted, "+" is assumed. The letter
283 "T" is the date/time separator and "hh", "mm", "ss" represent hour, minute and second
288 3.2.7.2 Canonical representation
289 The canonical representation for dateTime is defined by prohibiting certain options
290 from the Lexical representation (par.3.2.7.1). Specifically, either the time zone must
291 be omitted or, if present, the time zone must be Coordinated Universal Time (UTC)
295 ::rtl::OString aDateTimeString
= makeDateTimeString(aDateTime
);
296 xmlChar
*pString
= static_cast<xmlChar
*>(xmlMalloc(aDateTimeString
.getLength()+1));
297 strncpy((char*)pString
, (char*)aDateTimeString
.getStr(), aDateTimeString
.getLength());
298 pString
[aDateTimeString
.getLength()] = 0;
299 xmlXPathReturnString(ctxt
, pString
);
302 static sal_Bool
parseDateTime(const ::rtl::OUString
& aString
, DateTime
& aDateTime
)
304 // take apart a canonical literal xsd:dateTime string
305 //CCYY-MM-DDThh:mm:ss(Z)
307 ::rtl::OUString aDateTimeString
= aString
.trim();
310 if (aDateTimeString
.getLength() < 19 || aDateTimeString
.getLength() > 20)
313 sal_Int32 nDateLength
= 10;
314 sal_Int32 nTimeLength
= 8;
316 ::rtl::OUString aDateTimeSep
= ::rtl::OUString::createFromAscii("T");
317 ::rtl::OUString aDateSep
= ::rtl::OUString::createFromAscii("-");
318 ::rtl::OUString aTimeSep
= ::rtl::OUString::createFromAscii(":");
319 ::rtl::OUString aUTCString
= ::rtl::OUString::createFromAscii("Z");
321 ::rtl::OUString aDateString
= aDateTimeString
.copy(0, nDateLength
);
322 ::rtl::OUString aTimeString
= aDateTimeString
.copy(nDateLength
+1, nTimeLength
);
324 sal_Int32 nIndex
= 0;
325 sal_Int32 nYear
= aDateString
.getToken(0, '-', nIndex
).toInt32();
326 sal_Int32 nMonth
= aDateString
.getToken(0, '-', nIndex
).toInt32();
327 sal_Int32 nDay
= aDateString
.getToken(0, '-', nIndex
).toInt32();
329 sal_Int32 nHour
= aTimeString
.getToken(0, ':', nIndex
).toInt32();
330 sal_Int32 nMinute
= aTimeString
.getToken(0, ':', nIndex
).toInt32();
331 sal_Int32 nSecond
= aTimeString
.getToken(0, ':', nIndex
).toInt32();
333 Date
tmpDate((USHORT
)nDay
, (USHORT
)nMonth
, (USHORT
)nYear
);
334 Time
tmpTime(nHour
, nMinute
, nSecond
);
335 DateTime
tmpDateTime(tmpDate
, tmpTime
);
336 if (aString
.indexOf(aUTCString
) < 0)
337 tmpDateTime
.ConvertToUTC();
339 aDateTime
= tmpDateTime
;
345 void xforms_daysFromDateFunction(xmlXPathParserContextPtr ctxt
, int nargs
)
347 // number of days from 1970-01-01 to supplied xsd:date(Time)
349 if (nargs
!= 1) XP_ERROR(XPATH_INVALID_ARITY
);
350 xmlChar
* pString
= xmlXPathPopString(ctxt
);
351 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
352 ::rtl::OUString
aString((char*)pString
, strlen((char*)pString
), RTL_TEXTENCODING_UTF8
);
355 if (parseDateTime(aString
, aDateTime
))
357 Date
aReferenceDate(1, 1, 1970);
358 sal_Int32 nDays
= aDateTime
- aReferenceDate
;
359 xmlXPathReturnNumber(ctxt
, nDays
);
362 xmlXPathReturnNumber(ctxt
, xmlXPathNAN
);
368 void xforms_secondsFromDateTimeFunction(xmlXPathParserContextPtr ctxt
, int nargs
)
370 // number of seconds from 1970-01-01T00:00:00Z to supplied xsd:date(Time)
372 if (nargs
!= 1) XP_ERROR(XPATH_INVALID_ARITY
);
373 xmlChar
* pString
= xmlXPathPopString(ctxt
);
374 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
375 ::rtl::OUString
aString((char*)pString
, strlen((char*)pString
), RTL_TEXTENCODING_UTF8
);
379 if (parseDateTime(aString
, aDateTime
))
381 Date
aReferenceDate(1, 1, 1970);
382 Time
aReferenceTime(0, 0, 0);
383 sal_Int32 nDays
= aDateTime
- aReferenceDate
;
384 sal_Int32 nSeconds
= nDays
* 24 * 60 * 60;
385 nSeconds
+= aDateTime
.GetHour() * 60 * 60;
386 nSeconds
+= aDateTime
.GetMin() * 60;
387 nSeconds
+= aDateTime
.GetSec();
388 xmlXPathReturnNumber(ctxt
, nSeconds
);
391 xmlXPathReturnNumber(ctxt
, xmlXPathNAN
);
395 static sal_Bool
parseDuration(const xmlChar
* aString
, sal_Bool
& bNegative
, sal_Int32
& nYears
, sal_Int32
& nMonth
, sal_Int32
& nDays
,
396 sal_Int32
& nHours
, sal_Int32
& nMinutes
, sal_Int32
& nSeconds
)
398 sal_Bool bTime
= sal_False
; // in part after T
399 sal_Int32 nLength
= strlen((char*)aString
)+1;
400 char *pString
= (char*)rtl_allocateMemory(nLength
);
401 char *pString0
= pString
;
402 strncpy(pString
, (char*)aString
, nLength
);
404 if (pString
[0] == '-') {
405 bNegative
= sal_True
;
409 if (pString
[0] != 'P')
412 char* pToken
= pString
;
413 while(pToken
[0] != 0)
418 nYears
= atoi(pString
);
424 nMonth
= atoi(pString
);
426 nMinutes
= atoi(pString
);
431 nDays
= atoi(pString
);
436 nHours
= atoi(pString
);
441 nSeconds
= atoi(pString
);
452 rtl_freeMemory(pString0
);
456 void xforms_secondsFuction(xmlXPathParserContextPtr ctxt
, int nargs
)
458 // convert a xsd:duration to seconds
460 if (nargs
!= 1) XP_ERROR(XPATH_INVALID_ARITY
);
461 xmlChar
* pString
= xmlXPathPopString(ctxt
);
462 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
464 sal_Bool bNegative
= sal_False
;
465 sal_Int32 nYears
= 0;
466 sal_Int32 nMonths
= 0;
468 sal_Int32 nHours
= 0;
469 sal_Int32 nMinutes
= 0;
470 sal_Int32 nSeconds
= 0;
472 if (parseDuration(pString
, bNegative
, nYears
, nMonths
, nDays
, nHours
, nMinutes
, nSeconds
))
474 nSeconds
+= nMinutes
*60;
475 nSeconds
+= nHours
*60*60;
476 nSeconds
+= nDays
*24*60*60;
477 // year and month are ignored according to spec
479 nSeconds
= 0 - nSeconds
;
480 xmlXPathReturnNumber(ctxt
, nSeconds
);
483 xmlXPathReturnNumber(ctxt
, xmlXPathNAN
);
486 void xforms_monthsFuction(xmlXPathParserContextPtr ctxt
, int nargs
)
488 // convert a xsd:duration to seconds
490 if (nargs
!= 1) XP_ERROR(XPATH_INVALID_ARITY
);
491 xmlChar
* pString
= xmlXPathPopString(ctxt
);
492 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
494 sal_Bool bNegative
= sal_False
;
495 sal_Int32 nYears
= 0;
496 sal_Int32 nMonths
= 0;
498 sal_Int32 nHours
= 0;
499 sal_Int32 nMinutes
= 0;
500 sal_Int32 nSeconds
= 0;
502 if (parseDuration(pString
, bNegative
, nYears
, nMonths
, nDays
, nHours
, nMinutes
, nSeconds
))
504 nMonths
+= nYears
*12;
505 // Days, Houres, Minutes and seconds are ignored, see spec
507 nMonths
= 0 - nMonths
;
508 xmlXPathReturnNumber(ctxt
, nMonths
);
511 xmlXPathReturnNumber(ctxt
, xmlXPathNAN
);
515 // Node-set Functions
516 void xforms_instanceFuction(xmlXPathParserContextPtr ctxt
, int nargs
)
518 if (nargs
!= 1) XP_ERROR(XPATH_INVALID_ARITY
);
519 xmlChar
*pString
= xmlXPathPopString(ctxt
);
520 if (xmlXPathCheckError(ctxt
)) XP_ERROR(XPATH_INVALID_TYPE
);
521 ::rtl::OUString
aString((char*)pString
, strlen((char*)pString
), RTL_TEXTENCODING_UTF8
);
523 Reference
< XModel
> aModel
= ((CLibxml2XFormsExtension
*)ctxt
->context
->funcLookupData
)->getModel();
526 Reference
< XDocument
> aInstance
= aModel
->getInstanceDocument(aString
);
530 // xmlXPathObjectPtr xmlXPathNewNodeSet (xmlNodePtr val);
531 Reference
< XUnoTunnel
> aTunnel(aInstance
, UNO_QUERY_THROW
);
532 xmlNodePtr pNode
= reinterpret_cast< xmlNodePtr
>( aTunnel
->getSomething(Sequence
< sal_Int8
>()) );
533 xmlXPathObjectPtr pObject
= xmlXPathNewNodeSet(pNode
);
534 xmlXPathReturnNodeSet(ctxt
, pObject
->nodesetval
);
535 } catch (RuntimeException
&)
537 xmlXPathReturnEmptyNodeSet(ctxt
);
541 xmlXPathReturnEmptyNodeSet(ctxt
);
544 xmlXPathReturnEmptyNodeSet(ctxt
);
548 // Node-set Functions, XForms 1.1
549 void xforms_currentFunction(xmlXPathParserContextPtr ctxt
, int nargs
)
551 if (nargs
!= 0) XP_ERROR(XPATH_INVALID_ARITY
);
553 Reference
< XNode
> aNode
= ((CLibxml2XFormsExtension
*)ctxt
->context
->funcLookupData
)->getContextNode();
558 Reference
< XUnoTunnel
> aTunnel(aNode
, UNO_QUERY_THROW
);
559 xmlNodePtr pNode
= reinterpret_cast< xmlNodePtr
>( aTunnel
->getSomething(Sequence
< sal_Int8
>()) );
560 xmlXPathObjectPtr pObject
= xmlXPathNewNodeSet(pNode
);
561 xmlXPathReturnNodeSet(ctxt
, pObject
->nodesetval
);
563 catch (RuntimeException
&)
565 xmlXPathReturnEmptyNodeSet(ctxt
);
569 xmlXPathReturnEmptyNodeSet(ctxt
);