1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is Neil Deakin
18 * Portions created by the Initial Developer are Copyright (C) 2005
19 * the Initial Developer. All Rights Reserved.
22 * Laurent Jouanneau <laurent.jouanneau@disruptive-innovations.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
40 #include "nsIDOMNodeList.h"
41 #include "nsUnicharUtils.h"
43 #include "nsArrayUtils.h"
44 #include "nsIVariant.h"
45 #include "nsAppDirectoryServiceDefs.h"
48 #include "nsIIOService.h"
49 #include "nsIFileChannel.h"
51 #include "nsGkAtoms.h"
52 #include "nsContentUtils.h"
54 #include "nsXULTemplateBuilder.h"
55 #include "nsXULTemplateResultStorage.h"
57 #include "mozIStorageService.h"
59 //----------------------------------------------------------------------
61 // nsXULTemplateResultSetStorage
64 NS_IMPL_ISUPPORTS1(nsXULTemplateResultSetStorage
, nsISimpleEnumerator
)
67 nsXULTemplateResultSetStorage::nsXULTemplateResultSetStorage(mozIStorageStatement
* aStatement
)
68 : mStatement(aStatement
)
71 nsresult rv
= aStatement
->GetColumnCount(&count
);
76 for (PRUint32 c
= 0; c
< count
; c
++) {
78 rv
= aStatement
->GetColumnName(c
, name
);
79 if (NS_SUCCEEDED(rv
)) {
80 nsCOMPtr
<nsIAtom
> columnName
= do_GetAtom(NS_LITERAL_CSTRING("?") + name
);
81 mColumnNames
.AppendObject(columnName
);
87 nsXULTemplateResultSetStorage::HasMoreElements(PRBool
*aResult
)
94 nsresult rv
= mStatement
->ExecuteStep(aResult
);
95 NS_ENSURE_SUCCESS(rv
, rv
);
96 // Because the nsXULTemplateResultSetStorage is owned by many nsXULTemplateResultStorage objects,
97 // it could live longer than it needed to get results.
98 // So we destroy the statement to free resources when all results are fetched
99 if (*aResult
== PR_FALSE
) {
106 nsXULTemplateResultSetStorage::GetNext(nsISupports
**aResult
)
108 nsXULTemplateResultStorage
* result
=
109 new nsXULTemplateResultStorage(this);
112 return NS_ERROR_OUT_OF_MEMORY
;
121 nsXULTemplateResultSetStorage::GetColumnIndex(nsIAtom
* aColumnName
)
123 PRInt32 count
= mColumnNames
.Count();
124 for (PRInt32 c
= 0; c
< count
; c
++) {
125 if (mColumnNames
[c
] == aColumnName
)
133 nsXULTemplateResultSetStorage::FillColumnValues(nsCOMArray
<nsIVariant
>& aArray
)
138 PRInt32 count
= mColumnNames
.Count();
140 for (PRInt32 c
= 0; c
< count
; c
++) {
141 nsCOMPtr
<nsIWritableVariant
> value
= do_CreateInstance("@mozilla.org/variant;1");
144 mStatement
->GetTypeOfIndex(c
, &type
);
146 if (type
== mStatement
->VALUE_TYPE_INTEGER
) {
147 PRInt32 val
= mStatement
->AsInt32(c
);
148 value
->SetAsInt32(val
);
150 else if (type
== mStatement
->VALUE_TYPE_FLOAT
) {
151 double val
= mStatement
->AsDouble(c
);
152 value
->SetAsDouble(val
);
156 nsresult rv
= mStatement
->GetString(c
, val
);
158 value
->SetAsAString(EmptyString());
160 value
->SetAsAString(val
);
162 aArray
.AppendObject(value
);
168 //----------------------------------------------------------------------
170 // nsXULTemplateQueryProcessorStorage
173 NS_IMPL_ISUPPORTS1(nsXULTemplateQueryProcessorStorage
,
174 nsIXULTemplateQueryProcessor
)
177 nsXULTemplateQueryProcessorStorage::nsXULTemplateQueryProcessorStorage()
178 : mGenerationStarted(PR_FALSE
)
183 nsXULTemplateQueryProcessorStorage::GetDatasource(nsIArray
* aDataSources
,
184 nsIDOMNode
* aRootNode
,
186 nsIXULTemplateBuilder
* aBuilder
,
187 PRBool
* aShouldDelayBuilding
,
188 nsISupports
** aReturn
)
191 *aShouldDelayBuilding
= PR_FALSE
;
198 nsresult rv
= aDataSources
->GetLength(&length
);
199 NS_ENSURE_SUCCESS(rv
, rv
);
205 // We get only the first uri. This query processor supports
206 // only one database at a time.
207 nsCOMPtr
<nsIURI
> uri
;
208 uri
= do_QueryElementAt(aDataSources
, 0);
211 // No uri in the list of datasources
215 nsCOMPtr
<mozIStorageService
> storage
=
216 do_GetService("@mozilla.org/storage/service;1", &rv
);
217 NS_ENSURE_SUCCESS(rv
, rv
);
219 nsCOMPtr
<nsIFile
> databaseFile
;
220 nsCAutoString scheme
;
221 rv
= uri
->GetScheme(scheme
);
222 NS_ENSURE_SUCCESS(rv
, rv
);
224 if (scheme
.EqualsLiteral("profile")) {
227 rv
= uri
->GetPath(path
);
228 NS_ENSURE_SUCCESS(rv
, rv
);
230 if (path
.IsEmpty()) {
231 return NS_ERROR_FAILURE
;
234 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
235 getter_AddRefs(databaseFile
));
236 NS_ENSURE_SUCCESS(rv
, rv
);
238 rv
= databaseFile
->AppendNative(path
);
239 NS_ENSURE_SUCCESS(rv
, rv
);
242 nsCOMPtr
<nsIChannel
> channel
;
243 nsCOMPtr
<nsIIOService
> ioservice
=
244 do_GetService("@mozilla.org/network/io-service;1", &rv
);
245 NS_ENSURE_SUCCESS(rv
, rv
);
247 rv
= ioservice
->NewChannelFromURI(uri
, getter_AddRefs(channel
));
248 NS_ENSURE_SUCCESS(rv
, rv
);
250 nsCOMPtr
<nsIFileChannel
> fileChannel
= do_QueryInterface(channel
, &rv
);
251 NS_ENSURE_SUCCESS(rv
, rv
); // if it fails, not a file url
253 nsCOMPtr
<nsIFile
> file
;
254 rv
= fileChannel
->GetFile(getter_AddRefs(databaseFile
));
255 NS_ENSURE_SUCCESS(rv
, rv
);
258 // ok now we have an URI of a sqlite file
259 nsCOMPtr
<mozIStorageConnection
> connection
;
260 rv
= storage
->OpenDatabase(databaseFile
, getter_AddRefs(connection
));
261 NS_ENSURE_SUCCESS(rv
, rv
);
263 NS_ADDREF(*aReturn
= connection
);
270 nsXULTemplateQueryProcessorStorage::InitializeForBuilding(nsISupports
* aDatasource
,
271 nsIXULTemplateBuilder
* aBuilder
,
272 nsIDOMNode
* aRootNode
)
274 NS_ENSURE_STATE(!mGenerationStarted
);
276 mStorageConnection
= do_QueryInterface(aDatasource
);
277 if (!mStorageConnection
)
278 return NS_ERROR_INVALID_ARG
;
281 mStorageConnection
->GetConnectionReady(&ready
);
283 return NS_ERROR_UNEXPECTED
;
289 nsXULTemplateQueryProcessorStorage::Done()
291 mGenerationStarted
= PR_FALSE
;
296 nsXULTemplateQueryProcessorStorage::CompileQuery(nsIXULTemplateBuilder
* aBuilder
,
297 nsIDOMNode
* aQueryNode
,
298 nsIAtom
* aRefVariable
,
299 nsIAtom
* aMemberVariable
,
300 nsISupports
** aReturn
)
302 nsCOMPtr
<nsIDOMNodeList
> childNodes
;
303 aQueryNode
->GetChildNodes(getter_AddRefs(childNodes
));
306 childNodes
->GetLength(&length
);
308 nsCOMPtr
<mozIStorageStatement
> statement
;
309 nsCOMPtr
<nsIContent
> queryContent
= do_QueryInterface(aQueryNode
);
310 nsAutoString sqlQuery
;
312 // Let's get all text nodes (which should be the query)
313 nsContentUtils::GetNodeTextContent(queryContent
, PR_FALSE
, sqlQuery
);
315 nsresult rv
= mStorageConnection
->CreateStatement(NS_ConvertUTF16toUTF8(sqlQuery
),
316 getter_AddRefs(statement
));
317 NS_ENSURE_SUCCESS(rv
, rv
);
319 PRUint32 parameterCount
= 0;
320 PRUint32 count
= queryContent
->GetChildCount();
322 for (PRUint32 i
= 0; i
< count
; ++i
) {
323 nsIContent
*child
= queryContent
->GetChildAt(i
);
325 if (child
->NodeInfo()->Equals(nsGkAtoms::param
, kNameSpaceID_XUL
)) {
327 nsContentUtils::GetNodeTextContent(child
, PR_FALSE
, value
);
329 PRUint32 index
= parameterCount
;
330 nsAutoString name
, indexValue
;
332 if (child
->GetAttr(kNameSpaceID_None
, nsGkAtoms::name
, name
)) {
333 nsAutoString fullName
;
334 fullName
.AssignLiteral(":");
335 fullName
.Append(name
);
336 rv
= statement
->GetParameterIndex(NS_ConvertUTF16toUTF8(fullName
) , &index
);
337 NS_ENSURE_SUCCESS(rv
, rv
);
340 else if (child
->GetAttr(kNameSpaceID_None
, nsGkAtoms::index
, indexValue
)) {
341 PR_sscanf(NS_ConvertUTF16toUTF8(indexValue
).get(),"%d",&index
);
349 static nsIContent::AttrValuesArray sTypeValues
[] =
350 { &nsGkAtoms::int32
, &nsGkAtoms::integer
, &nsGkAtoms::int64
,
351 &nsGkAtoms::null
, &nsGkAtoms::double_
, &nsGkAtoms::string
, nsnull
};
353 PRInt32 typeError
, typeValue
= child
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::type
,
354 sTypeValues
, eCaseMatters
);
355 rv
= NS_ERROR_ILLEGAL_VALUE
;
356 PRInt32 valInt32
= 0;
357 PRInt64 valInt64
= 0;
358 PRFloat64 valFloat
= 0;
363 typeError
= PR_sscanf(NS_ConvertUTF16toUTF8(value
).get(),"%d",&valInt32
);
365 rv
= statement
->BindInt32Parameter(index
, valInt32
);
368 typeError
= PR_sscanf(NS_ConvertUTF16toUTF8(value
).get(),"%lld",&valInt64
);
370 rv
= statement
->BindInt64Parameter(index
, valInt64
);
373 rv
= statement
->BindNullParameter(index
);
376 typeError
= PR_sscanf(NS_ConvertUTF16toUTF8(value
).get(),"%lf",&valFloat
);
378 rv
= statement
->BindDoubleParameter(index
, valFloat
);
381 case nsIContent::ATTR_MISSING
:
382 rv
= statement
->BindStringParameter(index
, value
);
385 NS_ENSURE_SUCCESS(rv
, rv
);
389 *aReturn
= statement
;
390 NS_IF_ADDREF(*aReturn
);
396 nsXULTemplateQueryProcessorStorage::GenerateResults(nsISupports
* aDatasource
,
397 nsIXULTemplateResult
* aRef
,
399 nsISimpleEnumerator
** aResults
)
401 mGenerationStarted
= PR_TRUE
;
403 nsCOMPtr
<mozIStorageStatement
> statement
= do_QueryInterface(aQuery
);
405 return NS_ERROR_FAILURE
;
407 nsXULTemplateResultSetStorage
* results
=
408 new nsXULTemplateResultSetStorage(statement
);
411 return NS_ERROR_OUT_OF_MEMORY
;
414 NS_ADDREF(*aResults
);
420 nsXULTemplateQueryProcessorStorage::AddBinding(nsIDOMNode
* aRuleNode
,
423 const nsAString
& aExpr
)
429 nsXULTemplateQueryProcessorStorage::TranslateRef(nsISupports
* aDatasource
,
430 const nsAString
& aRefString
,
431 nsIXULTemplateResult
** aRef
)
433 nsXULTemplateResultStorage
* result
=
434 new nsXULTemplateResultStorage(nsnull
);
436 return NS_ERROR_OUT_OF_MEMORY
;
445 nsXULTemplateQueryProcessorStorage::CompareResults(nsIXULTemplateResult
* aLeft
,
446 nsIXULTemplateResult
* aRight
,
454 // We're going to see if values are integers or float, to perform
455 // a suitable comparison
456 nsCOMPtr
<nsISupports
> leftValue
, rightValue
;
458 aLeft
->GetBindingObjectFor(aVar
, getter_AddRefs(leftValue
));
460 aRight
->GetBindingObjectFor(aVar
, getter_AddRefs(rightValue
));
462 if (leftValue
&& rightValue
) {
463 nsCOMPtr
<nsIVariant
> vLeftValue
= do_QueryInterface(leftValue
);
464 nsCOMPtr
<nsIVariant
> vRightValue
= do_QueryInterface(rightValue
);
466 if (vLeftValue
&& vRightValue
) {
468 PRUint16 vtypeL
, vtypeR
;
469 vLeftValue
->GetDataType(&vtypeL
);
470 vRightValue
->GetDataType(&vtypeR
);
472 if (vtypeL
== vtypeR
) {
473 if (vtypeL
== nsIDataType::VTYPE_INT32
) {
474 PRInt32 leftValue
, rightValue
;
475 rv1
= vLeftValue
->GetAsInt32(&leftValue
);
476 rv2
= vRightValue
->GetAsInt32(&rightValue
);
477 if (NS_SUCCEEDED(rv1
) && NS_SUCCEEDED(rv2
)) {
478 if (leftValue
> rightValue
)
480 else if (leftValue
< rightValue
)
485 else if (vtypeL
== nsIDataType::VTYPE_DOUBLE
) {
486 double leftValue
, rightValue
;
487 rv1
= vLeftValue
->GetAsDouble(&leftValue
);
488 rv2
= vRightValue
->GetAsDouble(&rightValue
);
489 if (NS_SUCCEEDED(rv1
) && NS_SUCCEEDED(rv2
)) {
490 if (leftValue
> rightValue
)
492 else if (leftValue
< rightValue
)
501 // Values are not integers or floats, so we just compare them as simple strings
502 nsAutoString leftVal
;
504 aLeft
->GetBindingFor(aVar
, leftVal
);
506 nsAutoString rightVal
;
508 aRight
->GetBindingFor(aVar
, rightVal
);
510 *aResult
= Compare(nsDependentString(leftVal
),
511 nsDependentString(rightVal
),
512 nsCaseInsensitiveStringComparator());