update dev300-m58
[ooovba.git] / forms / source / xforms / xpathlib / xpathlib.cxx
bloba4dfbe3b08e6775b9a0a85e3b8007914dfac964e
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 $
10 * $Revision: 1.8 $
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"
34 #include <string.h>
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"
57 // C interface
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;
98 else
99 return NULL;
102 // boolean functions
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);
113 else
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);
128 if (aBool)
129 xmlXPathReturnString(ctxt, s1);
130 else
131 xmlXPathReturnString(ctxt, s2);
135 // Number Functions
136 void xforms_avgFunction(xmlXPathParserContextPtr ctxt, int nargs)
138 // use sum(), div() and count()
139 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
141 // save nodeset
142 xmlXPathObjectPtr pObject = valuePop(ctxt);
143 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
144 //push back a copy
145 valuePush(ctxt, xmlXPathObjectCopy(pObject));
146 // get the Sum
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);
166 double nMinimum = 0;
167 double nNumber = 0;
168 for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
170 nNumber = xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet, i));
171 if (xmlXPathIsNaN(nNumber))
173 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
174 return;
176 if (i == 0)
177 nMinimum = nNumber;
178 else if (nNumber < nMinimum)
179 nMinimum = nNumber;
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);
189 double nMaximum = 0;
190 double nNumber = 0;
191 for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
193 nNumber = xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet, i));
194 if (xmlXPathIsNaN(nNumber))
196 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
197 return;
199 if (i == 0)
200 nMaximum = nNumber;
201 else if (nNumber > nMaximum)
202 nMaximum = nNumber;
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);
211 xmlChar *aString;
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
227 // (...)
230 // String Functions
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);
243 else
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
274 // format
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
284 respectively.
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)
292 indicated by a "Z".
294 DateTime aDateTime;
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();
309 // check length
310 if (aDateTimeString.getLength() < 19 || aDateTimeString.getLength() > 20)
311 return sal_False;
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();
328 nIndex = 0;
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;
341 return sal_True;
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);
354 DateTime aDateTime;
355 if (parseDateTime(aString, aDateTime))
357 Date aReferenceDate(1, 1, 1970);
358 sal_Int32 nDays = aDateTime - aReferenceDate;
359 xmlXPathReturnNumber(ctxt, nDays);
361 else
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);
377 DateTime aDateTime;
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);
390 else
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;
406 pString++;
409 if (pString[0] != 'P')
410 return sal_False;
411 pString++;
412 char* pToken = pString;
413 while(pToken[0] != 0)
415 switch(pToken[0]) {
416 case 'Y':
417 pToken[0] = 0;
418 nYears = atoi(pString);
419 pString = ++pToken;
420 break;
421 case 'M':
422 pToken[0] = 0;
423 if (!bTime)
424 nMonth = atoi(pString);
425 else
426 nMinutes = atoi(pString);
427 pString = ++pToken;
428 break;
429 case 'D':
430 pToken[0] = 0;
431 nDays = atoi(pString);
432 pString = ++pToken;
433 break;
434 case 'H':
435 pToken[0] = 0;
436 nHours = atoi(pString);
437 pString = ++pToken;
438 break;
439 case 'S':
440 pToken[0] = 0;
441 nSeconds = atoi(pString);
442 pString = ++pToken;
443 break;
444 case 'T':
445 bTime = sal_True;
446 pString = ++pToken;
447 break;
448 default:
449 pToken++;
452 rtl_freeMemory(pString0);
453 return sal_True;
456 void xforms_secondsFuction(xmlXPathParserContextPtr ctxt, int nargs)
458 // convert a xsd:duration to seconds
459 // (-)PnYnMnDTnHnMnS
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;
467 sal_Int32 nDays = 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
478 if (bNegative)
479 nSeconds = 0 - nSeconds;
480 xmlXPathReturnNumber(ctxt, nSeconds);
482 else
483 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
486 void xforms_monthsFuction(xmlXPathParserContextPtr ctxt, int nargs)
488 // convert a xsd:duration to seconds
489 // (-)PnYnMnDTnHnMnS
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;
497 sal_Int32 nDays = 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
506 if (bNegative)
507 nMonths = 0 - nMonths;
508 xmlXPathReturnNumber(ctxt, nMonths);
510 else
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();
524 if (aModel.is())
526 Reference< XDocument > aInstance = aModel->getInstanceDocument(aString);
527 if (aInstance.is())
529 try {
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);
540 else
541 xmlXPathReturnEmptyNodeSet(ctxt);
543 else
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();
555 if (aNode.is())
557 try {
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);
568 else
569 xmlXPathReturnEmptyNodeSet(ctxt);