From 7ec7a492ed347346a2ec0f5a25d30a65ebb35c9e 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 --- .../source/xpath/XFormsFunctionCall.cpp | 532 ------------------ .../source/xpath/nsIXFormsUtilityService.h | 131 ----- extensions/xforms/nsXFormsUtilityService.cpp | 246 +++++++- 3 files changed, 231 insertions(+), 678 deletions(-) diff --git a/extensions/transformiix/source/xpath/XFormsFunctionCall.cpp b/extensions/transformiix/source/xpath/XFormsFunctionCall.cpp index 4072fb801c6..e69de29bb2d 100644 --- a/extensions/transformiix/source/xpath/XFormsFunctionCall.cpp +++ b/extensions/transformiix/source/xpath/XFormsFunctionCall.cpp @@ -1,532 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla XForms support. - * - * The Initial Developer of the Original Code is - * IBM Corporation. - * Portions created by the Initial Developer are Copyright (C) 2004 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Aaron Reed - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * XFormsFunctionCall - * A representation of the XPath NodeSet funtions - */ - -#include "FunctionLib.h" -#include "nsAutoPtr.h" -#include "txNodeSet.h" -#include "txAtoms.h" -#include "txIXPathContext.h" -#include "txTokenizer.h" -#include "XFormsFunctions.h" -#include -#include "nsIDOMDocument.h" -#include "nsIDOMDocumentEvent.h" -#include "nsIDOMEvent.h" -#include "nsIDOMEventTarget.h" -#include "nsIDOMElement.h" -#include "nsIXFormsUtilityService.h" -#include "nsServiceManagerUtils.h" // needed for do_GetService? -#include "prprf.h" - -/* - * Creates a XFormsFunctionCall of the given type - */ -XFormsFunctionCall::XFormsFunctionCall(XFormsFunctions aType, nsIDOMNode *aResolverNode) - : mType(aType) - , mResolverNode(aResolverNode) -{ -} - -/* - * Evaluates this Expr based on the given context node and processor state - * @param context the context node for evaluation of this Expr - * @param ps the ContextState containing the stack information needed - * for evaluation - * @return the result of the evaluation - */ -nsresult -XFormsFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult) -{ - *aResult = nsnull; - nsresult rv = NS_OK; - txListIterator iter(¶ms); - - switch (mType) { - case AVG: - { - if (!requireParams(1, 1, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - nsRefPtr nodes; - nsresult rv = evaluateToNodeSet((Expr*)iter.next(), aContext, - getter_AddRefs(nodes)); - NS_ENSURE_SUCCESS(rv, rv); - - double res = 0; - PRInt32 i; - for (i = 0; i < nodes->size(); ++i) { - nsAutoString resultStr; - txXPathNodeUtils::appendNodeValue(nodes->get(i), resultStr); - res += Double::toDouble(resultStr); - } - - if (i > 0) { - res = (res/i); - } - else { - res = Double::NaN; - } - return aContext->recycler()->getNumberResult(res, aResult); - } - case BOOLEANFROMSTRING: - { - if (!requireParams(1, 1, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - PRInt32 retvalue = -1; - nsAutoString booleanValue; - evaluateToString((Expr*)iter.next(), aContext, booleanValue); - - aContext->recycler()->getBoolResult( - booleanValue.EqualsLiteral("1") || - booleanValue.LowerCaseEqualsLiteral("true"), - aResult); - - return NS_OK; - } - case COUNTNONEMPTY: - { - if (!requireParams(1, 1, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - nsRefPtr nodes; - nsresult rv = evaluateToNodeSet((Expr*)iter.next(), aContext, - getter_AddRefs(nodes)); - NS_ENSURE_SUCCESS(rv, rv); - - double res = 0, test = 0; - PRInt32 i, count=0; - for (i = 0; i < nodes->size(); ++i) { - nsAutoString resultStr; - txXPathNodeUtils::appendNodeValue(nodes->get(i), resultStr); - if (!resultStr.IsEmpty()) { - count++; - } - } - - return aContext->recycler()->getNumberResult(count, aResult); - } - case DAYSFROMDATE: - { - if (!requireParams(1, 1, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - return NS_ERROR_NOT_IMPLEMENTED; - } - case IF: - { - if (!requireParams(3, 3, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - PRBool test; - nsAutoString valueToReturn; - test = evaluateToBoolean((Expr*)iter.next(), aContext); - - // grab 'true' value to return - Expr *getvalue = (Expr*)iter.next(); - - if (!test) { - // grab 'false' value to return - getvalue = (Expr*)iter.next(); - } - evaluateToString(getvalue, aContext, valueToReturn); - - return aContext->recycler()->getStringResult(valueToReturn, aResult); - } - case INDEX: - { - // Given an element's id as the parameter, need to query the element and - // make sure that it is a xforms:repeat node. Given that, must query - // its index. - if (!requireParams(1, 1, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - nsAutoString indexId; - evaluateToString((Expr*)iter.next(), aContext, indexId); - - // here document is the XForms document - nsCOMPtr document; - rv = mResolverNode->GetOwnerDocument(getter_AddRefs(document)); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(document, NS_ERROR_NULL_POINTER); - - // indexId should be the id of a nsIXFormsRepeatElement - nsCOMPtr repeatEle; - rv = document->GetElementById(indexId, getter_AddRefs(repeatEle)); - NS_ENSURE_SUCCESS(rv, rv); - - // now get the index value from the xforms:repeat. Need to use the - // service to do this work so that we don't have dependencies in - // transformiix on XForms. - nsCOMPtrxformsService = - do_GetService("@mozilla.org/xforms-utility-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 index; - rv = xformsService->GetRepeatIndex(repeatEle, &index); - NS_ENSURE_SUCCESS(rv, rv); - - return aContext->recycler()->getNumberResult(index, aResult); - - } - case INSTANCE: - { - nsresult rv; - if (!requireParams(1, 1, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - nsRefPtr resultSet; - rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet)); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString instanceId; - evaluateToString((Expr*)iter.next(), aContext, instanceId); - - // here document is the XForms document - nsCOMPtr document; - rv = mResolverNode->GetOwnerDocument(getter_AddRefs(document)); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(document, NS_ERROR_NULL_POINTER); - - nsCOMPtr instEle; - rv = document->GetElementById(instanceId, getter_AddRefs(instEle)); - - PRBool foundInstance = PR_FALSE; - nsAutoString localname, namespaceURI; - if (instEle) { - instEle->GetLocalName(localname); - instEle->GetNamespaceURI(namespaceURI); - if (localname.EqualsLiteral("instance") && - namespaceURI.EqualsLiteral(NS_NAMESPACE_XFORMS)) { - foundInstance = PR_TRUE; - } - } - - if (!foundInstance) { - // We didn't find an instance element with the given id. Return the - // empty result set. - *aResult = resultSet; - NS_ADDREF(*aResult); - - return NS_OK; - } - - // Make sure that this element is contained in the same - // model as the context node of the expression as per - // the XForms 1.0 spec. - - // first step is to get the contextNode passed in to - // the evaluation - - nsCOMPtr xfContextNode; - rv = txXPathNativeNode::getNode(aContext->getContextNode(), - getter_AddRefs(xfContextNode)); - NS_ENSURE_SUCCESS(rv, rv); - - // now see if the node we found (instEle) and the - // context node for the evaluation (xfContextNode) link - // back to the same model. - nsCOMPtrxformsService = - do_GetService("@mozilla.org/xforms-utility-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr instNode, modelInstance; - instNode = do_QueryInterface(instEle); - rv = xformsService->GetModelFromNode(instNode, - getter_AddRefs(modelInstance)); - - NS_ENSURE_SUCCESS(rv, rv); - - PRBool modelContainsNode = PR_FALSE; - rv = xformsService->IsNodeAssocWithModel(xfContextNode, - modelInstance, - &modelContainsNode); - NS_ENSURE_SUCCESS(rv, rv); - - if (modelContainsNode) { - // ok, we've found an instance node with the proper id - // that fulfills the requirement of being from the - // same model as the context node. Now we need to - // return a 'node-set containing just the root - // element node of the referenced instance data'. - // Wonderful. - - nsCOMPtr instanceRoot; - rv = xformsService->GetInstanceDocumentRoot( - instanceId, - modelInstance, - getter_AddRefs(instanceRoot)); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(instanceRoot, NS_ERROR_NULL_POINTER); - - nsAutoPtr txNode(txXPathNativeNode::createXPathNode(instanceRoot)); - if (txNode) { - resultSet->add(*txNode); - } - } - - - // XXX where we need to do the work - // if (walker.moveToElementById(instanceId)) { - // resultSet->add(walker.getCurrentPosition()); - // } - - *aResult = resultSet; - NS_ADDREF(*aResult); - - return NS_OK; - } - case MAX: - { - if (!requireParams(1, 1, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - nsRefPtr nodes; - nsresult rv = evaluateToNodeSet((Expr*)iter.next(), aContext, - getter_AddRefs(nodes)); - NS_ENSURE_SUCCESS(rv, rv); - - double res = Double::NaN; - PRInt32 i; - for (i = 0; i < nodes->size(); ++i) { - double test; - nsAutoString resultStr; - txXPathNodeUtils::appendNodeValue(nodes->get(i), resultStr); - test = Double::toDouble(resultStr); - if (Double::isNaN(test)) { - res = Double::NaN; - break; - } - if (test > res || i == 0) { - res = test; - } - } - - return aContext->recycler()->getNumberResult(res, aResult); - } - case MIN: - { - if (!requireParams(1, 1, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - nsRefPtr nodes; - nsresult rv = evaluateToNodeSet((Expr*)iter.next(), aContext, - getter_AddRefs(nodes)); - NS_ENSURE_SUCCESS(rv, rv); - - double res = Double::NaN; - PRInt32 i; - for (i = 0; i < nodes->size(); ++i) { - double test; - nsAutoString resultStr; - txXPathNodeUtils::appendNodeValue(nodes->get(i), resultStr); - test = Double::toDouble(resultStr); - if (Double::isNaN(test)) { - res = Double::NaN; - break; - } - if ((test < res) || (i==0)) { - res = test; - } - } - - return aContext->recycler()->getNumberResult(res, aResult); - } - case MONTHS: - { - if (!requireParams(1, 1, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - return NS_ERROR_NOT_IMPLEMENTED; - } - case NOW: - { - if (!requireParams(0, 0, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - PRExplodedTime time; - char ctime[60]; - - PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &time); - int gmtoffsethour = time.tm_params.tp_gmt_offset < 0 ? - -1*time.tm_params.tp_gmt_offset / 3600 : - time.tm_params.tp_gmt_offset / 3600; - int remainder = time.tm_params.tp_gmt_offset%3600; - int gmtoffsetminute = remainder ? remainder/60 : 00; - - char zone_location[40]; - const int zoneBufSize = sizeof(zone_location); - PR_snprintf(zone_location, zoneBufSize, "%c%02d:%02d\0", - time.tm_params.tp_gmt_offset < 0 ? '-' : '+', - gmtoffsethour, gmtoffsetminute); - - PR_FormatTime(ctime, sizeof(ctime), "%Y-%m-%dT%H:%M:%S\0", &time); - nsString sTime = NS_ConvertASCIItoUTF16(ctime) + NS_ConvertASCIItoUTF16(zone_location); - - return aContext->recycler()->getStringResult(sTime, aResult); - } - case PROPERTY: - { - if (!requireParams(1, 1, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - nsAutoString property; - evaluateToString((Expr*)iter.next(), aContext, property); - - // This function can handle "version" and "conformance-level" - // which is all that the XForms 1.0 spec is worried about - if (property.Equals(NS_LITERAL_STRING("version"))) - property.Assign(NS_LITERAL_STRING("1.0")); - else if (property.Equals(NS_LITERAL_STRING("conformance-level"))) - property.Assign(NS_LITERAL_STRING("basic")); - - return aContext->recycler()->getStringResult(property, aResult); - } - case SECONDS: - { - double dbl=0; - if (!requireParams(1, 1, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - return NS_ERROR_NOT_IMPLEMENTED; - - } - case SECONDSFROMDATETIME: - { - if (!requireParams(1, 1, aContext)) - return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; - - return NS_ERROR_NOT_IMPLEMENTED; - } - } /* switch() */ - - aContext->receiveError(NS_LITERAL_STRING("Internal error"), - NS_ERROR_UNEXPECTED); - return NS_ERROR_UNEXPECTED; -} - -#ifdef TX_TO_STRING -nsresult -XFormsFunctionCall::getNameAtom(nsIAtom** aAtom) -{ - switch (mType) { - case AVG: - { - *aAtom = txXPathAtoms::avg; - break; - } - case BOOLEANFROMSTRING: - { - *aAtom = txXPathAtoms::booleanFromString; - break; - } - case COUNTNONEMPTY: - { - *aAtom = txXPathAtoms::countNonEmpty; - break; - } - case DAYSFROMDATE: - { - *aAtom = txXPathAtoms::daysFromDate; - break; - } - case IF: - { - *aAtom = txXPathAtoms::ifFunc; - break; - } - case INDEX: - { - *aAtom = txXPathAtoms::index; - break; - } - case INSTANCE: - { - *aAtom = txXPathAtoms::instance; - break; - } - case MAX: - { - *aAtom = txXPathAtoms::max; - break; - } - case MIN: - { - *aAtom = txXPathAtoms::min; - break; - } - case MONTHS: - { - *aAtom = txXPathAtoms::months; - break; - } - case NOW: - { - *aAtom = txXPathAtoms::now; - break; - } - case PROPERTY: - { - *aAtom = txXPathAtoms::property; - break; - } - case SECONDS: - { - *aAtom = txXPathAtoms::seconds; - break; - } - case SECONDSFROMDATETIME: - { - *aAtom = txXPathAtoms::secondsFromDateTime; - break; - } - default: - { - *aAtom = 0; - return NS_ERROR_FAILURE; - } - } - NS_ADDREF(*aAtom); - return NS_OK; -} -#endif diff --git a/extensions/transformiix/source/xpath/nsIXFormsUtilityService.h b/extensions/transformiix/source/xpath/nsIXFormsUtilityService.h index 3f19241ab85..e69de29bb2d 100644 --- a/extensions/transformiix/source/xpath/nsIXFormsUtilityService.h +++ b/extensions/transformiix/source/xpath/nsIXFormsUtilityService.h @@ -1,131 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla XForms support. - * - * The Initial Developer of the Original Code is - * IBM Corporation. - * Portions created by the Initial Developer are Copyright (C) 2004 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Aaron Reed - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef nsIXFormsUtilityService_h -#define nsIXFormsUtilityService_h - - -#include "nsISupports.h" - -/* For IDL files that don't want to include root IDL files. */ -#ifndef NS_NO_VTABLE -#define NS_NO_VTABLE -#endif -class nsIDOMNode; /* forward declaration */ - -class nsIXFormsModelElement; /* forward declaration */ - - -/* starting interface: nsIXFormsUtilityService */ -#define NS_IXFORMSUTILITYSERVICE_IID_STR "4a744a59-8771-4065-959d-b8de3dad81da" - -#define NS_IXFORMSUTILITYSERVICE_IID \ - {0x4a744a59, 0x8771, 0x4065, \ - { 0x95, 0x9d, 0xb8, 0xde, 0x3d, 0xad, 0x81, 0xda }} - -#define NS_XFORMS_UTILITY_CONTRACTID "@mozilla.org/xforms-utility-service;1" - -/* Use this macro when declaring classes that implement this interface. */ -#define NS_DECL_NSIXFORMSUTILITYSERVICE \ - NS_IMETHOD GetModelFromNode(nsIDOMNode *node, nsIDOMNode **_retval); \ - 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); - -/** - * Private interface implemented by the nsXFormsUtilityService in XForms extension. - * Defining it here to prevent XPath requiring XForms extension. - */ -class NS_NO_VTABLE nsIXFormsUtilityService : public nsISupports { - public: - - NS_DEFINE_STATIC_IID_ACCESSOR(NS_IXFORMSUTILITYSERVICE_IID) - - /** - * Function to get the corresponding model element from a xforms node or - * a xforms instance data node. - */ - /* nsIDOMNode getModelFromNode (in nsIDOMNode node); */ - NS_IMETHOD GetModelFromNode(nsIDOMNode *node, nsIDOMNode **_retval) = 0; - - /** - * Function to see if the given node is associated with the given model. - * Right now this function is only called by XPath in the case of the - * instance() function. - * The provided node can be an instance node from an instance - * document and thus be associated to the model in that way (model elements - * contain instance elements). Otherwise the node will be an XForms element - * that was used as the context node of the XPath expression (i.e the - * XForms control has an attribute that contains an XPath expression). - * Form controls are associated with model elements either explicitly through - * single-node binding or implicitly (if model cannot by calculated, it - * will use the first model element encountered in the document). The model - * can also be inherited from a containing element like xforms:group or - * xforms:repeat. - */ - /* PRBool isNodeAssocWithModel (in nsIDOMNode aNode, in nsIDOMNode aModel); */ - NS_IMETHOD IsNodeAssocWithModel(nsIDOMNode *aNode, nsIDOMNode *aModel, PRBool *_retval) = 0; - - /** - * Function to get the instance document root for the instance element with - * the given id. The instance element must be associated with the given - * model. - */ - /* nsIDOMNode getInstanceDocumentRoot (in DOMString aID, in nsIDOMNode aModelNode); */ - NS_IMETHOD GetInstanceDocumentRoot(const nsAString & aID, nsIDOMNode *aModelNode, nsIDOMNode **_retval) = 0; - - /** - * Function to ensure that aValue is of the schema type aType. Will basically - * be a forwarder to the nsISchemaValidator function of the same name. - */ - /* boolean validateString (in AString aValue, in AString aType, in AString aNamespace); */ - NS_IMETHOD ValidateString(const nsAString & aValue, const nsAString & aType, const nsAString & aNamespace, PRBool *_retval) = 0; - - /** - * Function to retrieve the index from the given repeat element. - */ - /* unsigned long getRepeatIndex (in nsIDOMNode aRepeat); */ - NS_IMETHOD GetRepeatIndex(nsIDOMNode *aRepeat, PRUint32 *aIndex) = 0; - -}; - -#define NS_ERROR_XFORMS_CALCUATION_EXCEPTION \ - NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_GENERAL, 3001) - -#endif /* nsIXFormsUtilityService_h */ diff --git a/extensions/xforms/nsXFormsUtilityService.cpp b/extensions/xforms/nsXFormsUtilityService.cpp index 847b0ae9abc..5c016953c3c 100644 --- a/extensions/xforms/nsXFormsUtilityService.cpp +++ b/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; +} +