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
4 : *
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/
9 : *
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
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
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.
20 : *
21 : * Contributor(s):
22 : * Laurent Jouanneau <laurent.jouanneau@disruptive-innovations.com>
23 : *
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.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "prprf.h"
39 :
40 : #include "nsIDOMNodeList.h"
41 : #include "nsUnicharUtils.h"
42 :
43 : #include "nsArrayUtils.h"
44 : #include "nsIVariant.h"
45 : #include "nsAppDirectoryServiceDefs.h"
46 :
47 : #include "nsIURI.h"
48 : #include "nsIIOService.h"
49 : #include "nsIFileChannel.h"
50 : #include "nsIFile.h"
51 : #include "nsGkAtoms.h"
52 : #include "nsContentUtils.h"
53 :
54 : #include "nsXULTemplateBuilder.h"
55 : #include "nsXULTemplateResultStorage.h"
56 : #include "nsXULContentUtils.h"
57 : #include "nsXULSortService.h"
58 :
59 : #include "mozIStorageService.h"
60 :
61 : //----------------------------------------------------------------------
62 : //
63 : // nsXULTemplateResultSetStorage
64 : //
65 :
66 0 : NS_IMPL_ISUPPORTS1(nsXULTemplateResultSetStorage, nsISimpleEnumerator)
67 :
68 :
69 0 : nsXULTemplateResultSetStorage::nsXULTemplateResultSetStorage(mozIStorageStatement* aStatement)
70 0 : : mStatement(aStatement)
71 : {
72 : PRUint32 count;
73 0 : nsresult rv = aStatement->GetColumnCount(&count);
74 0 : if (NS_FAILED(rv)) {
75 0 : mStatement = nsnull;
76 0 : return;
77 : }
78 0 : for (PRUint32 c = 0; c < count; c++) {
79 0 : nsCAutoString name;
80 0 : rv = aStatement->GetColumnName(c, name);
81 0 : if (NS_SUCCEEDED(rv)) {
82 0 : nsCOMPtr<nsIAtom> columnName = do_GetAtom(NS_LITERAL_CSTRING("?") + name);
83 0 : mColumnNames.AppendObject(columnName);
84 : }
85 : }
86 : }
87 :
88 : NS_IMETHODIMP
89 0 : nsXULTemplateResultSetStorage::HasMoreElements(bool *aResult)
90 : {
91 0 : if (!mStatement) {
92 0 : *aResult = false;
93 0 : return NS_OK;
94 : }
95 :
96 0 : nsresult rv = mStatement->ExecuteStep(aResult);
97 0 : NS_ENSURE_SUCCESS(rv, rv);
98 : // Because the nsXULTemplateResultSetStorage is owned by many nsXULTemplateResultStorage objects,
99 : // it could live longer than it needed to get results.
100 : // So we destroy the statement to free resources when all results are fetched
101 0 : if (!*aResult) {
102 0 : mStatement = nsnull;
103 : }
104 0 : return NS_OK;
105 : }
106 :
107 : NS_IMETHODIMP
108 0 : nsXULTemplateResultSetStorage::GetNext(nsISupports **aResult)
109 : {
110 : nsXULTemplateResultStorage* result =
111 0 : new nsXULTemplateResultStorage(this);
112 :
113 0 : if (!result)
114 0 : return NS_ERROR_OUT_OF_MEMORY;
115 :
116 0 : *aResult = result;
117 0 : NS_ADDREF(result);
118 0 : return NS_OK;
119 : }
120 :
121 :
122 : PRInt32
123 0 : nsXULTemplateResultSetStorage::GetColumnIndex(nsIAtom* aColumnName)
124 : {
125 0 : PRInt32 count = mColumnNames.Count();
126 0 : for (PRInt32 c = 0; c < count; c++) {
127 0 : if (mColumnNames[c] == aColumnName)
128 0 : return c;
129 : }
130 :
131 0 : return -1;
132 : }
133 :
134 : void
135 0 : nsXULTemplateResultSetStorage::FillColumnValues(nsCOMArray<nsIVariant>& aArray)
136 : {
137 0 : if (!mStatement)
138 0 : return;
139 :
140 0 : PRInt32 count = mColumnNames.Count();
141 :
142 0 : for (PRInt32 c = 0; c < count; c++) {
143 0 : nsCOMPtr<nsIWritableVariant> value = do_CreateInstance("@mozilla.org/variant;1");
144 :
145 : PRInt32 type;
146 0 : mStatement->GetTypeOfIndex(c, &type);
147 :
148 0 : if (type == mStatement->VALUE_TYPE_INTEGER) {
149 0 : PRInt64 val = mStatement->AsInt64(c);
150 0 : value->SetAsInt64(val);
151 : }
152 0 : else if (type == mStatement->VALUE_TYPE_FLOAT) {
153 0 : double val = mStatement->AsDouble(c);
154 0 : value->SetAsDouble(val);
155 : }
156 : else {
157 0 : nsAutoString val;
158 0 : nsresult rv = mStatement->GetString(c, val);
159 0 : if (NS_FAILED(rv))
160 0 : value->SetAsAString(EmptyString());
161 : else
162 0 : value->SetAsAString(val);
163 : }
164 0 : aArray.AppendObject(value);
165 : }
166 : }
167 :
168 :
169 :
170 : //----------------------------------------------------------------------
171 : //
172 : // nsXULTemplateQueryProcessorStorage
173 : //
174 :
175 0 : NS_IMPL_ISUPPORTS1(nsXULTemplateQueryProcessorStorage,
176 : nsIXULTemplateQueryProcessor)
177 :
178 :
179 0 : nsXULTemplateQueryProcessorStorage::nsXULTemplateQueryProcessorStorage()
180 0 : : mGenerationStarted(false)
181 : {
182 0 : }
183 :
184 : NS_IMETHODIMP
185 0 : nsXULTemplateQueryProcessorStorage::GetDatasource(nsIArray* aDataSources,
186 : nsIDOMNode* aRootNode,
187 : bool aIsTrusted,
188 : nsIXULTemplateBuilder* aBuilder,
189 : bool* aShouldDelayBuilding,
190 : nsISupports** aReturn)
191 : {
192 0 : *aReturn = nsnull;
193 0 : *aShouldDelayBuilding = false;
194 :
195 0 : if (!aIsTrusted) {
196 0 : return NS_OK;
197 : }
198 :
199 : PRUint32 length;
200 0 : nsresult rv = aDataSources->GetLength(&length);
201 0 : NS_ENSURE_SUCCESS(rv, rv);
202 :
203 0 : if (length == 0) {
204 0 : return NS_OK;
205 : }
206 :
207 : // We get only the first uri. This query processor supports
208 : // only one database at a time.
209 0 : nsCOMPtr<nsIURI> uri;
210 0 : uri = do_QueryElementAt(aDataSources, 0);
211 :
212 0 : if (!uri) {
213 : // No uri in the list of datasources
214 0 : return NS_OK;
215 : }
216 :
217 : nsCOMPtr<mozIStorageService> storage =
218 0 : do_GetService("@mozilla.org/storage/service;1", &rv);
219 0 : NS_ENSURE_SUCCESS(rv, rv);
220 :
221 0 : nsCOMPtr<nsIFile> databaseFile;
222 0 : nsCAutoString scheme;
223 0 : rv = uri->GetScheme(scheme);
224 0 : NS_ENSURE_SUCCESS(rv, rv);
225 :
226 0 : if (scheme.EqualsLiteral("profile")) {
227 :
228 0 : nsCAutoString path;
229 0 : rv = uri->GetPath(path);
230 0 : NS_ENSURE_SUCCESS(rv, rv);
231 :
232 0 : if (path.IsEmpty()) {
233 0 : return NS_ERROR_FAILURE;
234 : }
235 :
236 : rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
237 0 : getter_AddRefs(databaseFile));
238 0 : NS_ENSURE_SUCCESS(rv, rv);
239 :
240 0 : rv = databaseFile->AppendNative(path);
241 0 : NS_ENSURE_SUCCESS(rv, rv);
242 : }
243 : else {
244 0 : nsCOMPtr<nsIChannel> channel;
245 : nsCOMPtr<nsIIOService> ioservice =
246 0 : do_GetService("@mozilla.org/network/io-service;1", &rv);
247 0 : NS_ENSURE_SUCCESS(rv, rv);
248 :
249 0 : rv = ioservice->NewChannelFromURI(uri, getter_AddRefs(channel));
250 0 : NS_ENSURE_SUCCESS(rv, rv);
251 :
252 0 : nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel, &rv);
253 0 : if (NS_FAILED(rv)) { // if it fails, not a file url
254 0 : nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_BAD_URI);
255 0 : return rv;
256 : }
257 :
258 0 : nsCOMPtr<nsIFile> file;
259 0 : rv = fileChannel->GetFile(getter_AddRefs(databaseFile));
260 0 : NS_ENSURE_SUCCESS(rv, rv);
261 : }
262 :
263 : // ok now we have an URI of a sqlite file
264 0 : nsCOMPtr<mozIStorageConnection> connection;
265 0 : rv = storage->OpenDatabase(databaseFile, getter_AddRefs(connection));
266 0 : if (NS_FAILED(rv)) {
267 0 : nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_CANNOT_OPEN_DATABASE);
268 0 : return rv;
269 : }
270 :
271 0 : NS_ADDREF(*aReturn = connection);
272 0 : return NS_OK;
273 : }
274 :
275 :
276 :
277 : NS_IMETHODIMP
278 0 : nsXULTemplateQueryProcessorStorage::InitializeForBuilding(nsISupports* aDatasource,
279 : nsIXULTemplateBuilder* aBuilder,
280 : nsIDOMNode* aRootNode)
281 : {
282 0 : NS_ENSURE_STATE(!mGenerationStarted);
283 :
284 0 : mStorageConnection = do_QueryInterface(aDatasource);
285 0 : if (!mStorageConnection)
286 0 : return NS_ERROR_INVALID_ARG;
287 :
288 : bool ready;
289 0 : mStorageConnection->GetConnectionReady(&ready);
290 0 : if (!ready)
291 0 : return NS_ERROR_UNEXPECTED;
292 :
293 0 : return NS_OK;
294 : }
295 :
296 : NS_IMETHODIMP
297 0 : nsXULTemplateQueryProcessorStorage::Done()
298 : {
299 0 : mGenerationStarted = false;
300 0 : return NS_OK;
301 : }
302 :
303 : NS_IMETHODIMP
304 0 : nsXULTemplateQueryProcessorStorage::CompileQuery(nsIXULTemplateBuilder* aBuilder,
305 : nsIDOMNode* aQueryNode,
306 : nsIAtom* aRefVariable,
307 : nsIAtom* aMemberVariable,
308 : nsISupports** aReturn)
309 : {
310 0 : nsCOMPtr<nsIDOMNodeList> childNodes;
311 0 : aQueryNode->GetChildNodes(getter_AddRefs(childNodes));
312 :
313 : PRUint32 length;
314 0 : childNodes->GetLength(&length);
315 :
316 0 : nsCOMPtr<mozIStorageStatement> statement;
317 0 : nsCOMPtr<nsIContent> queryContent = do_QueryInterface(aQueryNode);
318 0 : nsAutoString sqlQuery;
319 :
320 : // Let's get all text nodes (which should be the query)
321 0 : nsContentUtils::GetNodeTextContent(queryContent, false, sqlQuery);
322 :
323 0 : nsresult rv = mStorageConnection->CreateStatement(NS_ConvertUTF16toUTF8(sqlQuery),
324 0 : getter_AddRefs(statement));
325 0 : if (NS_FAILED(rv)) {
326 0 : nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_BAD_QUERY);
327 0 : return rv;
328 : }
329 :
330 0 : PRUint32 parameterCount = 0;
331 0 : for (nsIContent* child = queryContent->GetFirstChild();
332 : child;
333 0 : child = child->GetNextSibling()) {
334 :
335 0 : if (child->NodeInfo()->Equals(nsGkAtoms::param, kNameSpaceID_XUL)) {
336 0 : nsAutoString value;
337 0 : nsContentUtils::GetNodeTextContent(child, false, value);
338 :
339 0 : PRUint32 index = parameterCount;
340 0 : nsAutoString name, indexValue;
341 :
342 0 : if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) {
343 0 : rv = statement->GetParameterIndex(NS_ConvertUTF16toUTF8(name),
344 0 : &index);
345 0 : if (NS_FAILED(rv)) {
346 0 : nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_UNKNOWN_QUERY_PARAMETER);
347 0 : return rv;
348 : }
349 0 : parameterCount++;
350 : }
351 0 : else if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::index, indexValue)) {
352 0 : PR_sscanf(NS_ConvertUTF16toUTF8(indexValue).get(),"%d",&index);
353 0 : if (index > 0)
354 0 : index--;
355 : }
356 : else {
357 0 : parameterCount++;
358 : }
359 :
360 : static nsIContent::AttrValuesArray sTypeValues[] =
361 : { &nsGkAtoms::int32, &nsGkAtoms::integer, &nsGkAtoms::int64,
362 : &nsGkAtoms::null, &nsGkAtoms::double_, &nsGkAtoms::string, nsnull };
363 :
364 0 : PRInt32 typeError = 1;
365 : PRInt32 typeValue = child->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type,
366 0 : sTypeValues, eCaseMatters);
367 0 : rv = NS_ERROR_ILLEGAL_VALUE;
368 0 : PRInt32 valInt32 = 0;
369 0 : PRInt64 valInt64 = 0;
370 0 : PRFloat64 valFloat = 0;
371 :
372 0 : switch (typeValue) {
373 : case 0:
374 : case 1:
375 0 : typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%d",&valInt32);
376 0 : if (typeError > 0)
377 0 : rv = statement->BindInt32ByIndex(index, valInt32);
378 0 : break;
379 : case 2:
380 0 : typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%lld",&valInt64);
381 0 : if (typeError > 0)
382 0 : rv = statement->BindInt64ByIndex(index, valInt64);
383 0 : break;
384 : case 3:
385 0 : rv = statement->BindNullByIndex(index);
386 0 : break;
387 : case 4:
388 0 : typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%lf",&valFloat);
389 0 : if (typeError > 0)
390 0 : rv = statement->BindDoubleByIndex(index, valFloat);
391 0 : break;
392 : case 5:
393 : case nsIContent::ATTR_MISSING:
394 0 : rv = statement->BindStringByIndex(index, value);
395 0 : break;
396 : default:
397 0 : typeError = 0;
398 : }
399 :
400 0 : if (typeError <= 0) {
401 0 : nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_WRONG_TYPE_QUERY_PARAMETER);
402 0 : return rv;
403 : }
404 :
405 0 : if (NS_FAILED(rv)) {
406 0 : nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_QUERY_PARAMETER_NOT_BOUND);
407 0 : return rv;
408 : }
409 : }
410 : }
411 :
412 0 : *aReturn = statement;
413 0 : NS_IF_ADDREF(*aReturn);
414 :
415 0 : return NS_OK;
416 : }
417 :
418 : NS_IMETHODIMP
419 0 : nsXULTemplateQueryProcessorStorage::GenerateResults(nsISupports* aDatasource,
420 : nsIXULTemplateResult* aRef,
421 : nsISupports* aQuery,
422 : nsISimpleEnumerator** aResults)
423 : {
424 0 : mGenerationStarted = true;
425 :
426 0 : nsCOMPtr<mozIStorageStatement> statement = do_QueryInterface(aQuery);
427 0 : if (!statement)
428 0 : return NS_ERROR_FAILURE;
429 :
430 : nsXULTemplateResultSetStorage* results =
431 0 : new nsXULTemplateResultSetStorage(statement);
432 :
433 0 : if (!results)
434 0 : return NS_ERROR_OUT_OF_MEMORY;
435 :
436 0 : *aResults = results;
437 0 : NS_ADDREF(*aResults);
438 :
439 0 : return NS_OK;
440 : }
441 :
442 : NS_IMETHODIMP
443 0 : nsXULTemplateQueryProcessorStorage::AddBinding(nsIDOMNode* aRuleNode,
444 : nsIAtom* aVar,
445 : nsIAtom* aRef,
446 : const nsAString& aExpr)
447 : {
448 0 : return NS_OK;
449 : }
450 :
451 : NS_IMETHODIMP
452 0 : nsXULTemplateQueryProcessorStorage::TranslateRef(nsISupports* aDatasource,
453 : const nsAString& aRefString,
454 : nsIXULTemplateResult** aRef)
455 : {
456 : nsXULTemplateResultStorage* result =
457 0 : new nsXULTemplateResultStorage(nsnull);
458 0 : if (!result)
459 0 : return NS_ERROR_OUT_OF_MEMORY;
460 :
461 0 : *aRef = result;
462 0 : NS_ADDREF(*aRef);
463 0 : return NS_OK;
464 : }
465 :
466 :
467 : NS_IMETHODIMP
468 0 : nsXULTemplateQueryProcessorStorage::CompareResults(nsIXULTemplateResult* aLeft,
469 : nsIXULTemplateResult* aRight,
470 : nsIAtom* aVar,
471 : PRUint32 aSortHints,
472 : PRInt32* aResult)
473 : {
474 0 : *aResult = 0;
475 0 : if (!aVar)
476 0 : return NS_OK;
477 :
478 : // We're going to see if values are integers or float, to perform
479 : // a suitable comparison
480 0 : nsCOMPtr<nsISupports> leftValue, rightValue;
481 0 : if (aLeft)
482 0 : aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftValue));
483 0 : if (aRight)
484 0 : aRight->GetBindingObjectFor(aVar, getter_AddRefs(rightValue));
485 :
486 0 : if (leftValue && rightValue) {
487 0 : nsCOMPtr<nsIVariant> vLeftValue = do_QueryInterface(leftValue);
488 0 : nsCOMPtr<nsIVariant> vRightValue = do_QueryInterface(rightValue);
489 :
490 0 : if (vLeftValue && vRightValue) {
491 : nsresult rv1, rv2;
492 : PRUint16 vtypeL, vtypeR;
493 0 : vLeftValue->GetDataType(&vtypeL);
494 0 : vRightValue->GetDataType(&vtypeR);
495 :
496 0 : if (vtypeL == vtypeR) {
497 0 : if (vtypeL == nsIDataType::VTYPE_INT64) {
498 : PRInt64 leftValue, rightValue;
499 0 : rv1 = vLeftValue->GetAsInt64(&leftValue);
500 0 : rv2 = vRightValue->GetAsInt64(&rightValue);
501 0 : if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
502 0 : if (leftValue > rightValue)
503 0 : *aResult = 1;
504 0 : else if (leftValue < rightValue)
505 0 : *aResult = -1;
506 0 : return NS_OK;
507 : }
508 : }
509 0 : else if (vtypeL == nsIDataType::VTYPE_DOUBLE) {
510 : double leftValue, rightValue;
511 0 : rv1 = vLeftValue->GetAsDouble(&leftValue);
512 0 : rv2 = vRightValue->GetAsDouble(&rightValue);
513 0 : if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
514 0 : if (leftValue > rightValue)
515 0 : *aResult = 1;
516 0 : else if (leftValue < rightValue)
517 0 : *aResult = -1;
518 0 : return NS_OK;
519 : }
520 : }
521 : }
522 : }
523 : }
524 :
525 : // Values are not integers or floats, so we just compare them as simple strings
526 0 : nsAutoString leftVal;
527 0 : if (aLeft)
528 0 : aLeft->GetBindingFor(aVar, leftVal);
529 :
530 0 : nsAutoString rightVal;
531 0 : if (aRight)
532 0 : aRight->GetBindingFor(aVar, rightVal);
533 :
534 0 : *aResult = XULSortServiceImpl::CompareValues(leftVal, rightVal, aSortHints);
535 0 : return NS_OK;
536 : }
|