From 74eb5605cf34bc9b6cf0693077dc4e75c72559bd Mon Sep 17 00:00:00 2001 From: "doronr%us.ibm.com" Date: Thu, 4 Aug 2005 21:15:37 +0000 Subject: [PATCH] Bug 281987 - Finish out XPath extension functions. Patch by aaronr, r=sicking sr=tor a=mkaply git-svn-id: svn://10.0.0.236/trunk@177122 18797224-902f-48f8-a5cc-f745e15eee43 --- .../source/xpath/XFormsFunctionCall.cpp | 82 +++++- .../source/xpath/nsIXFormsUtilityService.h | 35 ++- .../xforms/nsXFormsUtilityService.cpp | 246 ++++++++++++++++-- 3 files changed, 342 insertions(+), 21 deletions(-) diff --git a/mozilla/extensions/transformiix/source/xpath/XFormsFunctionCall.cpp b/mozilla/extensions/transformiix/source/xpath/XFormsFunctionCall.cpp index 4072fb801c6..7fe1d53fc58 100644 --- a/mozilla/extensions/transformiix/source/xpath/XFormsFunctionCall.cpp +++ b/mozilla/extensions/transformiix/source/xpath/XFormsFunctionCall.cpp @@ -151,7 +151,26 @@ XFormsFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult) if (!requireParams(1, 1, aContext)) return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - return NS_ERROR_NOT_IMPLEMENTED; + nsAutoString date; + evaluateToString((Expr*)iter.next(), aContext, date); + + nsCOMPtrxformsService = + do_GetService("@mozilla.org/xforms-utility-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 result = 0; + double res = Double::NaN; + nsresult rv = xformsService->GetDaysFromDateTime(date, &result); + if (NS_SUCCEEDED(rv)) { + res = result; + } + else if (rv != NS_ERROR_ILLEGAL_VALUE) { + // if we failed for a reason other than the parameter value, pass that + // up the chain + return rv; + } + + return aContext->recycler()->getNumberResult(res, aResult); } case IF: { @@ -377,7 +396,26 @@ XFormsFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult) if (!requireParams(1, 1, aContext)) return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - return NS_ERROR_NOT_IMPLEMENTED; + nsAutoString duration; + evaluateToString((Expr*)iter.next(), aContext, duration); + + nsCOMPtrxformsService = + do_GetService("@mozilla.org/xforms-utility-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 result = 0; + double res = Double::NaN; + nsresult rv = xformsService->GetMonths(duration, &result); + if (NS_SUCCEEDED(rv)) { + res = result; + } + else if (rv != NS_ERROR_ILLEGAL_VALUE) { + // if we failed for a reason other than the parameter value, pass that + // up the chain + return rv; + } + + return aContext->recycler()->getNumberResult(res, aResult); } case NOW: { @@ -424,19 +462,53 @@ XFormsFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult) } case SECONDS: { - double dbl=0; if (!requireParams(1, 1, aContext)) return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - return NS_ERROR_NOT_IMPLEMENTED; + nsAutoString duration; + evaluateToString((Expr*)iter.next(), aContext, duration); + nsCOMPtrxformsService = + do_GetService("@mozilla.org/xforms-utility-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + double res; + nsresult rv = xformsService->GetSeconds(duration, &res); + if (NS_FAILED(rv)) { + if (rv != NS_ERROR_ILLEGAL_VALUE) { + // if we failed for a reason other than the parameter value, pass that + // up the chain + return rv; + } + res = Double::NaN; + } + + return aContext->recycler()->getNumberResult(res, aResult); } case SECONDSFROMDATETIME: { if (!requireParams(1, 1, aContext)) return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - return NS_ERROR_NOT_IMPLEMENTED; + nsAutoString dateTime; + evaluateToString((Expr*)iter.next(), aContext, dateTime); + + nsCOMPtrxformsService = + do_GetService("@mozilla.org/xforms-utility-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + double res; + nsresult rv = xformsService->GetSecondsFromDateTime(dateTime, &res); + if (NS_FAILED(rv)) { + if (rv != NS_ERROR_ILLEGAL_VALUE) { + // if we failed for a reason other than the parameter value, pass that + // up the chain + return rv; + } + res = Double::NaN; + } + + return aContext->recycler()->getNumberResult(res, aResult); } } /* switch() */ diff --git a/mozilla/extensions/transformiix/source/xpath/nsIXFormsUtilityService.h b/mozilla/extensions/transformiix/source/xpath/nsIXFormsUtilityService.h index 3f19241ab85..a827b8be6e1 100644 --- a/mozilla/extensions/transformiix/source/xpath/nsIXFormsUtilityService.h +++ b/mozilla/extensions/transformiix/source/xpath/nsIXFormsUtilityService.h @@ -66,7 +66,11 @@ class nsIXFormsModelElement; /* forward declaration */ NS_IMETHOD IsNodeAssocWithModel(nsIDOMNode *aNode, nsIDOMNode *aModel, PRBool *_retval); \ NS_IMETHOD GetInstanceDocumentRoot(const nsAString & aID, nsIDOMNode *aModelNode, nsIDOMNode **_retval); \ NS_IMETHOD ValidateString(const nsAString & aValue, const nsAString & aType, const nsAString & aNamespace, PRBool *_retval); \ - NS_IMETHOD GetRepeatIndex(nsIDOMNode *aRepeat, PRUint32 *aIndex); + NS_IMETHOD GetRepeatIndex(nsIDOMNode *aRepeat, PRUint32 *aIndex); \ + NS_IMETHOD GetMonths(const nsAString & aValue, PRInt32 *aMonths); \ + NS_IMETHOD GetSeconds(const nsAString & aValue, double *aSeconds); \ + NS_IMETHOD GetSecondsFromDateTime(const nsAString & aValue, double *aSeconds); \ + NS_IMETHOD GetDaysFromDateTime(const nsAString & aValue, PRInt32 *aDays); /** * Private interface implemented by the nsXFormsUtilityService in XForms extension. @@ -123,6 +127,35 @@ class NS_NO_VTABLE nsIXFormsUtilityService : public nsISupports { /* unsigned long getRepeatIndex (in nsIDOMNode aRepeat); */ NS_IMETHOD GetRepeatIndex(nsIDOMNode *aRepeat, PRUint32 *aIndex) = 0; + /** + * Function to retrieve the number of months represented by the + * xsd:duration provided in aValue + */ + /* long getMonths (in DOMString aValue); */ + NS_IMETHOD GetMonths(const nsAString & aValue, PRInt32 *aMonths) = 0; + + /** + * Function to retrieve the number of seconds represented by the + * xsd:duration provided in aValue + */ + /* AString getSeconds (in DOMString aValue); */ + NS_IMETHOD GetSeconds(const nsAString & aValue, double *aSeconds) = 0; + + /** + * Function to retrieve the number of seconds represented by the + * xsd:dateTime provided in aValue + */ + /* AString getSecondsFromDateTime (in DOMString aValue); */ + NS_IMETHOD GetSecondsFromDateTime(const nsAString & aValue, + double *aSeconds) = 0; + + /** + * Function to retrieve the number of days represented by the + * xsd:dateTime provided in aValue + */ + /* AString getDaysFromDateTime (in DOMString aValue); */ + NS_IMETHOD GetDaysFromDateTime(const nsAString & aValue, + PRInt32 * aDays) = 0; }; #define NS_ERROR_XFORMS_CALCUATION_EXCEPTION \ diff --git a/mozilla/extensions/xforms/nsXFormsUtilityService.cpp b/mozilla/extensions/xforms/nsXFormsUtilityService.cpp index 847b0ae9abc..5c016953c3c 100644 --- a/mozilla/extensions/xforms/nsXFormsUtilityService.cpp +++ b/mozilla/extensions/xforms/nsXFormsUtilityService.cpp @@ -36,6 +36,7 @@ * * ***** END LICENSE BLOCK ***** */ +#include "nsIServiceManager.h" #include "nsXFormsUtilityService.h" #include "nsXFormsUtils.h" #include "nsIXTFElement.h" @@ -47,12 +48,22 @@ #include "nsIDOMNodeList.h" #include "nsIInstanceElementPrivate.h" #include "nsIXFormsRepeatElement.h" +#include "nsISchemaValidator.h" +#include "nsISchemaDuration.h" +#include "nsXFormsSchemaValidator.h" +#include "prdtoa.h" NS_IMPL_ISUPPORTS1(nsXFormsUtilityService, nsIXFormsUtilityService) +/* I don't know why Doron didn't put this in the .idl so that it could be added + * to the generated .h file. Put it here for now + */ +#define NS_SCHEMAVALIDATOR_CONTRACTID "@mozilla.org/schemavalidator;1" + + NS_IMETHODIMP nsXFormsUtilityService::GetModelFromNode(nsIDOMNode *aNode, - nsIDOMNode **aModel) + nsIDOMNode **aModel) { nsCOMPtr element = do_QueryInterface(aNode); NS_ASSERTION(aModel, "no return buffer, we'll crash soon"); @@ -163,8 +174,8 @@ nsXFormsUtilityService::IsNodeAssocWithModel( nsIDOMNode *aNode, NS_IMETHODIMP nsXFormsUtilityService::GetInstanceDocumentRoot(const nsAString& aID, - nsIDOMNode *aModelNode, - nsIDOMNode **aInstanceRoot) + nsIDOMNode *aModelNode, + nsIDOMNode **aInstanceRoot) { nsresult rv = NS_ERROR_FAILURE; NS_ASSERTION(aInstanceRoot, "no return buffer, we'll crash soon"); @@ -195,22 +206,20 @@ nsXFormsUtilityService::GetInstanceDocumentRoot(const nsAString& aID, */ NS_IMETHODIMP nsXFormsUtilityService::ValidateString(const nsAString & aValue, - const nsAString & aType, - const nsAString & aNamespace, - PRBool *aResult) + const nsAString & aType, + const nsAString & aNamespace, + PRBool *aResult) { - // XXX TODO This function needs to call the XForms validator layer from - // bug 274083 when it goes into the build. + NS_ASSERTION(aResult, "no return buffer for result so we'll crash soon"); + *aResult = PR_FALSE; -#if 0 - nsresult rv = NS_ERROR_FAILURE; nsXFormsSchemaValidator *validator = new nsXFormsSchemaValidator(); - *aResult = validator->ValidateString(aValue, aType, aNamespace); - return rv; -#endif - - return NS_ERROR_NOT_IMPLEMENTED; + if (validator) { + *aResult = validator->ValidateString(aValue, aType, aNamespace); + delete validator; + } + return *aResult ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP @@ -225,3 +234,210 @@ nsXFormsUtilityService::GetRepeatIndex(nsIDOMNode *aRepeat, PRUint32 *aIndex) /// @bug This should somehow end up in a NaN per the XForms 1.0 Errata (XXX) return repeatEle ? repeatEle->GetIndex(aIndex) : NS_OK; } + +NS_IMETHODIMP +nsXFormsUtilityService::GetMonths(const nsAString & aValue, + PRInt32 * aMonths) +{ + NS_ASSERTION(aMonths, "no return buffer for months, we'll crash soon"); + + *aMonths = 0; + nsCOMPtr duration; + nsCOMPtr schemaValidator = + do_GetService(NS_SCHEMAVALIDATOR_CONTRACTID); + NS_ENSURE_TRUE(schemaValidator, NS_ERROR_FAILURE); + + nsresult rv = schemaValidator->ValidateBuiltinTypeDuration(aValue, + getter_AddRefs(duration)); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 sumMonths; + PRUint32 years; + PRUint32 months; + + duration->GetYears(&years); + duration->GetMonths(&months); + + sumMonths = months + years*12; + PRBool negative; + duration->GetNegative(&negative); + if (negative) { + // according to the spec, "the sign of the result will match the sign + // of the duration" + sumMonths *= -1; + } + + *aMonths = sumMonths; + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsUtilityService::GetSeconds(const nsAString & aValue, + double * aSeconds) +{ + nsCOMPtr duration; + nsCOMPtr schemaValidator = + do_GetService(NS_SCHEMAVALIDATOR_CONTRACTID); + NS_ENSURE_TRUE(schemaValidator, NS_ERROR_FAILURE); + + nsresult rv = schemaValidator->ValidateBuiltinTypeDuration(aValue, + getter_AddRefs(duration)); + NS_ENSURE_SUCCESS(rv, rv); + double sumSeconds; + PRUint32 days; + PRUint32 hours; + PRUint32 minutes; + PRUint32 seconds; + double fractSecs; + + duration->GetDays(&days); + duration->GetHours(&hours); + duration->GetMinutes(&minutes); + duration->GetSeconds(&seconds); + duration->GetFractionSeconds(&fractSecs); + + sumSeconds = seconds + minutes*60 + hours*3600 + days*24*3600 + fractSecs; + + PRBool negative; + duration->GetNegative(&negative); + if (negative) { + // according to the spec, "the sign of the result will match the sign + // of the duration" + sumSeconds *= -1; + } + + *aSeconds = sumSeconds; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsUtilityService::GetSecondsFromDateTime(const nsAString & aValue, + double * aSeconds) +{ + PRTime dateTime; + nsCOMPtr schemaValidator = + do_GetService(NS_SCHEMAVALIDATOR_CONTRACTID); + NS_ENSURE_TRUE(schemaValidator, NS_ERROR_FAILURE); + + nsresult rv = schemaValidator->ValidateBuiltinTypeDateTime(aValue, &dateTime); + NS_ENSURE_SUCCESS(rv, rv); + + PRTime secs64 = dateTime, remain64; + PRInt64 usecPerSec; + PRInt32 secs32, remain32; + + // convert from PRTime (microseconds from epoch) to seconds. + LL_I2L(usecPerSec, PR_USEC_PER_SEC); + LL_MOD(remain64, secs64, usecPerSec); /* remainder after conversion */ + LL_DIV(secs64, secs64, usecPerSec); /* Conversion in whole seconds */ + + // convert whole seconds and remainder to PRInt32 + LL_L2I(secs32, secs64); + LL_L2I(remain32, remain64); + + // ready the result to send back to transformiix land now in case there are + // no fractional seconds or we end up having a problem parsing them out. If + // we do, we'll just ignore the fractional seconds. + double totalSeconds = secs32; + *aSeconds = totalSeconds; + + // We're not getting fractional seconds back in the PRTime we get from + // the schemaValidator. We'll have to figure out the fractions from + // the original value. Since ValidateBuiltinTypeDateTime returned + // successful for us to get this far, we know that the value is in + // the proper format. + int findFractionalSeconds = aValue.FindChar('.'); + if (findFractionalSeconds < 0) { + // no fractions of seconds, so we are good to go as we are + return NS_OK; + } + + const nsAString& fraction = Substring(aValue, findFractionalSeconds+1, + aValue.Length()); + + PRBool done = PR_FALSE; + PRUnichar currentChar; + nsCAutoString fractionResult; + nsAString::const_iterator start, end, buffStart; + fraction.BeginReading(start); + fraction.BeginReading(buffStart); + fraction.EndReading(end); + + while ((start != end) && !done) { + currentChar = *start++; + + // Time is usually terminated with Z or followed by a time zone + // (i.e. -05:00). Time can also be terminated by the end of the string, so + // test for that as well. All of this specified at: + // http://www.w3.org/TR/xmlschema-2/#dateTime + if ((currentChar == 'Z') || (currentChar == '+') || (currentChar == '-') || + (start == end)) { + fractionResult.AssignLiteral("0."); + AppendUTF16toUTF8(Substring(buffStart.get(), start.get()-1), + fractionResult); + } else if ((currentChar > '9') || (currentChar < '0')) { + // has to be a numerical character or else abort. This should have been + // caught by the schemavalidator, but it is worth double checking. + done = PR_TRUE; + } + } + + if (fractionResult.IsEmpty()) { + // couldn't successfully parse the fractional seconds, so we'll just return + // without them. + return NS_OK; + } + + // convert the result string that we have to a double and add it to the total + totalSeconds += PR_strtod(fractionResult.get(), nsnull); + *aSeconds = totalSeconds; + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsUtilityService::GetDaysFromDateTime(const nsAString & aValue, + PRInt32 * aDays) +{ + NS_ASSERTION(aDays, "no return buffer for days, we'll crash soon"); + *aDays = 0; + + PRTime date; + nsCOMPtr schemaValidator = + do_GetService(NS_SCHEMAVALIDATOR_CONTRACTID); + NS_ENSURE_TRUE(schemaValidator, NS_ERROR_FAILURE); + + // aValue could be a xsd:date or a xsd:dateTime. If it is a dateTime, we + // should ignore the hours, minutes, and seconds according to 7.10.2 in + // the spec. So search for such things now. If they are there, strip 'em. + int findTime = aValue.FindChar('T'); + + nsAutoString dateString; + dateString.Assign(aValue); + if (findTime >= 0) { + dateString.Assign(Substring(dateString, 0, findTime)); + } + + nsresult rv = schemaValidator->ValidateBuiltinTypeDate(dateString, &date); + NS_ENSURE_SUCCESS(rv, rv); + + PRTime secs64 = date; + PRInt64 usecPerSec; + PRInt32 secs32; + + // convert from PRTime (microseconds from epoch) to seconds. Shouldn't + // have to worry about remainders since input is a date. Smallest value + // is in whole days. + LL_I2L(usecPerSec, PR_USEC_PER_SEC); + LL_DIV(secs64, secs64, usecPerSec); + + // convert whole seconds to PRInt32 + LL_L2I(secs32, secs64); + + // convert whole seconds to days. 86400 seconds in a day. + *aDays = secs32/86400; + + return NS_OK; +} +