Ticket #318: DataManipulationLib.cfm

File DataManipulationLib.cfm, 160.7 kB (added by cfgrok, 17 years ago)
Line 
1<!---
2
3This library is part of the Common Function Library Project. An open source
4collection of UDF libraries designed for ColdFusion 5.0. For more information,
5please see the web site at:
6
7http://www.cflib.org
8
9Warning:
10You may not need all the functions in this library. If speed
11is _extremely_ important, you may want to consider deleting
12functions you do not plan on using. Normally you should not
13have to worry about the size of the library.
14
15License:
16This code may be used freely.
17You may modify this code as you see fit, however, this header, and the header
18for the functions must remain intact.
19
20This code is provided as is. We make no warranty or guarantee. Use of this code is at your own risk.
21--->
22
23<cfscript>
24/**
25 * This functions helps to quickly build arrays, both simple and complex.
26 *
27 * @param paramN This UDF accepts N optional arguments. Each argument is added to the returned array. (Optional)
28 * @return Returns an array.
29 * @author Erki Esken (&#101;&#114;&#107;&#105;&#64;&#100;&#114;&#101;&#97;&#109;&#100;&#114;&#117;&#109;&#109;&#101;&#114;&#46;&#99;&#111;&#109;)
30 * @version 1, July 3, 2002
31 */
32function Array() {
33var result = ArrayNew(1);
34var to = ArrayLen(arguments);
35var i = 0;
36for (i=1; i LTE to; i=i+1)
37result[i] = Duplicate(arguments[i]);
38return result;
39}
40
41/**
42 * Appends two values to a 2D array.
43 *
44 * @param aName The array. (Required)
45 * @param value1 First value. (Required)
46 * @param value2 Second value. (Required)
47 * @return Returns the array.
48 * @author Minh Lee Goon (&#99;&#111;&#110;&#116;&#97;&#99;&#116;&#64;&#100;&#105;&#103;&#101;&#114;&#97;&#116;&#105;&#100;&#101;&#115;&#105;&#103;&#110;&#115;&#116;&#117;&#100;&#105;&#111;&#115;&#46;&#99;&#111;&#109;)
49 * @version 1, March 21, 2006
50 */
51function arrayAppend2D(aName, value1, value2) {
52var theLen = arrayLen(aName);
53
54aName[theLen+1][1] = value1;
55aName[theLen+1][2] = value2;
56
57return aName;
58}
59
60/**
61 * Appends a value to an array if the value does not already exist within the array.
62 *
63 * @param a1 The array to modify.
64 * @param val The value to append.
65 * @return Returns a modified array or an error string.
66 * @author Craig Fisher (&#99;&#114;&#97;&#105;&#103;&#64;&#97;&#108;&#116;&#97;&#105;&#110;&#116;&#101;&#114;&#97;&#99;&#116;&#105;&#118;&#101;&#46;&#99;&#111;&#109;)
67 * @version 1, October 29, 2001
68 */
69function ArrayAppendUnique(a1,val) {
70if ((NOT IsArray(a1))) {
71writeoutput("Error in <Code>ArrayAppendUnique()</code>! Correct usage: ArrayAppendUnique(<I>Array</I>, <I>Value</I>) -- Appends <em>Value</em> to the array if <em>Value</em> does not already exist");
72return 0;
73}
74if (NOT ListFind(Arraytolist(a1), val)) {
75arrayAppend(a1, val);
76}
77return a1;
78}
79
80/**
81 * Used to remove missing positions from an array.
82 *
83 * @param arr Array to compact. (Required)
84 * @param delim Temporary list delimiter. Defaults to |. (Optional)
85 * @return Returns an array.
86 * @author M Gillespie for HOUCFUG (&#104;&#111;&#117;&#99;&#102;&#117;&#103;&#64;&#121;&#97;&#104;&#111;&#111;&#103;&#114;&#111;&#117;&#112;&#115;&#46;&#99;&#111;&#109;)
87 * @version 1, March 2, 2007
88 */
89function arrayCompact(arr) {
90var delim="|";
91if(arraylen(arguments) gt 1) {delim=arguments[2];}
92return listtoarray(arraytolist(arr,delim),delim);
93}
94
95/**
96 * Recursive functions to compare arrays and nested structures.
97 *
98 * @param LeftArray The first array. (Required)
99 * @param RightArray The second array. (Required)
100 * @return Returns a boolean.
101 * @author Ja Carter (&#106;&#97;&#64;&#110;&#117;&#111;&#114;&#98;&#105;&#116;&#46;&#99;&#111;&#109;)
102 * @version 1, September 23, 2004
103 */
104function arrayCompare(LeftArray,RightArray) {
105var result = true;
106var i = "";
107
108//Make sure both params are arrays
109if (NOT (isArray(LeftArray) AND isArray(RightArray))) return false;
110
111//Make sure both arrays have the same length
112if (NOT arrayLen(LeftArray) EQ arrayLen(RightArray)) return false;
113
114// Loop through the elements and compare them one at a time
115for (i=1;i lte arrayLen(LeftArray); i = i+1) {
116//elements is a structure, call structCompare()
117if (isStruct(LeftArray[i])){
118result = structCompare(LeftArray[i],RightArray[i]);
119if (NOT result) return false;
120//elements is an array, call arrayCompare()
121} else if (isArray(LeftArray[i])){
122result = arrayCompare(LeftArray[i],RightArray[i]);
123if (NOT result) return false;
124//A simple type comparison here
125} else {
126if(LeftArray[i] IS NOT RightArray[i]) return false;
127}
128}
129
130return true;
131}
132
133/**
134 * Concatenates two arrays.
135 *
136 * @param a1 The first array.
137 * @param a2 The second array.
138 * @return Returns an array.
139 * @author Craig Fisher (&#99;&#114;&#97;&#105;&#103;&#64;&#97;&#108;&#116;&#97;&#105;&#110;&#101;&#116;&#114;&#97;&#99;&#116;&#105;&#118;&#101;&#46;&#99;&#111;&#109;)
140 * @version 1, September 13, 2001
141 */
142function ArrayConcat(a1, a2){
143var i=1;
144if ((NOT IsArray(a1)) OR (NOT IsArray(a2))) {
145writeoutput("Error in <Code>ArrayConcat()</code>! Correct usage: ArrayConcat(<I>Array1</I>, <I>Array2</I>) -- Concatenates Array2 to the end of Array1");
146return 0;
147}
148for (i=1;i LTE ArrayLen(a2);i=i+1) {
149ArrayAppend(a1, Duplicate(a2[i]));
150}
151return a1;
152}
153
154/**
155 * Returns the index of the first item in an array that contains a specified substring.
156 * Mods by RCamden
157 *
158 * @param arrayToSearch Array to search. (Required)
159 * @param valueToFind Value to look for. (Required)
160 * @return Returns a number.
161 * @author Sudhir Duddella (&#115;&#107;&#100;&#117;&#100;&#100;&#101;&#108;&#108;&#97;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
162 * @version 1, March 31, 2003
163 */
164function ArrayContains(arrayToSearch,valueToFind){
165var arrayList = "";
166
167arrayList = ArrayToList(arrayToSearch);
168return ListContains(arrayList,valueToFind);
169}
170
171/**
172 * Returns the index of the first item in an array that contains a specified substring.
173 * Mods by RCamden
174 *
175 * @param arrayToSearch Array to search. (Required)
176 * @param valueToFind Value to look for. (Required)
177 * @return Returns a number.
178 * @author Sudhir Duddella (&#115;&#107;&#100;&#117;&#100;&#100;&#101;&#108;&#108;&#97;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
179 * @version 1, March 31, 2003
180 */
181function ArrayContainsNoCase(arrayToSearch,valueToFind){
182var arrayList = "";
183
184arrayList = ArrayToList(arrayToSearch);
185return ListContainsNoCase(arrayList,valueToFind);
186}
187
188/**
189 * Returns true if a specified array position is defined.
190 *
191 * @param arr The array to check. (Required)
192 * @param pos The position to check. (Required)
193 * @return Returns a boolean.
194 * @author Raymond Camden (&#114;&#97;&#121;&#64;&#99;&#97;&#109;&#100;&#101;&#110;&#102;&#97;&#109;&#105;&#108;&#121;&#46;&#99;&#111;&#109;)
195 * @version 2, October 24, 2003
196 */
197function arrayDefinedAt(arr,pos) {
198var temp = "";
199try {
200temp = arr[pos];
201return true;
202}
203catch(coldfusion.runtime.UndefinedElementException ex) {
204return false;
205}
206catch(coldfusion.runtime.CfJspPage$ArrayBoundException ex) {
207return false;
208}
209}
210
211/**
212 * Deletes an elements list from an array.
213 *
214 * @param a The array to modify. (Required)
215 * @param l The list of indexes to remove. (Required)
216 * @return Returns an array.
217 * @author Giampaolo Bellavite (&#103;&#105;&#97;&#109;&#112;&#97;&#111;&#108;&#111;&#64;&#98;&#101;&#108;&#108;&#97;&#118;&#105;&#116;&#101;&#46;&#99;&#111;&#109;)
218 * @version 1, January 21, 2005
219 */
220function ArrayDeleteAtList(a,l) {
221var i=1;
222l = listSort(l, "numeric", "desc");
223for(i=1; i lte listLen(l); i=i+1) arrayDeleteAt(a, listGetAt(l,i));
224return a;
225}
226
227/**
228 * Compares two arrays (with simple values) and returns the difference between the two.
229 *
230 * @param smallerArray First array. (Required)
231 * @param biggerArray Second array. (Required)
232 * @return Returns an array.
233 * @author Greg Nettles (&#103;&#114;&#101;&#103;&#110;&#101;&#116;&#116;&#108;&#101;&#115;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
234 * @version 1, March 13, 2007
235 */
236function arrayDiff(smallerArray,biggerArray) {
237var i = "";
238var result = arrayNew(1);
239var s = arrayToList(arguments.smallerArray);
240for (i=1;i lte arrayLen(arguments.biggerArray); i=i+1) if (listFind(s, arguments.biggerArray[i]) is 0) arrayAppend(result, arguments.biggerArray[i]);
241return result;
242}
243
244/**
245 * Applies a filter to an array.
246 *
247 * @param array Array to modify. (Required)
248 * @param filter The UDF, NOT THE NAME, but the UDF to use as a filter. (Required)
249 * @return Returns an array.
250 * @author Raymond Camden (&#114;&#97;&#121;&#64;&#99;&#97;&#109;&#100;&#101;&#110;&#102;&#97;&#109;&#105;&#108;&#121;&#46;&#99;&#111;&#109;)
251 * @version 1, March 31, 2003
252 */
253function arrayFilter(array,filter) {
254var newA = arrayNew(1);
255var i = 1;
256
257for(;i lte arrayLen(array); i=i+1) {
258if(filter(array[i])) arrayAppend(newA,array[i]);
259}
260
261return newA;
262}
263
264/**
265 * Like listFindNoCase(), but for arrays.
266 *
267 * @param arrayToSearch The array to search. (Required)
268 * @param valueToFind The value to look for. (Required)
269 * @return Returns a number.
270 * @author Nathan Dintenfass (&#110;&#97;&#116;&#104;&#97;&#110;&#64;&#99;&#104;&#97;&#110;&#103;&#101;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
271 * @version 1, September 6, 2002
272 */
273function ArrayFindNoCase(arrayToSearch,valueToFind){
274//a variable for looping
275var ii = 0;
276//loop through the array, looking for the value
277for(ii = 1; ii LTE arrayLen(arrayToSearch); ii = ii + 1){
278//if this is the value, return the index
279if(NOT compareNoCase(arrayToSearch[ii],valueToFind))
280return ii;
281}
282//if we've gotten this far, it means the value was not found, so return 0
283return 0;
284}
285
286/**
287 * Locate a value in an already-sorted array.
288 *
289 * @param array The array to check. (Required)
290 * @param value The value to look for. (Required)
291 * @return Returns the position of the match, or 0.
292 * @author Kenneth Fricklas (&#107;&#101;&#110;&#102;&#64;&#109;&#97;&#108;&#108;&#102;&#105;&#110;&#100;&#101;&#114;&#46;&#99;&#111;&#109;)
293 * @version 1, September 30, 2005
294 */
295function arrayFindSorted(arrayX, value)
296{
297var m = 0;
298var found = 0;
299var done = 0;
300var hi = arrayLen(arrayX)+1;
301var lo = 1;
302var i = 1;
303var maxtest = 500;
304do {
305m = (hi + lo) \ 2;
306if (arrayX[m] EQ value)
307{
308found = 1;
309done = 1;
310}
311else
312{
313if ((m EQ lo) or (m EQ hi))
314done = 1; /* not found */
315else
316{
317if (value LT arrayX[m])
318{
319/* higher */
320hi = m;
321}
322else
323{
324/* lower */
325lo = m;
326}
327}
328}
329if (i EQ maxtest)
330{
331done = 1;
332writeoutput("Error! overflow in search");
333}
334else
335i = i + 1;
336} while (done EQ 0);
337if (found)
338return m;
339else
340return 0;
341}
342
343/**
344 * Inserts an array at specified position in another array.
345 *
346 * @param a1 The first array.
347 * @param a2 The second array.
348 * @param pos The position to insert at.
349 * @return Returns an array.
350 * @author Craig Fisher (&#99;&#114;&#97;&#105;&#103;&#64;&#97;&#108;&#116;&#97;&#105;&#110;&#116;&#101;&#114;&#97;&#99;&#116;&#105;&#118;&#101;&#46;&#99;&#111;&#109;)
351 * @version 1, September 13, 2001
352 */
353function ArrayInsertArrayAt(a1, a2, pos) {
354var aNew = ArrayNew(1);
355var len1 = "";
356var len2 = "";
357var i = 1;
358if ((NOT isArray(a1)) OR (NOT isArray(a2)) OR (NOT IsNumeric(pos)) OR (pos LT 1) OR (pos GT ArrayLen(a1) +1) ) {
359writeoutput("Error in <Code>ArrayInsertArrayAt()</code>! Correct usage: ArrayInsertArrayAt(<I>Array1</I>, <I>Array2</I>,
360<I>position</I>) -- Inserts <I>Array2</I> at <I>position</I> in
361<I>Array2</I>");
362return 0;
363}
364pos=int(pos);
365len1=ArrayLen(a1);
366len2=ArrayLen(a2);
367aNew=Duplicate(a1);
368if (pos IS NOT Len1 + 1) {
369for (i=0; i LT len2; i=i+1) ArrayInsertAt(aNew, pos + i, Duplicate(a2[i+1]));
370}
371else {
372for (i=1;i LTE len2;i=i+1) ArrayAppend(aNew, Duplicate(a2[i]));
373}
374return aNew;
375}
376
377/**
378 * Returns the index of the first item in an array that contains a list element.
379 *
380 * @param arrayToSearch The array to search. (Required)
381 * @param listToFind List that will be searched for. If any item is found, the array index is returned. (Required)
382 * @param delimiter List delimiter. Defaults to a comma. (Optional)
383 * @return Returns a number.
384 * @author Steve Robison, Jr (&#115;&#116;&#101;&#118;&#101;&#114;&#111;&#98;&#105;&#115;&#111;&#110;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
385 * @version 1, March 28, 2005
386 */
387function ArrayListCompareNoCase(arrayToSearch,listToFind){
388//a variable for looping
389var ii = 0; // variable for looping through list
390var jj = 0; // variable for looping through array
391var delimiter = ','; // default delimiter
392
393
394// check to see if delimiters were passed
395if (ArrayLen(arguments) gt 2) delimiter = arguments[3];
396
397//loop through list
398for(ii = 1; ii LTE ListLen(listToFind, delimiter); ii = ii + 1) {
399//loop through the array, looking for the value
400for(jj = 1; jj LTE arrayLen(arrayToSearch); jj = jj + 1){
401//if this is the value, return the index
402if(NOT compareNoCase(arrayToSearch[jj],ListGetAt(listToFind, ii, delimiter)))
403return jj;
404}
405}
406//if we've gotten this far, it means the value was not found, so return 0
407return 0;
408}
409
410/**
411 * Sorts an array of structures based on a key in the structures.
412 *
413 * @param aofS Array of structures.
414 * @param key Key to sort by.
415 * @param sortOrder Order to sort by, asc or desc.
416 * @param sortType Text, textnocase, or numeric.
417 * @param delim Delimiter used for temporary data storage. Must not exist in data. Defaults to a period.
418 * @return Returns a sorted array.
419 * @author Nathan Dintenfass (&#110;&#97;&#116;&#104;&#97;&#110;&#64;&#99;&#104;&#97;&#110;&#103;&#101;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
420 * @version 1, December 10, 2001
421 */
422function arrayOfStructsSort(aOfS,key){
423//by default we'll use an ascending sort
424var sortOrder = "asc";
425//by default, we'll use a textnocase sort
426var sortType = "textnocase";
427//by default, use ascii character 30 as the delim
428var delim = ".";
429//make an array to hold the sort stuff
430var sortArray = arraynew(1);
431//make an array to return
432var returnArray = arraynew(1);
433//grab the number of elements in the array (used in the loops)
434var count = arrayLen(aOfS);
435//make a variable to use in the loop
436var ii = 1;
437//if there is a 3rd argument, set the sortOrder
438if(arraylen(arguments) GT 2)
439sortOrder = arguments[3];
440//if there is a 4th argument, set the sortType
441if(arraylen(arguments) GT 3)
442sortType = arguments[4];
443//if there is a 5th argument, set the delim
444if(arraylen(arguments) GT 4)
445delim = arguments[5];
446//loop over the array of structs, building the sortArray
447for(ii = 1; ii lte count; ii = ii + 1)
448sortArray[ii] = aOfS[ii][key] & delim & ii;
449//now sort the array
450arraySort(sortArray,sortType,sortOrder);
451//now build the return array
452for(ii = 1; ii lte count; ii = ii + 1)
453returnArray[ii] = aOfS[listLast(sortArray[ii],delim)];
454//return the array
455return returnArray;
456}
457
458/**
459 * Returns a list of keys from a structure within an array.
460 *
461 * @param array Array to parse. (Required)
462 * @param key Key to use. (Required)
463 * @param delimeter Delimeter to use for returned list. Defaults to a comma. (Optional)
464 * @return Returns a list.
465 * @author jeff tapper (&#106;&#115;&#116;&#97;&#112;&#115;&#64;&#101;&#99;&#104;&#111;&#110;&#121;&#99;&#46;&#99;&#111;&#109;)
466 * @version 1, October 20, 2003
467 */
468function ArrayOfStructsToList(Array,Key){
469var delim = ",";
470var i = 0;
471var list = "";
472if(arrayLen(arguments) gt 2) delim = arguments[3];
473
474for (i=1;i lte arrayLen(array);i=i+1){
475list = listAppend(list,array[i][key],delim);
476}
477
478return list;
479}
480
481/**
482 * Changes a given array of structures to a structure of arrays.
483 *
484 * @param ar Array of structs. (Required)
485 * @return Returns a struct.
486 * @author Nathan Strutz (&#109;&#114;&#110;&#97;&#116;&#101;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
487 * @version 1, September 21, 2004
488 */
489function arrayOfStructsToStructOfArrays(ar) {
490var st = structNew();
491var arKeys = structKeyArray(ar[1]);
492var i=0;
493var j=0;
494var arLen = arrayLen(ar);
495for (i=1;i lte arrayLen(arKeys);i=i+1) {
496st[arKeys[i]] = arrayNew(1);
497for (j=1;j lte arLen;j=j+1) {
498st[arKeys[i]][j] = ar[j][arKeys[i]];
499}
500}
501return st;
502}
503
504/**
505 * Converts an array of structures to a CF Query Object.
506 * 6-19-02: Minor revision by Rob Brooks-Bilson (&#114;&#98;&#105;&#108;&#115;&#64;&#97;&#109;&#107;&#111;&#114;&#46;&#99;&#111;&#109;)
507 *
508 * Update to handle empty array passed in. Mod by Nathan Dintenfass. Also no longer using list func.
509 *
510 * @param Array The array of structures to be converted to a query object. Assumes each array element contains structure with same (Required)
511 * @return Returns a query object.
512 * @author David Crawford (&#114;&#98;&#105;&#108;&#115;&#64;&#97;&#109;&#107;&#111;&#114;&#46;&#99;&#111;&#109;&#100;&#99;&#114;&#97;&#119;&#102;&#111;&#114;&#100;&#64;&#97;&#99;&#116;&#101;&#107;&#115;&#111;&#102;&#116;&#46;&#99;&#111;&#109;)
513 * @version 2, March 19, 2003
514 */
515function arrayOfStructuresToQuery(theArray){
516var colNames = "";
517var theQuery = queryNew("");
518var i=0;
519var j=0;
520//if there's nothing in the array, return the empty query
521if(NOT arrayLen(theArray))
522return theQuery;
523//get the column names into an array =
524colNames = structKeyArray(theArray[1]);
525//build the query based on the colNames
526theQuery = queryNew(arrayToList(colNames));
527//add the right number of rows to the query
528queryAddRow(theQuery, arrayLen(theArray));
529//for each element in the array, loop through the columns, populating the query
530for(i=1; i LTE arrayLen(theArray); i=i+1){
531for(j=1; j LTE arrayLen(colNames); j=j+1){
532querySetCell(theQuery, colNames[j], theArray[i][colNames[j]], i);
533}
534}
535return theQuery;
536}
537
538/**
539 * Reverses the order of elements in a one-dimensional array.
540 *
541 * @param InArray One-dimensional array to be reversed.
542 * @return Returna a new one dimensional array.
543 * @author Raymond Simmons (&#114;&#97;&#121;&#109;&#111;&#110;&#100;&#64;&#116;&#101;&#114;&#114;&#97;&#105;&#110;&#99;&#111;&#103;&#110;&#105;&#116;&#97;&#46;&#99;&#111;&#109;)
544 * @version 1.0, October 9, 2001
545 */
546function ArrayReverse(inArray){
547var outArray = ArrayNew(1);
548var i=0;
549        var j = 1;
550for (i=ArrayLen(inArray);i GT 0;i=i-1){
551outArray[j] = inArray[i];
552j = j + 1;
553}
554return outArray;
555}
556
557/**
558 * Shuffles the values in a one-dimensional array.
559 *
560 * @param ar One dimensional array you want shuffled.
561 * @return Returns an array.
562 * @author Ivan Latunov (&#105;&#118;&#97;&#110;&#64;&#99;&#102;&#99;&#104;&#97;&#116;&#46;&#110;&#101;&#116;)
563 * @version 1, October 9, 2001
564 */
565function ArrayShuffle(ar) {
566var ar1=ArrayNew(1);
567var i=1;
568var n=1;
569var len=ArrayLen(ar);
570if (NOT IsArray(ar,1)) {
571writeoutput("Error in <Code>ArrayShuffle()</code>! Correct usage: ArrayShuffle(<I>Array</I>) - Shuffles the values in one dimensional Array");
572return 0;
573}
574
575if (ArrayLen(ar) eq 0) {
576return ar1;
577}
578
579for (i=1; i lte len; i=i+1) {
580n = RandRange(1,ArrayLen(ar));
581ArrayAppend(ar1,ar[n]);
582ArrayDeleteAt(ar,n);
583}
584return ar1;
585}
586
587/**
588 * Slices an array.
589 *
590 * @param ary The array to slice. (Required)
591 * @param start The index to start with. Defaults to 1. (Optional)
592 * @param finish The index to end with. Defaults to the end of the array. (Optional)
593 * @return Returns an array.
594 * @author Darrell Maples (&#100;&#114;&#109;&#97;&#112;&#108;&#101;&#115;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
595 * @version 1, July 13, 2005
596 */
597function arraySlice(ary) {
598var start = 1;
599var finish = arrayLen(ary);
600var slice = arrayNew(1);
601var j = 1;
602
603if (len(arguments[2])) { start = arguments[2]; };
604if (len(arguments[3])) { finish = arguments[3]; };
605
606for (j=start; j LTE finish; j=j+1) {
607arrayAppend(slice, ary[j]);
608}
609return slice;
610}
611
612/**
613 * Sorts a two dimensional array by the specified column in the second dimension.
614 *
615 * @return Returns an array.
616 * @author Robert West (&#114;&#111;&#98;&#101;&#114;&#116;&#46;&#119;&#101;&#115;&#116;&#64;&#100;&#105;&#103;&#105;&#112;&#104;&#105;&#108;&#105;&#99;&#46;&#99;&#111;&#109;)
617 * @version 1, October 8, 2002
618 */
619function ArraySort2D(arrayToSort, sortColumn, type) {
620var order = "asc";
621var i = 1;
622var j = 1;
623var thePosition = "";
624var theList = "";
625var arrayToReturn = ArrayNew(2);
626var sortArray = ArrayNew(1);
627var counter = 1;
628if (ArrayLen(Arguments) GT 3){
629order = Arguments[4];
630}
631for (i=1; i LTE ArrayLen(arrayToSort); i=i+1) {
632ArrayAppend(sortArray, arrayToSort[i][sortColumn]);
633}
634theList = ArrayToList(sortArray);
635ArraySort(sortArray, type, order);
636for (i=1; i LTE ArrayLen(sortArray); i=i+1) {
637thePosition = ListFind(theList, sortArray[i]);
638theList = ListDeleteAt(theList, thePosition);
639for (j=1; j LTE ArrayLen(arrayToSort[thePosition]); j=j+1) {
640arrayToReturn[counter][j] = arrayToSort[thePosition][j];
641}
642ArrayDeleteAt(arrayToSort, thePosition);
643counter = counter + 1;
644}
645return arrayToReturn;
646}
647
648/**
649 * Complex variable checking with a single function call.
650 * Version 2 by Michael Wolfe, &#109;&#105;&#107;&#101;&#121;&#64;&#109;&#105;&#107;&#101;&#121;&#99;&#101;&#110;&#116;&#114;&#97;&#108;&#46;&#99;&#111;&#109;. Returns false earlier.
651 *
652 * @param assertion Assertion rule string you want to validate against. Variable names should be delimited by the pipe (|). (Required)
653 * @return Returns a Boolean value.
654 * @author Dean Chalk (&#109;&#105;&#107;&#101;&#121;&#64;&#109;&#105;&#107;&#101;&#121;&#99;&#101;&#110;&#116;&#114;&#97;&#108;&#46;&#99;&#111;&#109;&#100;&#99;&#104;&#97;&#108;&#107;&#57;&#57;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
655 * @version 2, September 23, 2004
656 */
657function assert(assertion) {
658var result = 1;
659var loopvar1 = 0;
660var loopvar2 = 0;
661var variableassertion = "";
662var varsection = "";
663var varname = "";
664var varalias = "";
665var assertionsection = "";
666var anassertion = "";
667var assertnow = "";
668var doBreak = false;
669
670for(loopvar1 = 1; loopvar1 LTE listlen(assertion, "!"); loopvar1 = incrementvalue(loopvar1)) {
671variableassertion = listgetat(assertion, loopvar1, "!");
672varsection = trim(gettoken(variableassertion, 1, ":"));
673varname = trim(listfirst(varsection, " "));
674varalias = trim(listlast(varsection, " "));
675assertionsection = trim(gettoken(variableassertion, 2, ":"));
676
677for(loopvar2 = 1; loopvar2 LTE listlen(assertionsection, ";"); loopvar2 = incrementvalue(loopvar2)) {
678anassertion = listgetat(assertionsection, loopvar2, ";");
679
680if(not isdefined(varname)){
681result = 0;
682doBreak = true;
683break;
684} else {
685assertnow = replacenocase(anassertion, "|#varalias#|", varname, "ALL");
686
687if(not(evaluate(trim(assertnow)))){
688result = 0;
689doBreak = true;
690break;
691}
692}
693}
694
695if(doBreak){
696break;
697}
698}
699
700return result;
701}
702
703/**
704 * Simply converts access yes/no or other boolean variables to 0/1 format, almost opposite of yesnoformat
705 *
706 * @param value The value to convert. (Optional)
707 * @return Returns 1 or 0.
708 * @author Craig M. Rosenblum (&#99;&#114;&#111;&#115;&#101;&#110;&#98;&#108;&#117;&#109;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
709 * @version 1, July 3, 2006
710 */
711function booleanize(value) {
712if (not isboolean(value)) {
713value = replacenocase(value,'on',1);
714value = replacenocase(value,'off',0);
715}
716if (yesnoformat(value) eq 'Yes') value = 1;
717if (yesnoformat(value) eq 'No') value = 0;
718return value;
719}
720
721/**
722 * Mocks the CFQUERY tag.
723 *
724 * @param dsn Datasource. Must be registered in the ODBC Control Panel. (Required)
725 * @param col List of columns. (Required)
726 * @param sql Sql to use. (Required)
727 * @return Returns a query.
728 * @author Joe Nicora (&#106;&#111;&#101;&#64;&#115;&#101;&#101;&#109;&#101;&#99;&#114;&#101;&#97;&#116;&#101;&#46;&#99;&#111;&#109;)
729 * @version 1, September 21, 2004
730 */
731function cfquery(dsn,col,sql) {
732    var datasource = dsn;
733    var userName = "";
734    var password = "";
735    var adOpenStatic = 3;
736    var adLockReadOnly=1;
737    var adCmdTxt = 1;
738    var adGetRowsRest = -1;
739    var columns = listToArray(col);
740    var strSQL = sql;
741    var objDataConn = CreateObject("COM", "ADODB.Connection");
742    var objDataRst = "";
743    var intRecordCount = "";
744    var arrRst = "";
745    var qry = queryNew(arrayToList(columns));
746    var thisCol = "";
747    var thisRow = "";
748
749    objDataConn.Open(Datasource, userName, password, -1);
750    objDataRst = CreateObject("COM", "ADODB.Recordset");
751    objDataRst.open(strSQL, objDataConn, adOpenStatic, adLockReadOnly, adCmdTxt);
752    intRecordCount = objDataRst.RecordCount;
753    arrRst = objDataRst.GetRows(adGetRowsRest);
754
755    queryAddRow(qry,intRecordCount);
756    for (thisCol=1; thisCol LTE arrayLen(columns); thisCol=thisCol+1) {
757        for (thisRow=1; thisRow LTE arrayLen(arrRst[thisCol]); thisRow=thisRow+1) {
758querySetCell(qry,columns[thisCol],arrRst[thisCol][thisRow],thisRow);
759}
760    }
761    objDataRST.close();
762    objDataConn.close();
763    return qry;
764}
765
766/**
767 * Applies simple evaluations to every cell in a query column.
768 *
769 * @param query Query object. (Required)
770 * @param columnName Column to be modified. (Required)
771 * @param theEval Evaluation to be performed. Use X to represent column data. (Required)
772 * @return Returns a query.
773 * @author Shawn Seley (&#115;&#104;&#97;&#119;&#110;&#115;&#101;&#64;&#97;&#111;&#108;&#46;&#99;&#111;&#109;)
774 * @version 1, September 12, 2003
775 */
776function ColumnLoop(query, columnName, theEval) {
777var row = 0;
778var x = "";
779var rec_count = query.recordCount;
780for(row=1; row LTE rec_count; row=row+1) {
781x = query[columnName][row];
782querySetCell(query,columnname,evaluate(theEval),row);
783}
784return query;
785}
786
787/**
788 * This UDF calculates the total of a column from a query.
789 * Version 2 by Raymond Camden
790 *
791 * @param qryColumn The name and column of the query, i.e. foo.total (Required)
792 * @return Returns a number.
793 * @author Scott Barber (&#99;&#104;&#97;&#114;&#108;&#101;&#115;&#98;&#97;&#114;&#98;&#101;&#114;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
794 * @version 2, May 13, 2003
795 */
796function columnTotal(qryColumn){
797return arraySum(listToArray(evaluate("valueList(" & qryColumn & ")")));
798}
799
800/**
801 * Creates a CFC instance based upon a relative, absolute or dot notation path.
802 *
803 * @param path Path for the component. (Required)
804 * @param type Type of the path. Possible values are "component" (normal dot notation), "relative" and "absolute". Defaults to component. (Optional)
805 * @return Returns a CFC.
806 * @author Dan G. Switzer, II (&#100;&#115;&#119;&#105;&#116;&#122;&#101;&#114;&#64;&#112;&#101;&#110;&#103;&#111;&#119;&#111;&#114;&#107;&#115;&#46;&#99;&#111;&#109;)
807 * @version 1, May 13, 2003
808 */
809function component(path){
810var sPath=Arguments.path;var oProxy="";var oFile="";var sType="";
811if( arrayLen(Arguments) gt 1 ) sType = lCase(Arguments[2]);
812
813// determine a default type
814if( len(sType) eq 0 ){
815if( (sPath DOES NOT CONTAIN ".") OR ((sPath CONTAINS ".") AND (sPath DOES NOT CONTAIN "/") AND (sPath DOES NOT CONTAIN "\")) ) sType = "component";
816else sType = "relative";
817}
818
819// create the component
820switch( left(sType,1) ){
821case "c":
822return createObject("component", sPath);
823break;
824
825default:
826if( left(sType, 1) neq "a" ) sPath = expandPath(sPath);
827oProxy = createObject("java", "coldfusion.runtime.TemplateProxy");
828oFile = createObject("java", "java.io.File");
829oFile.init(sPath);
830return oProxy.resolveFile(getPageContext(), oFile);
831break;
832}
833}
834
835/**
836 * Simulate the c functionality of i--.
837 *
838 * @param intCounter The name, not the value, of the variable to be decremented.
839 * @return Returns the value of the variable BEFORE it has been decremented.
840 * @author Stephan Scheele (&#115;&#116;&#101;&#112;&#104;&#97;&#110;&#64;&#115;&#116;&#101;&#112;&#104;&#97;&#110;&#45;&#116;&#45;&#115;&#99;&#104;&#101;&#101;&#108;&#101;&#46;&#100;&#101;)
841 * @version 1, April 19, 2002
842 */
843function counterMinusMinus(intCounter) {
844var temp = evaluate(intCounter);
845"#intCounter#" = temp - 1;
846return temp;
847}
848
849/**
850 * Simulate the c functionality of i++.
851 *
852 * @param intCounter The name, not the value, of the variable to be incremented.
853 * @return Returns the value of the variable BEFORE it was incremented.
854 * @author Stephan Scheele (&#115;&#116;&#101;&#112;&#104;&#97;&#110;&#64;&#115;&#116;&#101;&#112;&#104;&#97;&#110;&#45;&#116;&#45;&#115;&#99;&#104;&#101;&#101;&#108;&#101;&#46;&#100;&#101;)
855 * @version 1, April 17, 2002
856 */
857function counterPlusPlus(intCounter){
858var temp = evaluate(intCounter);
859"#intCounter#" = temp + 1;
860return temp;
861}
862
863/**
864 * CSVFormat accepts the name of an existing query and converts it to csv format.
865 * Updated version of UDF orig. written by Simon Horwith
866 *
867 * @param query The query to format.
868 * @param qualifer A string to qualify the data with.
869 * @param columns The columns ot use. Defaults to all columns.
870 * @return A CSV formatted string.
871 * @author Jeff Howden (&#106;&#101;&#102;&#102;&#64;&#109;&#101;&#109;&#98;&#101;&#114;&#115;&#46;&#101;&#118;&#111;&#108;&#116;&#46;&#111;&#114;&#103;)
872 * @version 2, December 3, 2001
873 */
874function CSVFormat(query)
875{
876  var returnValue = ArrayNew(1);
877  var rowValue = '';
878  var columns = query.columnlist;
879  var qualifier = '';
880  var i = 1;
881  var j = 1;
882  if(ArrayLen(Arguments) GTE 2) qualifier = Arguments[2];
883  if(ArrayLen(Arguments) GTE 3 AND Len(Arguments[3])) columns = Arguments[3];
884  returnValue[1] = ListQualify(columns, qualifier);
885  ArrayResize(returnValue, query.recordcount + 1);
886  columns = ListToArray(columns);
887  for(i = 1; i LTE query.recordcount; i = i + 1)
888  {
889    rowValue = ArrayNew(1);
890    ArrayResize(rowValue, ArrayLen(columns));
891    for(j = 1; j LTE ArrayLen(columns); j = j + 1)
892      rowValue[j] = qualifier & query[columns[j]][i] & qualifier;
893    returnValue[i + 1] = ArrayToList(rowValue);
894  }
895  returnValue = ArrayToList(returnValue, Chr(13));
896  return returnValue;
897}
898
899/**
900 * Transform a CSV formatted string with header column into a query object.
901 *
902 * @param cvsString CVS Data. (Required)
903 * @param rowDelim Row delimiter. Defaults to CHR(10). (Optional)
904 * @param colDelim Column delimiter. Defaults to a comma. (Optional)
905 * @return Returns a query.
906 * @author Tony Brandner (&#116;&#111;&#110;&#121;&#64;&#98;&#114;&#97;&#110;&#100;&#110;&#101;&#114;&#115;&#46;&#99;&#111;&#109;)
907 * @version 1, September 30, 2005
908 */
909function csvToQuery(csvString){
910var rowDelim = chr(10);
911var colDelim = ",";
912var numCols = 1;
913var newQuery = QueryNew("");
914var arrayCol = ArrayNew(1);
915var i = 1;
916var j = 1;
917
918csvString = trim(csvString);
919
920if(arrayLen(arguments) GE 2) rowDelim = arguments[2];
921if(arrayLen(arguments) GE 3) colDelim = arguments[3];
922
923arrayCol = listToArray(listFirst(csvString,rowDelim),colDelim);
924
925for(i=1; i le arrayLen(arrayCol); i=i+1) queryAddColumn(newQuery, arrayCol[i], ArrayNew(1));
926
927for(i=2; i le listLen(csvString,rowDelim); i=i+1) {
928queryAddRow(newQuery);
929for(j=1; j le arrayLen(arrayCol); j=j+1) {
930if(listLen(listGetAt(csvString,i,rowDelim),colDelim) ge j) {
931querySetCell(newQuery, arrayCol[j],listGetAt(listGetAt(csvString,i,rowDelim),j,colDelim), i-1);
932}
933}
934}
935return newQuery;
936}
937
938/**
939 * Counts the number of keys in a structure of structures.
940 * Added missing return statement (rkc)
941 *
942 * @param myStruct The structure to examine. (Required)
943 * @return Returns a numeric value.
944 * @author Galen Smallen (&#103;&#97;&#108;&#101;&#110;&#64;&#111;&#110;&#99;&#108;&#105;&#46;&#99;&#111;&#109;)
945 * @version 2, August 23, 2002
946 */
947function deepStructCount(myStruct) {
948    var deepCount=0;
949    var x = "";
950    var i = "";
951       
952    for (x in myStruct) {
953        if(isArray(myStruct[x])) {
954            for(i=1; i lte arrayLen(myStruct[x]); i=i+1) {
955                if(isStruct(myStruct[x][i])) deepCount = deepCount+deepStructCount(myStruct[x][i]);
956            }
957        } else if (isStruct(myStruct[x])) {
958            deepCount=deepCount+deepStructCount(myStruct[x]);
959        } else {
960            deepCount=deepCount+1;
961        }
962    }
963return deepCount;
964}
965
966/**
967 * Displays contents of any data type except WDDX.
968 *
969 * @param varToProcess The variable to dump.
970 * @return Returns a string.
971 * @author Chris Benson (&#97;&#105;&#114;&#102;&#111;&#111;&#102;&#64;&#121;&#97;&#104;&#111;&#111;&#46;&#99;&#111;&#109;)
972 * @version 1, April 23, 2002
973 */
974function DumpVar(varToProcess){
975var structLoopCount = 0;
976var LoopCount = 0;
977var ObjSize = 0;
978var key = "";
979        var keys = "";
980var numOfColumns = 0;
981        var count2 = 0;
982       
983var StartString = "";
984var EndString = "</table>#chr(10)#";
985if(isSimpleValue(varToProcess)){
986if(isWDDX(varToProcess)){
987StartString = "#chr(10)#<table bordercolor='black' border='1' cellspacing='0' cellpadding='1'>#chr(10)#";
988
989return StartString & "<tr>#chr(10)#<td>WDDX currently not displayable</td>#chr(10)#</tr>#chr(10)#" & EndString;
990}else{
991return varToProcess;
992}
993}else if(isArray(varToProcess)){
994StartString = "#chr(10)#<table bordercolor='##008000' border='1' cellspacing='0' cellpadding='1'>#chr(10)#";
995ObjSize = ArrayLen(varToProcess);
996
997for(LoopCount = 1;LoopCount LTE ObjSize;LoopCount = LoopCount + 1){
998StartString = StartString & "<tr>#chr(10)#<td bgcolor='##cceecc' valign='top'>#LoopCount#</td><td>#dumpVar(varToProcess[LoopCount])#</td>#chr(10)#</tr>#chr(10)#";
999}
1000return StartString & EndString;
1001}else if(isStruct(varToProcess)){
1002StartString = "#chr(10)#<table bordercolor='blue' border='1' cellspacing='0' cellpadding='1'>#chr(10)#";
1003
1004for(key in varToProcess){
1005StartString = StartString & "<tr>#chr(10)#<td bgcolor='##aaaaee' valign='top'>#key#</td>#chr(10)#<td>#dumpVar(varToProcess[key])#</td>#chr(10)#</tr>#chr(10)#";
1006}
1007return StartString & EndString;
1008}else if(isQuery(varToProcess)){
1009StartString = "#chr(10)#<table bordercolor='red' border='1' cellspacing='0' cellpadding='1'>#chr(10)#";
1010ObjSize = varToProcess.recordCount;
1011Keys = varToProcess.columnList;
1012numOfColumns = ListLen(Keys);
1013StartString = StartString & "<tr>#chr(10)#";
1014
1015for(count2 = 1;count2 LTE numOfColumns;count2 = count2 + 1){
1016StartString = StartString & "<td bgcolor='##eeaaaa'>#listGetAt(Keys,count2)#</td>#chr(10)#";
1017}
1018StartString = StartString & "</tr>#chr(10)#";
1019
1020for(LoopCount = 1;LoopCount LTE ObjSize;LoopCount = LoopCount + 1){
1021StartString = StartString & "<tr>#chr(10)#";
1022for(count2 = 1;count2 LTE numOfColumns;count2 = count2 + 1){
1023StartString = StartString & "<td>#varToProcess[listGetAt(Keys,count2)][loopCount]#</td>#chr(10)#";
1024}
1025StartString = StartString & "</tr>#chr(10)#";
1026}
1027return StartString & EndString;
1028}else{
1029return " ";
1030}
1031}
1032
1033/**
1034 * Returns a value list from a dynamic column of a query.
1035 *
1036 * @param query The query to examine. (Required)
1037 * @param col The column to return values for. (Required)
1038 * @param delim Delimiter. Defaults to comma. (Optional)
1039 * @return Returns a list.
1040 * @author Raymond Camden (&#114;&#97;&#121;&#64;&#99;&#97;&#109;&#100;&#101;&#110;&#102;&#97;&#109;&#105;&#108;&#121;&#46;&#99;&#111;&#109;)
1041 * @version 1, August 14, 2002
1042 */
1043function DynamicValueList(query,col) {
1044var delim = ",";
1045if(arrayLen(arguments) gte 3) delim = arguments[3];
1046return arrayToList(query[col],delim);
1047}
1048
1049/**
1050 * Removes the element at index one and inserts a new element at the highest index plus one.
1051 *
1052 * @param array Array to modify. (Required)
1053 * @param valueToAdd Value to add. (Required)
1054 * @return Returns an array.
1055 * @author Adrian Lynch (&#97;&#100;&#114;&#105;&#97;&#110;&#46;&#108;&#64;&#116;&#104;&#111;&#117;&#103;&#104;&#116;&#98;&#117;&#98;&#98;&#108;&#101;&#46;&#110;&#101;&#116;)
1056 * @version 1, May 13, 2003
1057 */
1058function FirstInFirstOut( array, valueToAdd ) {
1059
1060// Delete element at index 1
1061ArrayDeleteAt( array, 1 );
1062
1063// Add new element at last index plus one
1064array[ArrayLen( array ) + 1] = valueToAdd;
1065
1066return array;
1067
1068}
1069
1070/**
1071 * Converts form variables to query string.
1072 * Modified by RCamden
1073 *
1074 * @return Returns a string.
1075 * @author Billy Cravens (&#98;&#105;&#108;&#108;&#121;&#64;&#97;&#114;&#99;&#104;&#105;&#116;&#101;&#99;&#104;&#120;&#46;&#99;&#111;&#109;)
1076 * @version 1, June 26, 2002
1077 */
1078function form2qs() {
1079var str = "";
1080var field = "";
1081for(key in form) {
1082str = str & "&#key#=" & urlEncodedFormat(form[key]);
1083}
1084return str;
1085}
1086
1087/**
1088 * Examine the contents of a BINARY file.
1089 *
1090 * @param BVar Binary variable. (Required)
1091 * @param loc Location in the binary file. (Required)
1092 * @return Returns a string.
1093 * @author John Bartlett (&#106;&#98;&#97;&#114;&#116;&#108;&#101;&#116;&#116;&#64;&#115;&#116;&#114;&#97;&#110;&#103;&#101;&#106;&#111;&#117;&#114;&#110;&#101;&#121;&#46;&#110;&#101;&#116;)
1094 * @version 1, November 15, 2002
1095 */
1096function Get(BVar,loc) {
1097if (isBinary(BVar) EQ "No") return 0;
1098if (BVar[loc] GTE 0) return BVar[loc];
1099return BVar[loc] + 256;
1100}
1101
1102/**
1103 * Returns an array of all properties in cfc's metadata, inherited or not.
1104 *
1105 * @param object The metadata from a component. (Required)
1106 * @return Returns an array of structs.
1107 * @author Robby Lansaw (&#114;&#111;&#98;&#98;&#121;&#64;&#111;&#104;&#115;&#111;&#103;&#111;&#111;&#101;&#121;&#46;&#99;&#111;&#109;)
1108 * @version 1, August 18, 2004
1109 */
1110function getComponentProps(object) {
1111var propArray = arrayNew(1);
1112var internalArray = arrayNew(1);
1113var i = '';
1114var j = '';
1115
1116if (structKeyExists(object,'properties')) {
1117for (i=1; i lte arraylen(object.properties);i = i+1) {
1118arrayappend(propArray,object.properties[i]);
1119propArray[arrayLen(propArray)].foundin = object.name;
1120}
1121}
1122if (listlast(object.extends.name,'.' ) neq 'component'){
1123internalArray = getComponentProps(object.extend );
1124for (j=1;j lte arraylen(internalArray);j = j+1){
1125arrayappend(propArray,internalArray[j]);
1126}
1127}
1128return propArray;
1129}
1130
1131/**
1132 * Accepts a numeric GUID stored in a Byte Array and converts it to a string in the normal convention.
1133 *
1134 * @param guidByteArray GUID Byte array returned from a query. (Required)
1135 * @return Returns a string.
1136 * @author Samuel Neff (&#115;&#97;&#109;&#64;&#98;&#108;&#105;&#110;&#101;&#120;&#46;&#99;&#111;&#109;)
1137 * @version 1, September 6, 2002
1138 */
1139function guidToString(guidByteArray) {
1140   var hexString='';
1141   
1142   if (IsArray(guidByteArray) AND ArrayLen(guidByteArray) GTE 16) {
1143     hexString=hexString & guidByteToHex(guidByteArray[4]);
1144     hexString=hexString & guidByteToHex(guidByteArray[3]);
1145     hexString=hexString & guidByteToHex(guidByteArray[2]);
1146     hexString=hexString & guidByteToHex(guidByteArray[1]);
1147     hexString=hexString & "-";
1148     hexString=hexString & guidByteToHex(guidByteArray[6]);
1149     hexString=hexString & guidByteToHex(guidByteArray[5]);
1150     hexString=hexString & "-";
1151     hexString=hexString & guidByteToHex(guidByteArray[8]);
1152     hexString=hexString & guidByteToHex(guidByteArray[7]);
1153     hexString=hexString & "-";
1154     hexString=hexString & guidByteToHex(guidByteArray[9]);
1155     hexString=hexString & guidByteToHex(guidByteArray[10]);
1156     hexString=hexString & "-";
1157     hexString=hexString & guidByteToHex(guidByteArray[11]);
1158     hexString=hexString & guidByteToHex(guidByteArray[12]);
1159     hexString=hexString & guidByteToHex(guidByteArray[13]);
1160     hexString=hexString & guidByteToHex(guidByteArray[14]);
1161     hexString=hexString & guidByteToHex(guidByteArray[15]);
1162     hexString=hexString & guidByteToHex(guidByteArray[16]);
1163   }
1164   
1165   return hexString;
1166}
1167
1168function guidByteToHex(guidByte) {
1169   // Accepts a single byte and converts it to a two digit Hex number.
1170   
1171   var hexByte=Ucase(Right(FormatBaseN(guidByte, 16),2));
1172   if (Len(hexByte) IS 0) {
1173      hexByte='00';
1174   } else if (Len(hexByte) IS 1) {
1175      hexByte='0' & hexByte;
1176   }
1177   
1178   return hexByte;
1179}
1180
1181/**
1182 * Checks that a value is equal to 1 or 0.
1183 *
1184 * @param x Value to check. (Required)
1185 * @return Returns a boolean.
1186 * @author Mike Tangorre (&#109;&#116;&#97;&#110;&#103;&#111;&#114;&#114;&#101;&#64;&#99;&#102;&#99;&#111;&#100;&#101;&#114;&#46;&#99;&#111;&#109;)
1187 * @version 1, February 14, 2006
1188 */
1189function isBit(x){
1190   if(isSimpleValue(x) and len(x) eq 1 and (x eq 0 or x eq 1))
1191      return true;
1192   else
1193      return false;
1194}
1195
1196/**
1197 * Returns a boolean for whether a CF variable is a CFC instance.
1198 *
1199 * @param objectToCheck The object to check. (Required)
1200 * @return Returns a boolean.
1201 * @author Nathan Dintenfass (&#110;&#97;&#116;&#104;&#97;&#110;&#64;&#99;&#104;&#97;&#110;&#103;&#101;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
1202 * @version 1, October 16, 2002
1203 */
1204function IsCFC(objectToCheck){
1205//get the meta data of the object we're inspecting
1206var metaData = getMetaData(arguments.objectToCheck);
1207//if it's an object, let's try getting the meta Data
1208if(isObject(arguments.objectToCheck)){
1209//if it has a type, and that type is "component", then it's a component
1210if(structKeyExists(metaData,"type") AND metaData.type is "component"){
1211return true;
1212}
1213}
1214//if we've gotten here, it must not have been a contentObject
1215return false;
1216}
1217
1218/**
1219 * Checks if a given variable is a specific CFC type
1220 *
1221 * @param objectToCheck CFC instance to check. (Required)
1222 * @param type String name of CFC. (Required)
1223 * @param checkFullName Boolean specifying if type should only match for the full name of the CFC. Defaults to false. If any value is passed, checkFullname is true. (Optional)
1224 * @return Returns a boolean.
1225 * @author Nathan Dintenfass (&#110;&#97;&#116;&#104;&#97;&#110;&#64;&#99;&#104;&#97;&#110;&#103;&#101;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
1226 * @version 1, December 23, 2002
1227 */
1228function isCFCType(objectToCheck,type){
1229//get the meta data of the object we're inspecting (we use duplicate so we don't mess with the instance)
1230var metaData = getMetaData(arguments.objectToCheck);
1231//are we going to check for a full name, or just the end?
1232var checkFullName = true;
1233//which component are we checking? (used to allow traversing the "extends" for extended components)
1234var metaToCheck = metaData;
1235//which name shall we check?
1236var nameToCheck = metaData.name;
1237//if the arguments.type has no periods, don't check the full name
1238if(listLen(arguments.type,".") LTE 1)
1239checkFullName = false;
1240//allow a third argument to force the checkFullName
1241if(structCount(arguments) GT 2)
1242checkFullName = arguments[3];
1243//if it's an object, see if it's the right kind of component
1244if(isObject(arguments.objectToCheck)){
1245//if it has a type, and that type is "component", then it's a component, so we then look at the type
1246if(structKeyExists(metaData,"type") AND metaData.type is "component"){
1247//do a while loop to be sure we see if this component extends the type we want
1248while(structKeyExists(metaToCheck,"extends")){
1249//if we are not checking the full name, then take only the last element in the full name
1250if(NOT checkFullName)
1251nameToCheck = listLast(metaToCheck.name,".");
1252else
1253nameToCheck = metaToCheck.name;
1254//if the name of the component we're looking at is the type we're looking for, return true
1255if(nameToCheck is arguments.type)
1256return true;
1257//set this to the extends of the current component to traverse the meta data tree
1258metaToCheck = metaToCheck.extends;
1259}
1260}
1261}
1262//if we've gotten here, it must not have been a the right kind of object
1263return false;
1264}
1265
1266/**
1267 * Checks that a variable is defined and that the variable is not an empty value. Optionally lets you check that the variable is a specific value.
1268 * See also: isEmpty() -- http://cflib.org/udf.cfm?id=420
1269 *
1270 * @param varname Name of the variable to check for (Required)
1271 * @param value The value a simple variable should be to pass the test (optional) (Optional)
1272 * @return boolean - 1 or 0
1273 * @author Joseph Flanigan (&#106;&#111;&#115;&#101;&#112;&#104;&#64;&#115;&#119;&#105;&#116;&#99;&#104;&#45;&#98;&#111;&#120;&#46;&#111;&#114;&#103;)
1274 * @version 1, August 18, 2003
1275 */
1276function isDefinedValue(varname)
1277{
1278  var value = "";
1279    if (IsDefined(listfirst(Arguments[1],"[")))
1280     {
1281     value = evaluate(Arguments[1]);
1282     if (IsSimpleValue(value))
1283        {
1284            if (ArrayLen(Arguments) EQ 2 )
1285                { if ( value EQ Arguments[2]){return 1;}
1286                else return 0;
1287                }
1288            else if ( find(value,"" )) {return 0;}
1289            else return 1; // something is there, just not testing for it.
1290        }
1291     else if (IsStruct(value))
1292        {
1293            if (StructIsEmpty(value)) { return 0;}
1294            else {return 1;}
1295        }
1296     else if (IsArray(value))
1297        {
1298            if (ArrayIsEmpty(value)) {return 0;}
1299            else {return 1;}
1300        }
1301     else if (IsQuery(value))
1302        {
1303            if (YesNoFormat(value.recordcount)) {return 1;}
1304            else {return 0;}
1305        }
1306    return 0;
1307      }
1308return 0;
1309}
1310
1311/**
1312 * Checks that a variable exists and has value. CFMX version.
1313 *
1314 * @param varname The name of the variable to test for (Required)
1315 * @param value The value a simple variable should be to pass the test (optional) (Optional)
1316 * @return 1 (yes, it is defined) or 0 (no, it is not defined)
1317 * @author Joseph Flanigan (&#106;&#111;&#115;&#101;&#112;&#104;&#64;&#115;&#119;&#105;&#116;&#99;&#104;&#45;&#98;&#111;&#120;&#46;&#111;&#114;&#103;)
1318 * @version 1, October 20, 2003
1319 */
1320function isDefinedValueMX(varname)
1321{
1322  var varvalue = "";
1323    try{
1324    if (IsDefined(listfirst(Arguments[1],"[")))
1325     {
1326     varvalue = evaluate(Arguments[1]);
1327
1328     if (IsSimpleValue(varvalue))
1329        {
1330            if (ArrayLen(Arguments) EQ 2 )
1331                { if ( varvalue EQ Arguments[2]){return 1;}
1332                else return 0;
1333                }
1334            else if ( find(varvalue,"" )) {return 0;}
1335            else return 1; // something is there, just not testing for it.
1336        }
1337     else if (IsStruct(varvalue))
1338        {
1339            if (StructIsEmpty(varvalue)) { return 0;}
1340            else {return 1;}
1341        }
1342     else if (IsArray(varvalue))
1343        {
1344            if (ArrayIsEmpty(varvalue)) {return 0;}
1345            else {return 1;}
1346        }
1347     else if (IsQuery(varvalue))
1348        {
1349            if (YesNoFormat(varvalue.recordcount)) {return 1;}
1350            else {return 0;}
1351        }
1352    return 0; // not defined
1353      }
1354     } //try
1355     catch(Any excpt)
1356      { return 0;} // return excpt.Message;
1357return 0;
1358}
1359
1360/**
1361 * Check if a variable is set and has a value.
1362 * Mods by RCamden to add support for struct/query
1363 *
1364 * @param varName Variable to check for. (Required)
1365 * @return Returns a boolean.
1366 * @author Fabio Serra (&#102;&#97;&#115;&#101;&#114;&#64;&#102;&#97;&#115;&#101;&#114;&#46;&#110;&#101;&#116;)
1367 * @version 1, July 10, 2003
1368 */
1369function isEmpty(varName) {
1370var ptr = "";
1371
1372if(not isDefined(varName)) return true;
1373ptr = evaluate(varName);
1374
1375if(isSimpleValue(ptr)) {
1376if(not len(ptr)) return true;
1377} else if(isArray(ptr)) {
1378if(arrayIsEmpty(ptr)) return true;
1379} else if(isStruct(ptr)) {
1380if(structIsEmpty(ptr)) return true;
1381} else if(isQuery(ptr)) {
1382if(not ptr.recordCount) return true;
1383}
1384
1385return false;
1386}
1387
1388/**
1389 * Returns True if the value passed to it represents &quot;NULL&quot;.
1390 *
1391 * @param val Value to evaluate for NULL. (Required)
1392 * @param NullIdentifier String that represents NULL. Default is an empty string (""). (Optional)
1393 * @return Returns a Boolean.
1394 * @author Rob Brooks-Bilson (&#114;&#98;&#105;&#108;&#115;&#64;&#97;&#109;&#107;&#111;&#114;&#46;&#99;&#111;&#109;)
1395 * @version 1, May 1, 2002
1396 */
1397function IsNull(val){
1398  var NullIdentifier = "";
1399  if (ArrayLen(Arguments) gte 2)
1400    NullIdentifier = Arguments[2];
1401  if (val is NullIdentifier) {
1402    return True;
1403  }
1404  else {
1405    return False;
1406  }
1407}
1408
1409/**
1410 * Returns true if all positions in an array are defined.
1411 *
1412 * @param arr The array to check. (Required)
1413 * @return Returns a boolean.
1414 * @author Raymond Camden (&#114;&#97;&#121;&#64;&#99;&#97;&#109;&#100;&#101;&#110;&#102;&#97;&#109;&#105;&#108;&#121;&#46;&#99;&#111;&#109;)
1415 * @version 1, April 29, 2002
1416 */
1417function IsSafeArray(arr) {
1418var i=1;
1419var temp = "";
1420
1421for(i=1; i lte arrayLen(arr); i=i+1) {
1422try {
1423temp = arr[i];
1424} catch(coldfusion.runtime.UndefinedElementException ex) {
1425return false;
1426}
1427}
1428
1429return true;
1430}
1431
1432/**
1433 * Computes the length of every key in the passed structure and returns a structure with unique key names of the lengths.
1434 *
1435 * @param structIn The struct to check. (Required)
1436 * @param excludeList List of keys to ignore. (Optional)
1437 * @param ending String to append to key names in resulting struct. (Optional)
1438 * @return Returns a struct.
1439 * @author Peter J. Farrell (&#112;&#106;&#102;&#64;&#109;&#97;&#101;&#115;&#116;&#114;&#111;&#112;&#117;&#98;&#108;&#105;&#115;&#104;&#105;&#110;&#103;&#46;&#99;&#111;&#109;)
1440 * @version 1, October 5, 2004
1441 */
1442function LenStruct(structIn) {
1443var i = 0;
1444var structIn_count = StructCount(structIn);
1445var struct0ut = StructNew();
1446var ending = "_Len";
1447var excludeList = "";
1448var key = "";
1449
1450// Check if excludeList was passed
1451if(arrayLen(Arguments) GT 1) {
1452excludeList = Arguments[2];
1453}
1454
1455// Check if different ending was passed
1456if(arrayLen(Arguments) GT 2) {
1457ending = Arguments[3];
1458}
1459for (key IN structIn) {
1460if (NOT listFindNoCase(excludeList,key) AND isSimpleValue(structIn[key])) {
1461structOut[key&ending] = Len(structIn[key]);
1462}
1463}
1464return structOut;
1465}
1466
1467/**
1468 * Converts a delimited list of key/value pairs to a structure.
1469 *
1470 * @param list List of key/value pairs to initialize the structure with. Format follows key=value.
1471 * @param delimiter Delimiter seperating the key/value pairs. Default is the comma.
1472 * @return Returns a structure.
1473 * @author Rob Brooks-Bilson (&#114;&#98;&#105;&#108;&#115;&#64;&#97;&#109;&#107;&#111;&#114;&#46;&#99;&#111;&#109;)
1474 * @version 1.0, December 10, 2001
1475 */
1476function ListToStruct(list){
1477  var myStruct=StructNew();
1478  var i=0;
1479  var delimiter=",";
1480  if (ArrayLen(arguments) gt 1){
1481    delimiter = arguments[2];
1482  }
1483  for (i=1; i LTE ListLen(list, delimiter); i=i+1){
1484    StructInsert(myStruct, ListFirst(ListGetAt(list, i, delimiter), "="), ListLast(ListGetAt(list, i, delimiter), "="));
1485  }
1486  return myStruct;
1487}
1488
1489/**
1490 * Based on ListToStruct() from Rob Brooks-Bilson, this one allows the structure key to be repeated and the value added to a list.
1491 * Version 2 - Ray modified the code a bit and fixed a missing var.
1492 *
1493 * @param list List of key and value pairs. (Required)
1494 * @param delimiter List delimiter. Defaults to a comma. (Optional)
1495 * @return Returns a struct.
1496 * @author Tony Brandner (&#116;&#111;&#110;&#121;&#64;&#98;&#114;&#97;&#110;&#100;&#110;&#101;&#114;&#115;&#46;&#99;&#111;&#109;)
1497 * @version 2, August 3, 2005
1498 */
1499function listToStructRepeatKeys(list){
1500  var myStruct=StructNew();
1501  var i=0;
1502  var delimiter=",";
1503  var tempVar="";
1504
1505  if(arrayLen(arguments) gt 1) delimiter = arguments[2];
1506
1507  for(i=listLen(list, delimiter); i gt 0; i=i-1) {
1508   tempVar = trim(listGetAt(list, i, delimiter));
1509   if (structKeyExists(myStruct,listFirst(tempVar, "="))) {
1510myStruct[listFirst(tempVar, "=")] = listAppend(myStruct[listFirst(tempVar, "=")],listLast(tempVar, "="));
1511} else {
1512myStruct[listFirst(tempVar, "=")] = listLast(tempVar, "=");
1513}
1514  }
1515  return myStruct;
1516}
1517
1518/**
1519 * This function is a UDF for maketree custom tag developed by Michael Dinowitz.
1520 *
1521 * @param query Query to be sorted. (Required)
1522 * @param unique Name of the column containing the primary key. (Required)
1523 * @param parent Name of the column containing the parent. (Required)
1524 * @return Returns a query.
1525 * @author Qasim Rasheed (&#113;&#97;&#115;&#105;&#109;&#114;&#97;&#115;&#104;&#101;&#101;&#100;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
1526 * @version 1, February 17, 2005
1527 */
1528function maketree( query, unique, parent ){
1529var current = 0;
1530var path = 0;
1531var i = 0;
1532var j = 0;
1533var items = "";
1534var parents = "";
1535var position = "";
1536var column = "";
1537var retQuery = querynew( query.columnlist & ',sortlevel' );
1538for (i=1;i lte query.recordcount;i=i+1)
1539items = listappend( items, query[unique][i] );
1540for (i=1;i lte query.recordcount;i=i+1)
1541parents = listappend( parents, query[parent][i] );
1542
1543for (i=1;i lte query.recordcount;i=i+1){
1544queryaddrow( retQuery );
1545position = listfind( parents, current );
1546while (not position){
1547path= listrest( path );
1548current = listfirst( path );
1549position = listfind( parents, current );
1550}
1551for (j=1;j lte listlen( query.columnlist ); j=j+1){
1552column = listgetat( query.columnlist, j );
1553querysetcell( retQuery, column, evaluate( 'query.'&column&'[position]') );
1554}
1555querysetcell( retQuery, 'sortlevel', listlen( path ) );
1556current = listgetat( items, position );
1557parents = listsetat( parents, position, '-' );
1558path = listprepend( path, current);
1559}
1560return retQuery;
1561}
1562
1563/**
1564 * Simulate the c functionality of --i.
1565 *
1566 * @param intCounter The name, not the value, of the variable to be decremented.
1567 * @return Returns the value of the variable after it has been decremented.
1568 * @author Stephan Scheele (&#115;&#116;&#101;&#112;&#104;&#97;&#110;&#64;&#115;&#116;&#101;&#112;&#104;&#97;&#110;&#45;&#116;&#45;&#115;&#99;&#104;&#101;&#101;&#108;&#101;&#46;&#100;&#101;)
1569 * @version 1, April 17, 2002
1570 */
1571function minusMinusCounter(intCounter){
1572"#intCounter#" = evaluate(intCounter) - 1;
1573return evaluate(intCounter);
1574}
1575
1576/**
1577 * Initialize an empty query with default values.
1578 *
1579 * @param q Query. (Required)
1580 * @param Fields Fields to use. (Required)
1581 * @param Values Values to use. (Required)
1582 * @return Returns a query.
1583 * @author John Bartlett (&#106;&#98;&#97;&#114;&#116;&#108;&#101;&#116;&#116;&#64;&#115;&#116;&#114;&#97;&#110;&#103;&#101;&#106;&#111;&#117;&#114;&#110;&#101;&#121;&#46;&#110;&#101;&#116;)
1584 * @version 1, August 10, 2007
1585 */
1586function nullQuery(q,Fields,Values) {
1587var i=0;
1588var NewQ=QueryNew(Replace(Fields,"|",",","ALL"));
1589if (q.RecordCount GT 0) return q;
1590QueryAddRow(NewQ);
1591for(i=1; i LTE ListLen(Fields,'|'); i=i+1) {
1592querySetCell(NewQ,ListGetAt(Fields,i,'|'),ListGetAt(Values,i,'|'));
1593}
1594return NewQ;
1595}
1596
1597/**
1598 * Simulate the c functionality of ++i.
1599 *
1600 * @param intCounter The name, not the value, of the variable to be incremented.
1601 * @return Returns the value of the variable after it has been incremented.
1602 * @author Stephan Scheele (&#115;&#116;&#101;&#112;&#104;&#97;&#110;&#64;&#115;&#116;&#101;&#112;&#104;&#97;&#110;&#45;&#116;&#45;&#115;&#99;&#104;&#101;&#101;&#108;&#101;&#46;&#100;&#101;)
1603 * @version 1, April 17, 2002
1604 */
1605function plusPlusCounter(intCounter) {
1606"#intCounter#" = evaluate(intCounter) + 1;
1607return evaluate(intCounter);
1608}
1609
1610/**
1611 * Converts a query to excel-ready format.
1612 * Version 2 by Andrew Tyrone. It now returns a string instead of directly outputting.
1613 *
1614 * @param query The query to use. (Required)
1615 * @param headers A list of headers. Defaults to col. (Optional)
1616 * @param cols The columns of the query. Defaults to all columns. (Optional)
1617 * @param alternateColor The color to use for every other row. Defaults to white. (Optional)
1618 * @return Returns a string.
1619 * @author Jesse Monson (&#106;&#101;&#115;&#115;&#101;&#64;&#105;&#120;&#115;&#116;&#117;&#100;&#105;&#111;&#115;&#46;&#99;&#111;&#109;)
1620 * @version 2, February 23, 2005
1621 */
1622function Query2Excel(query) {
1623var InputColumnList = query.columnList;
1624var Headers = query.columnList;
1625
1626var AlternateColor = "FFFFFF";
1627var header = "";
1628var headerLen = 0;
1629var col = "";
1630var colValue = "";
1631var colLen = 0;
1632var i = 1;
1633var j = 1;
1634var k = 1;
1635
1636var HTMLData = "";
1637
1638if (arrayLen(arguments) gte 2) {
1639Headers = arguments[2];
1640}
1641if (arrayLen(arguments) gte 3) {
1642InputColumnList = arguments[3];
1643}
1644
1645if (arrayLen(arguments) gte 4) {
1646AlternateColor = arguments[4];
1647}
1648if (listLen(InputColumnList) neq listLen(Headers)) {
1649return "Input Column list and Header list are not of equal length";
1650}
1651
1652HTMLData = HTMLData & "<table border=1><tr bgcolor=""C0C0C0"">";
1653for (i=1;i lte ListLen(Headers);i=i+1){
1654header=listGetAt(Headers,i);
1655headerLen=Len(header)*10;
1656HTMLData = HTMLData & "<th width=""#headerLen#""><b>#header#</b></th>";
1657}
1658HTMLData = HTMLData & "</tr>";
1659for (j=1;j lte query.recordcount;j=j+1){
1660if (j mod 2) {
1661HTMLData = HTMLData & "<tr bgcolor=""FFFFFF"">";
1662} else {
1663HTMLData = HTMLData & "<tr bgcolor=""#alternatecolor#"">";
1664}
1665for (k=1;k lte ListLen(InputColumnList);k=k+1) {
1666col=ListGetAt(InputColumnList,k);
1667colValue=query[trim(col)][j];
1668colLength=Len(colValue)*10;
1669if (NOT Len(colValue)) {
1670colValue="&nbsp;";
1671}
1672if (isNumeric(colValue) and Len(colValue) gt 10) {
1673colValue="'#colValue#";
1674}
1675HTMLData = HTMLData & "<td width=""#colLength#"">#colValue#</td>";
1676}
1677HTMLData = HTMLData & "</tr>";
1678}
1679HTMLData = HTMLData & "</table>";
1680
1681return HTMLData;
1682}
1683
1684/**
1685 * Converts a Java QueryBean object to a ColdFusion query object.
1686 *
1687 * @param objQueryBean A Java QueryBean object. (Required)
1688 * @return Returns a query.
1689 * @author Alistair Davidson (&#97;&#108;&#105;&#115;&#116;&#97;&#105;&#114;&#95;&#100;&#97;&#118;&#105;&#100;&#115;&#111;&#110;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
1690 * @version 1, May 20, 2005
1691 */
1692function queryBeanToQuery(objQueryBean) {
1693var qry_return = "";
1694var arrColumns = ArrayNew(1);
1695var arrRows = arrayNew(1);
1696var thisRow = 0;
1697var thisCol = 0;
1698var numRows = 0;
1699var thisVal = "";
1700
1701if( objQueryBean.getClass() EQ "class coldfusion.xml.rpc.QueryBean" ){
1702arrColumns = objQueryBean.getColumnList();
1703numCols = arrayLen( arrColumns );
1704arrRows = objQueryBean.getData();
1705numRows = arrayLen( arrRows );
1706// create the return query object
1707qry_return = QueryNew( ArrayToList(arrColumns) );
1708// loop round each row
1709for( thisRow = 1; thisRow LTE numRows; thisRow = thisRow + 1 ){
1710QueryAddRow( qry_return );
1711// loop round each column
1712for( thisCol = 1; thisCol LTE numCols; thisCol = thisCol + 1 ){
1713// empty columns seem to give rise to undefined array elements!
1714try{
1715thisVal = arrRows[thisRow][thisCol];
1716}
1717catch(Any e) {
1718thisVal = "";
1719}
1720QuerySetCell( qry_return, arrColumns[thisCol], thisVal );
1721}
1722}
1723return qry_return;
1724
1725} else return queryNew("");
1726}
1727
1728/**
1729 * Returns query column list.
1730 *
1731 * @param dbquery Query to examine. (Required)
1732 * @return Returns a list.
1733 * @author John Bartlett (&#106;&#98;&#97;&#114;&#116;&#108;&#101;&#116;&#116;&#64;&#115;&#116;&#114;&#97;&#110;&#103;&#101;&#106;&#111;&#117;&#114;&#110;&#101;&#121;&#46;&#110;&#101;&#116;)
1734 * @version 1, March 17, 2006
1735 */
1736function queryColumns(dbquery) {
1737var queryFields = "";
1738var metadata = dbquery.getMetadata();
1739var i = 0;
1740var col = "";
1741
1742for (i = 1; i lte metadata.getColumnCount(); i = i+1) {
1743col = metadata.getColumnLabel(javaCast("int", i));
1744queryFields = listAppend(queryFields,col);
1745}
1746
1747return queryFields;
1748}
1749
1750/**
1751 * Makes a struct for all values in a given column(s) of a query.
1752 *
1753 * @param query The query to operate on (Required)
1754 * @param keyColumn The name of the column to use for the key in the struct (Required)
1755 * @param valueColumn The name of the column in the query to use for the values in the struct (defaults to whatever the keyColumn is) (Optional)
1756 * @param reverse Boolean value for whether to go through the query in reverse (default is false) (Optional)
1757 * @return struct
1758 * @author Nathan Dintenfass (&#110;&#97;&#116;&#104;&#97;&#110;&#64;&#99;&#104;&#97;&#110;&#103;&#101;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
1759 * @version 1, July 9, 2003
1760 */
1761function queryColumnsToStruct(query,keyColumn){
1762var valueColumn = keyColumn;
1763var reverse = false;
1764var struct = structNew();
1765var increment = 1;
1766var ii = 1;
1767var rowsGotten = 0;
1768//if there is a third argument, treat that as the valueColumn
1769if(arrayLen(arguments) GT 2)
1770valueColumn = arguments[3];
1771//if there is a 4th argument, use that as the reverse
1772if(arrayLen(arguments) GT 3)
1773reverse = arguments[4];
1774//if reversing, we go backwards through the query
1775if(reverse){
1776ii = query.recordCount;
1777increment = -1;
1778}
1779//loop through the query, populating the struct
1780//we do the while loop rather than a for loop because we don't know what direction we're going in
1781while(rowsGotten LT query.recordCount){
1782struct[query[keyColumn][ii]] = query[valueColumn][ii];
1783ii = ii + increment;
1784rowsGotten = rowsGotten + 1;
1785}
1786return struct;
1787}
1788
1789/**
1790 * Takes a selected column of data from a query and converts it into an array.
1791 *
1792 * @param query The query to scan. (Required)
1793 * @param column The name of the column to return data from. (Required)
1794 * @return Returns an array.
1795 * @author Peter J. Farrell (&#112;&#106;&#102;&#64;&#109;&#97;&#101;&#115;&#116;&#114;&#111;&#112;&#117;&#98;&#108;&#105;&#115;&#104;&#105;&#110;&#103;&#46;&#99;&#111;&#109;)
1796 * @version 1, July 22, 2005
1797 */
1798function queryColumnToArray(qry, column) {
1799var arr = arrayNew(1);
1800var ii = "";
1801var loop_len = arguments.qry.recordcount;
1802for (ii=1; ii lte loop_len; ii=ii+1) {
1803arrayAppend(arr, arguments.qry[arguments.column][ii]);
1804}
1805return arr;
1806}
1807
1808/**
1809 * Returns a list of query column data types.
1810 *
1811 * @param dbquery Query to analyze. (Required)
1812 * @return Returns a list.
1813 * @author John Bartlett (&#106;&#98;&#97;&#114;&#116;&#108;&#101;&#116;&#116;&#64;&#115;&#116;&#114;&#97;&#110;&#103;&#101;&#106;&#111;&#117;&#114;&#110;&#101;&#121;&#46;&#110;&#101;&#116;)
1814 * @version 1, April 11, 2006
1815 */
1816function queryColumnTypes(dbquery) {
1817var columnTypes="";
1818var metadata=dbquery.getMetadata();
1819var i=0;
1820var column="";
1821
1822for (i=1; i lte metadata.getColumnCount(); i=i+1) {
1823column = metadata.getColumnLabel(javaCast("int",i));
1824columnTypes = listAppend(columnTypes,dbquery.getColumnTypeName(metadata.getColumnType(dbquery.findColumn(column))));
1825}
1826
1827return columnTypes;
1828}
1829
1830/**
1831 * Concatenate two queries together.
1832 *
1833 * @param q1 First query. (Optional)
1834 * @param q2 Second query. (Optional)
1835 * @return Returns a query.
1836 * @author Chris Dary (&#117;&#109;&#98;&#114;&#97;&#101;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
1837 * @version 1, February 23, 2006
1838 */
1839function queryConcat(q1,q2) {
1840var row = "";
1841var col = "";
1842
1843if(q1.columnList NEQ q2.columnList) {
1844return q1;
1845}
1846
1847for(row=1; row LTE q2.recordCount; row=row+1) {
1848queryAddRow(q1);
1849for(col=1; col LTE listLen(q1.columnList); col=col+1)
1850querySetCell(q1,ListGetAt(q1.columnList,col), q2[ListGetAt(q1.columnList,col)][row]);
1851}
1852return q1;
1853}
1854
1855/**
1856 * Removes duplicate rows from a query based on a key column.
1857 * Modded by Ray Camden to remove evaluate
1858 *
1859 * @param theQuery The query to dedupe. (Required)
1860 * @param keyColumn Column name to check for duplicates. (Required)
1861 * @return Returns a query.
1862 * @author Matthew Fusfield (&#109;&#97;&#116;&#116;&#64;&#102;&#117;&#115;&#46;&#110;&#101;&#116;)
1863 * @version 1, October 11, 2002
1864 */
1865function QueryDeDupe(theQuery,keyColumn) {
1866var checkList='';
1867var newResult=QueryNew(theQuery.ColumnList);
1868var keyvalue='';
1869var q = 1;
1870
1871// loop through each row of the source query
1872for (;q LTE theQuery.RecordCount;q=q+1) {
1873
1874keyvalue = theQuery[keycolumn][q];
1875// see if the primary key value has already been used
1876if (NOT ListFind(checkList,keyvalue)) {
1877
1878/* this is not a duplicate, so add it to the list and copy
1879the row to the destination query */
1880checkList=ListAppend(checklist,keyvalue);
1881QueryAddRow(NewResult);
1882
1883// copy all columns from source to destination for this row
1884for (x=1;x LTE ListLen(theQuery.ColumnList);x=x+1) {
1885QuerySetCell(NewResult,ListGetAt(theQuery.ColumnList,x),theQuery[ListGetAt(theQuery.ColumnList,x)][q]);
1886}
1887}
1888}
1889return NewResult;
1890}
1891
1892/**
1893 * Removes rows from a query.
1894 * Added var col = "";
1895 * No longer using Evaluate. Function is MUCH smaller now.
1896 *
1897 * @param Query Query to be modified
1898 * @param Rows Either a number or a list of numbers
1899 * @return This function returns a query.
1900 * @author Raymond Camden (&#114;&#97;&#121;&#64;&#99;&#97;&#109;&#100;&#101;&#110;&#102;&#97;&#109;&#105;&#108;&#121;&#46;&#99;&#111;&#109;)
1901 * @version 2, October 11, 2001
1902 */
1903function QueryDeleteRows(Query,Rows) {
1904var tmp = QueryNew(Query.ColumnList);
1905var i = 1;
1906var x = 1;
1907
1908for(i=1;i lte Query.recordCount; i=i+1) {
1909if(not ListFind(Rows,i)) {
1910QueryAddRow(tmp,1);
1911for(x=1;x lte ListLen(tmp.ColumnList);x=x+1) {
1912QuerySetCell(tmp, ListGetAt(tmp.ColumnList,x), query[ListGetAt(tmp.ColumnList,x)][i]);
1913}
1914}
1915}
1916return tmp;
1917}
1918
1919/**
1920 * Returns information about the differences between 2 queries with the same columns.
1921 *
1922 * @param q1 The first query. (Required)
1923 * @param q2 The second query. (Required)
1924 * @return Returns a structure.
1925 * @author Nathan Dintenfass (&#110;&#97;&#116;&#104;&#97;&#110;&#64;&#99;&#104;&#97;&#110;&#103;&#101;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
1926 * @version 1, May 26, 2003
1927 */
1928function queryDiff(q1,q2){
1929//vars for looping
1930var ii = 0;
1931var cc = 0;
1932//a struct to hold the result
1933var result = structNew();
1934//grab the columns -- NOTE: THIS VERSION ASSUMES THEY HAVE THE SAME COLUMNS!!
1935var cols = listToArray(q1.columnList);
1936var thisCol = "";
1937//we'll loop whichever query is shortest. by default, we'll loop query1
1938var shorterQuery = q1;
1939var longerQuery = q2;
1940var keyToUseForLonger = "added";
1941var sameSize = true;
1942var rowDiff = 0;
1943//vars to use in the loop
1944var q1Value = "";
1945var q2Value = "";
1946var thenNow = structNew();
1947//make the standard keys in the result
1948result.changed = structNew();
1949result.added = structNew();
1950result.removed = structNew();
1951result.query1 = q1;
1952result.query2 = q2;
1953//if the queries are not the same size, indicate that
1954if(q1.recordCount NEQ q2.recordCount){
1955sameSize = false;
1956//if q2 is shorter, use that instead
1957if(q1.recordCount GT q2.recordCount){
1958shorterQuery = q2;
1959longerQuery = q1;
1960keyToUseForLonger = "removed";
1961}
1962}
1963//loop the correct query to get rows that are different in Q2 from Q1
1964for(ii = 1; ii LTE shorterQuery.recordCount; ii = ii + 1){
1965for(cc = 1; cc LTE arrayLen(cols); cc = cc + 1){
1966thisCol = cols[cc];
1967q1Value = q1[thisCol][ii];
1968q2Value = q2[thisCol][ii];
1969//if this col is different, grab the row index
1970if(compare(q1Value,q2Value)){
1971//if we don't already have this row in the changed group, put it there
1972if(NOT structKeyExists(result.changed,ii))
1973result.changed[ii] = structNew();
1974thenNow = structNew();
1975thenNow.then = q1Value;
1976thenNow.now = q2Value;
1977thenNow.row = ii;
1978thenNow.col = thisCol;
1979result.changed[ii][thisCol] = thenNow;
1980}
1981}
1982}
1983//if they are not the same size, add the row index to the appropriate key
1984if(NOT sameSize){
1985rowDiff = longerQuery.recordCount - shorterQuery.recordCount;
1986for(ii = rowDiff + shorterQuery.recordCount; ii LTE longerQuery.recordCount; ii = ii + 1){
1987result[keyToUseForLonger][ii] = ii;
1988}
1989}
1990//return the result
1991return result;
1992}
1993
1994/**
1995 * Provides direct access to query cells by knowing a key field value within the same row.
1996 *
1997 * @param theQuery The query to search. (Required)
1998 * @param keyField The column value to return. (Required)
1999 * @param keyFieldValue The value to search for in keyFIeld. (Required)
2000 * @param columnName The column value to return. (Required)
2001 * @return Returns a string.
2002 * @author Shawn Seley (&#115;&#104;&#97;&#119;&#110;&#115;&#101;&#64;&#97;&#111;&#108;&#46;&#99;&#111;&#109;)
2003 * @version 1, June 28, 2002
2004 */
2005function QueryGetCellByKey(theQuery, keyField, keyFieldValue, columnName){
2006var key_field_value_list = Evaluate("ValueList(theQuery.#keyField#)");
2007var row_number = ListFindNoCase(key_field_value_list, keyFieldValue);
2008
2009return theQuery[columnName][row_number];
2010
2011}
2012
2013/**
2014 * Merge two queries.
2015 *
2016 * @param querysource Source query. (Required)
2017 * @param queryoutput Destination query. (Required)
2018 * @param keyColumn Column (that exists in both queries) to merge on. (Required)
2019 * @param mergeList List of columns from source query to add to destination query. Defaults to all of them. (Optional)
2020 * @return Returns a query.
2021 * @author Alain Blais (&#65;&#108;&#97;&#105;&#110;&#95;&#98;&#108;&#97;&#105;&#115;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
2022 * @version 1, July 21, 2004
2023 */
2024function querymerge(querysource,queryoutput,keyColumn){
2025var mergeColumn = querysource.columnlist;
2026var valueArray = arrayNew(1);
2027// define counters
2028var i = 1;
2029var iRow = 1;
2030var jRow = 1;
2031//if there is a 4th argument, use that as the mergeColumn
2032if(arrayLen(arguments) GT 3) mergeColumn = arguments[4];
2033//loop through the merge column
2034for(i=1; i lte listLen(mergeColumn,','); i=i+1) {
2035if (listFindNoCase(queryoutput.columnlist,listGetAt(mergeColumn,i,','),',') eq 0) {
2036// loop through each row of queryoutput and add information from querysource
2037found = listGetAt(mergeColumn,i,',');
2038for (iRow=1; iRow lte queryoutput.recordcount; iRow=iRow+1) {
2039// find the row in querysource that matches the value in keycolumn from queryoutput
2040jRow = 1;
2041while (jRow lt querysource.recordcount and querysource[keyColumn][jRow] neq queryoutput[keycolumn][iRow]) {
2042jRow = jRow + 1;
2043}
2044if (querysource[keyColumn][jRow] eq queryoutput[keycolumn][iRow]) {
2045valueArray[iRow] = querysource[listGetAt(mergeColumn,i,',')][jRow];
2046}
2047}
2048// add the columnm
2049queryaddcolumn(queryoutput,listGetAt(mergeColumn,i,','),valueArray);
2050}
2051}
2052return queryoutput;
2053}
2054
2055/**
2056 * Returns specified number of random records.
2057 *
2058 * @param theQuery The query to return random records from. (Required)
2059 * @param NumberOfRows The number of random records to return. (Required)
2060 * @return Returns a query.
2061 * @author Shawn Seley and John King (&#115;&#104;&#97;&#119;&#110;&#115;&#101;&#64;&#97;&#111;&#108;&#46;&#99;&#111;&#109;)
2062 * @version 1, July 10, 2002
2063 */
2064function QueryRandomRows(theQuery, NumberOfRows) {
2065var FinalQuery = QueryNew(theQuery.ColumnList);
2066var x = 0;
2067var y = 0;
2068var i = 0;
2069var random_element = 0;
2070var random_row = 0;
2071var row_list = "";
2072
2073if(NumberOfRows GT theQuery.recordcount) NumberOfRows = theQuery.recordcount;
2074
2075QueryAddRow(FinalQuery, NumberOfRows);
2076
2077// build a list of rows from which we will "scratch off" the randomly selected values in order to avoid repeats
2078for (i=1; i LTE theQuery.RecordCount; i=i+1) row_list = row_list & i & ",";
2079
2080// Build the new query
2081for(x=1; x LTE NumberOfRows; x=x+1){
2082// pick a random_row from row_list and delete that element from row_list (to prevent duplicates)
2083random_element = RandRange(1, ListLen(row_list)); // pick a random list element
2084random_row = ListGetAt(row_list, random_element); // get the corresponding query row number
2085row_list = ListDeleteAt(row_list, random_element); // delete the used element from the list
2086for(y=1; y LTE ListLen(theQuery.ColumnList); y=y+1) {
2087QuerySetCell(FinalQuery, ListGetAt(theQuery.ColumnList, y), theQuery[ListGetAt(theQuery.ColumnList, y)][random_row],x);
2088}
2089}
2090
2091return FinalQuery;
2092}
2093
2094/**
2095 * Reverse the order of a query.
2096 *
2097 * @param qryOriginal Name of the query you want to reverse (Required)
2098 * @return Returns a query object.
2099 * @author David Whiterod (&#119;&#104;&#105;&#116;&#101;&#114;&#111;&#100;&#46;&#100;&#97;&#118;&#105;&#100;&#64;&#115;&#97;&#117;&#103;&#111;&#118;&#46;&#115;&#97;&#46;&#103;&#111;&#118;&#46;&#97;&#117;)
2100 * @version 1, June 27, 2002
2101 */
2102function QueryReverse (qryOriginal) {
2103
2104  // Reverse the order of qryOriginal
2105  // Make a new query using the same columns as qryOriginal
2106  var qryNew = QueryNew(qryOriginal.ColumnList);
2107  var row = 1;
2108  var column = 1;
2109  //Loop through qryOriginal in reverse order (last becomes first)
2110  for(row=qryOriginal.recordCount;row gte 1; row=row-1) {
2111    //Add a new row in the new query
2112    QueryAddRow(qryNew,1);
2113    //Get the values for each column in qryOriginal
2114    for(column=1;column lte ListLen(qryOriginal.ColumnList);column=column+1) {
2115      QuerySetCell(qryNew, ListGetAt(qryOriginal.ColumnList,column), qryOriginal[ListGetAt(qryOriginal.ColumnList,column)][row]);
2116    }
2117  }
2118 
2119  return qryNew;
2120 
2121}
2122
2123/**
2124 * Returns the first query row number that contains the specified key value.
2125 *
2126 * @param theQuery The query to search. (Required)
2127 * @param keyField The column to search. (Required)
2128 * @param keyFieldValue The value to search for. (Required)
2129 * @return Returns a numeric value.
2130 * @author Shawn Seley (&#115;&#104;&#97;&#119;&#110;&#115;&#101;&#64;&#97;&#111;&#108;&#46;&#99;&#111;&#109;)
2131 * @version 1, June 28, 2002
2132 */
2133function QueryRowFromKey(theQuery, keyField, keyFieldValue){
2134var key_field_value_list = Evaluate("ValueList(theQuery.#keyField#)");
2135return ListFindNoCase(key_field_value_list, keyFieldValue);
2136}
2137
2138/**
2139 * Function to take a single row from a query and generate a list.
2140 *
2141 * @param query The query to examine. (Required)
2142 * @param queryrow Row to use. (Optional)
2143 * @param delim Delimiter to use. (Optional)
2144 * @return Returns a string.
2145 * @author Tim Sloan (&#116;&#105;&#109;&#64;&#115;&#108;&#111;&#97;&#110;&#99;&#111;&#110;&#115;&#117;&#108;&#116;&#105;&#110;&#103;&#46;&#99;&#111;&#109;)
2146 * @version 1, August 6, 2004
2147 */
2148function queryRowToList(query){
2149var queryrow = 1;
2150var j = 1;
2151var querycols = listToArray(query.columnList);
2152var delim = ",";
2153var listReturn = "";
2154if(arrayLen(arguments) GT 1) queryrow = arguments[2];
2155if(arrayLen(arguments) GT 2) delim = arguments[3];
2156for(j = 1; j lte arraylen(querycols); j = j + 1){
2157listReturn = ListAppend(listReturn, query[querycols[j]][queryrow], delim);
2158}
2159return listReturn;
2160}
2161
2162/**
2163 * Makes a row of a query into a structure.
2164 *
2165 * @param query The query to work with.
2166 * @param row Row number to check. Defaults to row 1.
2167 * @return Returns a structure.
2168 * @author Nathan Dintenfass (&#110;&#97;&#116;&#104;&#97;&#110;&#64;&#99;&#104;&#97;&#110;&#103;&#101;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
2169 * @version 1, December 11, 2001
2170 */
2171function queryRowToStruct(query){
2172//by default, do this to the first row of the query
2173var row = 1;
2174//a var for looping
2175var ii = 1;
2176//the cols to loop over
2177var cols = listToArray(query.columnList);
2178//the struct to return
2179var stReturn = structnew();
2180//if there is a second argument, use that for the row number
2181if(arrayLen(arguments) GT 1)
2182row = arguments[2];
2183//loop over the cols and build the struct from the query row
2184for(ii = 1; ii lte arraylen(cols); ii = ii + 1){
2185stReturn[cols[ii]] = query[cols[ii]][row];
2186}
2187//return the struct
2188return stReturn;
2189}
2190
2191/**
2192 * Allows changing of a query cell by knowing a key field value within the same row.
2193 *
2194 * @param theQuery The query to modify. (Required)
2195 * @param keyField The column to search against. (Required)
2196 * @param keyFieldValue The value to search for. (Required)
2197 * @param columnName The column to modify. (Required)
2198 * @param newValue The value to set. (Required)
2199 * @return Returns a query.
2200 * @author Shawn Seley (&#115;&#104;&#97;&#119;&#110;&#115;&#101;&#64;&#97;&#111;&#108;&#46;&#99;&#111;&#109;)
2201 * @version 1, June 28, 2002
2202 */
2203function QuerySetCellByKey(theQuery, keyField, keyFieldValue, columnName, newValue){
2204var key_field_value_list = Evaluate("ValueList(theQuery.#keyField#)");
2205var row_number = ListFindNoCase(key_field_value_list, keyFieldValue);
2206querysetCell(theQuery,columnName,newValue,row_number);
2207}
2208
2209/**
2210 * Accepts a specifically formatted chunk of text, and returns it as a query object.
2211 * v2 rewrite by Jamie Jackson
2212 *
2213 * @param queryData Specifically format chunk of text to convert to a query. (Required)
2214 * @return Returns a query object.
2215 * @author Bert Dawson (&#98;&#101;&#114;&#116;&#64;&#114;&#101;&#100;&#98;&#97;&#110;&#110;&#101;&#114;&#46;&#99;&#111;&#109;)
2216 * @version 2, December 18, 2007
2217 */
2218function querySim(queryData) {
2219var fieldsDelimiter="|";
2220var colnamesDelimiter=",";
2221var listOfColumns="";
2222var tmpQuery="";
2223var numLines="";
2224var cellValue="";
2225var cellValues="";
2226var colName="";
2227var lineDelimiter=chr(10) & chr(13);
2228var lineNum=0;
2229var colPosition=0;
2230
2231// the first line is the column list, eg "column1,column2,column3"
2232listOfColumns = Trim(ListGetAt(queryData, 1, lineDelimiter));
2233
2234// create a temporary Query
2235tmpQuery = QueryNew(listOfColumns);
2236
2237// the number of lines in the queryData
2238numLines = ListLen(queryData, lineDelimiter);
2239
2240// loop though the queryData starting at the second line
2241for(lineNum=2; lineNum LTE numLines; lineNum = lineNum + 1) {
2242cellValues = ListGetAt(queryData, lineNum, lineDelimiter);
2243
2244if (ListLen(cellValues, fieldsDelimiter) IS ListLen(listOfColumns,",")) {
2245QueryAddRow(tmpQuery);
2246for (colPosition=1; colPosition LTE ListLen(listOfColumns); colPosition = colPosition + 1){
2247cellValue = Trim(ListGetAt(cellValues, colPosition, fieldsDelimiter));
2248colName = Trim(ListGetAt(listOfColumns,colPosition));
2249QuerySetCell(tmpQuery, colName, cellValue);
2250}
2251}
2252}
2253
2254return( tmpQuery );
2255
2256}
2257
2258/**
2259 * Converts a query object into an array of structures.
2260 *
2261 * @param query The query to be transformed
2262 * @return This function returns a structure.
2263 * @author Nathan Dintenfass (&#110;&#97;&#116;&#104;&#97;&#110;&#64;&#99;&#104;&#97;&#110;&#103;&#101;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
2264 * @version 1, September 27, 2001
2265 */
2266function QueryToArrayOfStructures(theQuery){
2267var theArray = arraynew(1);
2268var cols = ListtoArray(theQuery.columnlist);
2269var row = 1;
2270var thisRow = "";
2271var col = 1;
2272for(row = 1; row LTE theQuery.recordcount; row = row + 1){
2273thisRow = structnew();
2274for(col = 1; col LTE arraylen(cols); col = col + 1){
2275thisRow[cols[col]] = theQuery[cols[col]][row];
2276}
2277arrayAppend(theArray,duplicate(thisRow));
2278}
2279return(theArray);
2280}
2281
2282/**
2283 * Transform a query result into a csv formatted variable.
2284 *
2285 * @param query The query to transform. (Required)
2286 * @param headers A list of headers to use for the first row of the CSV string. Defaults to cols. (Optional)
2287 * @param cols The columns from the query to transform. Defaults to all the columns. (Optional)
2288 * @return Returns a string.
2289 * @author adgnot sebastien (&#115;&#97;&#100;&#103;&#110;&#111;&#116;&#64;&#111;&#103;&#105;&#108;&#118;&#121;&#46;&#110;&#101;&#116;)
2290 * @version 1, June 26, 2002
2291 */
2292function QueryToCsv(query){
2293var csv = "";
2294var cols = "";
2295var headers = "";
2296var i = 1;
2297var j = 1;
2298
2299if(arrayLen(arguments) gte 2) headers = arguments[2];
2300if(arrayLen(arguments) gte 3) cols = arguments[3];
2301
2302if(cols is "") cols = query.columnList;
2303if(headers IS "") headers = cols;
2304
2305headers = listToArray(headers);
2306
2307for(i=1; i lte arrayLen(headers); i=i+1){
2308csv = csv & """" & headers[i] & """;";
2309}
2310
2311csv = csv & chr(13) & chr(10);
2312
2313cols = listToArray(cols);
2314
2315for(i=1; i lte query.recordCount; i=i+1){
2316for(j=1; j lte arrayLen(cols); j=j+1){
2317csv = csv & """" & query[cols[j]][i] & """;";
2318}
2319csv = csv & chr(13) & chr(10);
2320}
2321return csv;
2322}
2323
2324/**
2325 * Changes a query into a struct of arrays.
2326 *
2327 * @param query The query to be transformed.
2328 * @return Returns a structure.
2329 * @author Nathan Dintenfass (&#110;&#97;&#116;&#104;&#97;&#110;&#64;&#99;&#104;&#97;&#110;&#103;&#101;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
2330 * @version 1, February 23, 2002
2331 */
2332function queryToStructOfArrays(q){
2333//a variable to hold the struct
2334var st = structNew();
2335//two variable for iterating
2336var ii = 1;
2337var cc = 1;
2338//grab the columns into an array for easy looping
2339var cols = listToArray(q.columnList);
2340//iterate over the columns of the query and create the arrays of values
2341for(ii = 1; ii lte arrayLen(cols); ii = ii + 1){
2342//make the array with the col name as the key in the root struct
2343st[cols[ii]] = arrayNew(1);
2344//now loop for the recordcount of the query and insert the values
2345for(cc = 1; cc lte q.recordcount; cc = cc + 1)
2346arrayAppend(st[cols[ii]],q[cols[ii]][cc]);
2347}
2348//return the struct
2349return st;
2350}
2351
2352/**
2353 * Converts a query to a structure of structures with the primary index of the main structure auto incremented.
2354 *
2355 * @param theQuery The query to transform. (Required)
2356 * @return Returns a struct.
2357 * @author Peter J. Farrell (&#112;&#106;&#102;&#64;&#109;&#97;&#101;&#115;&#116;&#114;&#111;&#112;&#117;&#98;&#108;&#105;&#115;&#104;&#105;&#110;&#103;&#46;&#99;&#111;&#109;)
2358 * @version 1, September 23, 2004
2359 */
2360function queryToStructOfStructsAutoRow(theQuery){
2361var theStructure = StructNew();
2362var cols = ListToArray(theQuery.columnlist);
2363var row = 1;
2364var thisRow = "";
2365var col = 1;
2366
2367for(row = 1; row LTE theQuery.recordcount; row = row + 1){
2368thisRow = StructNew();
2369for(col = 1; col LTE arraylen(cols); col = col + 1){
2370thisRow[cols[col]] = theQuery[cols[col]][row];
2371}
2372theStructure[row] = Duplicate(thisRow);
2373}
2374return theStructure;
2375}
2376
2377/**
2378 * Converts a query object into a structure of structures accessible by its primary key.
2379 *
2380 * @param theQuery The query you want to convert to a structure of structures.
2381 * @param primaryKey Query column to use as the primary key.
2382 * @return Returns a structure.
2383 * @author Shawn Seley (&#115;&#104;&#97;&#119;&#110;&#115;&#101;&#64;&#97;&#111;&#108;&#46;&#99;&#111;&#109;)
2384 * @version 1, March 27, 2002
2385 */
2386function QueryToStructOfStructures(theQuery, primaryKey){
2387  var theStructure = structnew();
2388  // remove primary key from cols listing
2389  var cols = ListToArray(ListDeleteAt(theQuery.columnlist, ListFindNoCase(theQuery.columnlist, primaryKey)));
2390  var row = 1;
2391  var thisRow = "";
2392  var col = 1;
2393
2394  for(row = 1; row LTE theQuery.recordcount; row = row + 1){
2395    thisRow = structnew();
2396    for(col = 1; col LTE arraylen(cols); col = col + 1){
2397      thisRow[cols[col]] = theQuery[cols[col]][row];
2398    }
2399    theStructure[theQuery[primaryKey][row]] = duplicate(thisRow);
2400  }
2401  return(theStructure);
2402}
2403
2404/**
2405 * Change a row in a query to variables in a scope.
2406 *
2407 * @param q The query to use.
2408 * @param scope Scope to save data in. Defaults to "" or local scope.
2409 * @param row Row number to use. Defaults to 1.
2410 * @return Returns an empty string.
2411 * @author Nathan Dintenfass (&#110;&#97;&#116;&#104;&#97;&#110;&#64;&#99;&#104;&#97;&#110;&#103;&#101;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
2412 * @version 1, March 11, 2002
2413 */
2414function queryToVars(q){
2415//first, an array of the column names for looping
2416var cols = listToArray(q.columnList);
2417//a var to use as iterator
2418var ii = 1;
2419//by default, use no scope
2420var scope = "";
2421//by default, use the first row
2422var row = 1;
2423//if there is a second argument, use that as the scope
2424if(arrayLen(arguments) GT 1)
2425scope = arguments[2] & ".";
2426//if there is a third argument and it is numeric, use that as the row (make sure it is a positive integer)
2427if(arrayLen(arguments) GT 2 and isNumeric(arguments[3]))
2428row = ceiling(abs(arguments[3]));
2429//loop over the columns, making a variables for each one
2430for(ii = 1; ii lte arrayLen(cols); ii = ii + 1)
2431setVariable(scope & cols[ii],q[cols[ii]][row]);
2432//return nothing
2433return "";
2434}
2435
2436/**
2437 * Generates an XMLDoc object from a basic CF Query.
2438 *
2439 * @param query The query to transform. (Required)
2440 * @param rootElement Name of the root node. (Default is "query.") (Optional)
2441 * @param row Name of each row. Default is "row." (Optional)
2442 * @param nodeMode Defines the structure of the resulting XML. Options are 1) "values" (default), which makes each value of each column mlText of individual nodes; 2) "columns", which makes each value of each column an attribute of a node for that column; 3) "rows", which makes each row a node, with the column names as attributes. (Optional)
2443 * @return Returns a string.
2444 * @author Nathan Dintenfass (&#110;&#97;&#116;&#104;&#97;&#110;&#64;&#99;&#104;&#97;&#110;&#103;&#101;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
2445 * @version 2, November 15, 2002
2446 */
2447function queryToXML(query){
2448//the default name of the root element
2449var root = "query";
2450//the default name of each row
2451var row = "row";
2452//make an array of the columns for looping
2453var cols = listToArray(query.columnList);
2454//which mode will we use?
2455var nodeMode = "values";
2456//vars for iterating
2457var ii = 1;
2458var rr = 1;
2459//vars for holding the values of the current column and value
2460var thisColumn = "";
2461var thisValue = "";
2462//a new xmlDoc
2463var xml = xmlNew();
2464//if there are 2 arguments, the second one is name of the root element
2465if(structCount(arguments) GTE 2)
2466root = arguments[2];
2467//if there are 3 arguments, the third one is the name each element
2468if(structCount(arguments) GTE 3)
2469row = arguments[3];
2470//if there is a 4th argument, it's the nodeMode
2471if(structCount(arguments) GTE 4)
2472nodeMode = arguments[4];
2473//create the root node
2474xml.xmlRoot = xmlElemNew(xml,root);
2475//capture basic info in attributes of the root node
2476xml[root].xmlAttributes["columns"] = arrayLen(cols);
2477xml[root].xmlAttributes["rows"] = query.recordCount;
2478//loop over the recordcount of the query and add a row for each one
2479for(rr = 1; rr LTE query.recordCount; rr = rr + 1){
2480arrayAppend(xml[root].xmlChildren,xmlElemNew(xml,row));
2481//loop over the columns, populating the values of this row
2482for(ii = 1; ii LTE arrayLen(cols); ii = ii + 1){
2483thisColumn = lcase(cols[ii]);
2484thisValue = query[cols[ii]][rr];
2485switch(nodeMode){
2486case "rows":
2487xml[root][row][rr].xmlAttributes[thisColumn] = thisValue;
2488break;
2489case "columns":
2490arrayAppend(xml[root][row][rr].xmlChildren,xmlElemNew(xml,thisColumn));
2491xml[root][row][rr][thisColumn].xmlAttributes["value"] = thisValue;
2492break;
2493default:
2494arrayAppend(xml[root][row][rr].xmlChildren,xmlElemNew(xml,thisColumn));
2495xml[root][row][rr][thisColumn].xmlText = thisValue;
2496}
2497
2498}
2499}
2500//return the xmlDoc
2501return xml;
2502}
2503
2504/**
2505 * Sorts a two dimensional array by the specified column using quicksort.
2506 *
2507 * @param arrayToSort The array to sort. (Required)
2508 * @param key Position in the second dimension to sort by. (Required)
2509 * @param down Index in fist dimension indicating where to begin sorting. (Required)
2510 * @param top Position in first dimension indicating where to end sorting. (Required)
2511 * @return Returns an array.
2512 * @author Matthew Wear (&#109;&#97;&#116;&#116;&#46;&#98;&#46;&#119;&#101;&#97;&#114;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
2513 * @version 1, March 28, 2006
2514 */
2515function quickSort2D(arrayToSort, key, down, top) {
2516var i = down;
2517var j = top;
2518var p = JavaCast("int",((down + top)/2));
2519var x = arrayToSort[p][key];
2520var temp = 0;
2521var length = 0;
2522var y = 0;
2523var z = 0;
2524
2525do {
2526while(arrayToSort[i][key] LT x AND i LT p) i=i+1;
2527while(arrayToSort[j][key] GT x AND j GT p) j=j-1;
2528if(i LT j){
2529if(j EQ p){
2530length = ArrayLen(arrayToSort)+1;
2531for(z=length;z GT p+1;z=z-1)
2532for(y=1;y LTE ArrayLen(arrayToSort[i]);y=y+1)
2533arrayToSort[z][y]=arrayToSort[z-1][y];
2534
2535for(y=1;y LTE ArrayLen(arrayToSort[i]);y=y+1){
2536arrayToSort[j+1][y] = arrayToSort[i][y];
2537arrayToSort[i][y] = 0;
2538}
2539
2540ArrayDeleteAt(arrayToSort,i);
2541
2542i=i-1;
2543p=p-1;
2544}
2545
2546else if(i EQ p){
2547length = ArrayLen(arrayToSort)+1;
2548for(z=length;z GT p;z=z-1)
2549for(y=1;y LTE ArrayLen(arrayToSort[i]);y=y+1)
2550arrayToSort[z][y]=arrayToSort[z-1][y];
2551
2552j = j + 1;
2553i = i + 1;
2554p = p + 1;
2555
2556for(y=1;y LTE ArrayLen(arrayToSort[i]);y=y+1){
2557arrayToSort[i-1][y] = arrayToSort[j][y];
2558arrayToSort[j][y] = 0;
2559}
2560
2561ArrayDeleteAt(arrayToSort,j);
2562}
2563
2564else{
2565for(y=1;y LTE ArrayLen(arrayToSort[i]);y=y+1){
2566temp = arrayToSort[i][y];
2567arrayToSort[i][y] = arrayToSort[j][y];
2568arrayToSort[j][y] = temp;
2569}
2570}
2571}
2572
2573if(i LT p) i=i+1;
2574if(j GT p) j=j-1;
2575
2576}while(i LT j);
2577
2578if(down LT j) arrayToSort = QuickSort2D(arrayToSort, key, down, p-1);
2579if(i LT top) arrayToSort = QuickSort2D(arrayToSort, key, p+1, top);
2580
2581return arrayToSort;
2582}
2583
2584/**
2585 * Returns a number of random selections from a list based on their given weights.
2586 *
2587 * @param weights Structure with keys and numeric values for weights. (Required)
2588 * @param n Number of selections to make. (Required)
2589 * @return Returns an array.
2590 * @author Chris Spencer (&#99;&#104;&#114;&#105;&#115;&#115;&#112;&#101;&#110;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
2591 * @version 1, May 25, 2006
2592 */
2593function randomWeightedSelection(weights, n){
2594var seq = structKeyArray(weights);
2595var totals = arrayNew(1);
2596var runningtotal = 0;
2597var selections = arrayNew(1);
2598var s = 0;
2599var i = 0;
2600
2601for(i=1; i lte arrayLen(seq); i=i+1){
2602runningtotal = runningtotal + weights[seq[i]];
2603arrayAppend(totals, runningtotal);
2604}
2605for(s=1; s lte n; s=s+1){
2606r = rand()*runningtotal;
2607for(i=1; i lte arrayLen(seq); i=i+1){
2608if(totals[i] gt r){
2609arrayAppend(selections,seq[i]);
2610break;
2611}
2612}
2613}
2614
2615return selections;
2616}
2617
2618/**
2619 * Pass an Array of structures and the name of a column that exists within each, and it will create a Grouped &quot;Structure of Array of Structures&quot;.
2620 *
2621 * @param mydata Structure to parse. (Optional)
2622 * @param col Column to group by. (Required)
2623 * @return Returns a structure.
2624 * @author Casey Broich (&#99;&#97;&#98;&#64;&#112;&#97;&#103;&#101;&#120;&#46;&#99;&#111;&#109;)
2625 * @version 1, May 26, 2003
2626 */
2627function ReGroupBy(mydata,col){
2628  var i = "";
2629  var sttemp = structnew();
2630  var thisValue = "";
2631  for (i=1; i LTE arraylen(mydata); i=i+1){
2632    thisValue = mydata[i][col];
2633    if (not structkeyexists(sttemp, thisValue)){
2634      sttemp[thisValue] = arraynew(1);
2635    }
2636    arrayappend(sttemp[thisValue] , mydata[i]);
2637  }
2638  return sttemp;
2639}
2640
2641/**
2642 * Removes any empty structure keys from within a structure.
2643 * version 2 by Raymond Camden, added var, slimmed things down a bit.
2644 *
2645 * @param structure Structure to modify. (Required)
2646 * @return Returns a structure.
2647 * @author Brian Rinaldi (&#98;&#114;&#105;&#110;&#97;&#108;&#100;&#105;&#64;&#99;&#114;&#105;&#116;&#105;&#99;&#97;&#108;&#100;&#105;&#103;&#105;&#116;&#97;&#108;&#46;&#99;&#111;&#109;)
2648 * @version 1, April 19, 2004
2649 */
2650function removeEmptyStructureKeys(structure) {
2651var newStructure = structNew();
2652var keys = structKeyList(arguments.structure);
2653var name = "";
2654var i = 1;
2655for (;i lte listLen(keys);i=i+1) {
2656name = listGetAt(keys,i);
2657if (not isSimpleValue(arguments.structure[name])) {
2658newStructure[name] = arguments.structure[name];
2659}
2660else if (arguments.structure[name] neq "") {
2661newStructure[name] = arguments.structure[name];
2662}
2663}
2664return newStructure;
2665}
2666
2667/**
2668 * Converts a structure of arrays into a name/key style structure.
2669 *
2670 * @param struct Structure to convert. (Required)
2671 * @param theKey Struct key used to define new struct. (Required)
2672 * @param cols Keys to include in new structure. (Optional)
2673 * @return Returns a structure.
2674 * @author Casey Broich (&#99;&#97;&#98;&#64;&#112;&#97;&#103;&#101;&#120;&#46;&#99;&#111;&#109;)
2675 * @version 1, June 12, 2003
2676 */
2677function saTOss(struct,thekey){
2678var x = "";
2679var i = "";
2680var ii = "";
2681var new = structnew();
2682var cols = structkeyarray(Struct);
2683
2684if(arrayLen(arguments) GT 2) cols = listToArray(arguments[3]);
2685
2686for(i = 1; i lte arraylen(Struct[thekey]); i = i + 1){
2687new[Struct[thekey][i]] = structnew();
2688for(ii = 1; ii lte arraylen(cols); ii = ii + 1){
2689new[Struct[thekey][i]][cols[ii]] = Struct[cols[ii]][i];
2690}
2691}
2692return new;
2693}
2694
2695/**
2696 * This UDF will find the first variable scope that exists for a variable in the list of variable scopes and return its value.
2697 *
2698 * @param strVariable Variable to check for. (Required)
2699 * @param lstScope List of scopes to check. (Required)
2700 * @param default Default value to use if the variable does not exists. Defaults to an empty string. (Optional)
2701 * @return Returns any possible value.
2702 * @author Scott Jibben (&#115;&#99;&#111;&#116;&#116;&#64;&#106;&#105;&#98;&#98;&#101;&#110;&#46;&#99;&#111;&#109;)
2703 * @version 1, February 17, 2004
2704 */
2705function ScopeCoalesce(strVariable, lstScopes) {
2706  var vRet = "";
2707  var nIndex = 1;
2708  var nLstLen = ListLen(lstScopes);
2709
2710  // assign the default return if passed in
2711  If (ArrayLen(Arguments) GTE 3)
2712    vRet = Arguments[3];
2713
2714  // loop over the list
2715  while (nIndex LTE nLstLen) {
2716    if (IsDefined(ListGetAt(lstScopes, nIndex) & '.' & strVariable)) {
2717      vRet = Evaluate(ListGetAt(lstScopes, nIndex) & '.' & strVariable);
2718      break;
2719    }
2720    nIndex = nIndex + 1;
2721  }
2722
2723  return vRet;
2724}
2725
2726/**
2727 * Returns a shifted array at the passed Shift On value.
2728 *
2729 * @param inArray Array to shift. (Required)
2730 * @param shiftOnValue Index to shift. (Required)
2731 * @return Returns an array.
2732 * @author Richard Nugen (&#114;&#105;&#99;&#104;&#97;&#114;&#100;&#64;&#99;&#111;&#114;&#112;&#111;&#114;&#97;&#116;&#101;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
2733 * @version 1, August 16, 2005
2734 */
2735function shiftArray(inArray,ShiftOnVal) {
2736var tmpArray = arrayNew(1);
2737var x = 0;
2738       
2739for(x=1; x lte arrayLen(inArray); x=x+1) {
2740if(inArray[x] EQ ShiftOnVal) { break; }
2741else {
2742arrayAppend(tmpArray,inArray[x]);
2743arrayDeleteAt(inArray,x);
2744x=0;
2745}
2746}
2747
2748for(x=1;x lte arrayLen(tmpArray); x=x+1) arrayAppend(inArray,tmpArray[x]);
2749       
2750return inArray;
2751}
2752
2753/**
2754 * This functions helps to quickly build structures, both simple and complex.
2755 * v2 by Brendan Baldwin &#98;&#114;&#101;&#110;&#100;&#97;&#110;&#46;&#98;&#97;&#108;&#100;&#119;&#105;&#110;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;
2756 *
2757 * @param paramN This UDF accepts N optional arguments. Each argument is added to the returned structure. (Optional)
2758 * @return Returns a structure.
2759 * @author Erki Esken (&#98;&#114;&#101;&#110;&#100;&#97;&#110;&#46;&#98;&#97;&#108;&#100;&#119;&#105;&#110;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;&#101;&#114;&#107;&#105;&#64;&#100;&#114;&#101;&#97;&#109;&#100;&#114;&#117;&#109;&#109;&#101;&#114;&#46;&#99;&#111;&#109;)
2760 * @version 2, August 22, 2007
2761 */
2762function struct() { return duplicate(arguments); }
2763
2764/**
2765 * Blends all nested structs, arrays, and variables in a struct to another.
2766 *
2767 * @param struct1 The first struct. (Required)
2768 * @param struct2 The second sturct. (Required)
2769 * @param overwriteflag Determines if keys are overwritten. Defaults to false. (Optional)
2770 * @return Returns a boolean.
2771 * @author Raymond Compton (&#117;&#115;&#97;&#82;&#97;&#121;&#100;&#97;&#114;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
2772 * @version 1, December 24, 2007
2773 */
2774function structBlend(Struct1,Struct2) {
2775var i = 1;
2776var OverwriteFlag = true;
2777var StructKeyAr = listToArray(structKeyList(Struct2));
2778var Success = true;
2779   if ( arrayLen(arguments) gt 2 AND listFindNoCase("0,no,false",Arguments[3]) ) // Optional 3rd argument "OverwriteFlag"
2780   OverwriteFlag = false;
2781try {
2782for ( i=1; i lte structCount(Struct2); i=i+1 ) {
2783if ( not isDefined('Struct1.#StructKeyAr[i]#') ) // If structkey doesn't exist in Struct1
2784Struct1[StructKeyAr[i]] = Struct2[StructKeyAr[i]]; // Copy all as is.
2785else if ( isStruct(struct2[StructKeyAr[i]]) ) // else if key is another struct
2786Success = structBlend(Struct1[StructKeyAr[i]],Struct2[StructKeyAr[i]],OverwriteFlag); // Recall function
2787else if ( OverwriteFlag ) // if Overwrite
2788Struct1[StructKeyAr[i]] = Struct2[StructKeyAr[i]]; // set Struct1 Key with Struct2 value.
2789}
2790}
2791catch(any excpt) { Success = false; }
2792return Success;
2793}
2794
2795/**
2796 * Recursive functions to compare structures and arrays.
2797 * Fix by Jose Alfonso.
2798 *
2799 * @param LeftStruct The first struct. (Required)
2800 * @param RightStruct The second structure. (Required)
2801 * @return Returns a boolean.
2802 * @author Ja Carter (&#106;&#97;&#64;&#110;&#117;&#111;&#114;&#98;&#105;&#116;&#46;&#99;&#111;&#109;)
2803 * @version 2, October 14, 2005
2804 */
2805function structCompare(LeftStruct,RightStruct) {
2806var result = true;
2807var LeftStructKeys = "";
2808var RightStructKeys = "";
2809var key = "";
2810
2811//Make sure both params are structures
2812if (NOT (isStruct(LeftStruct) AND isStruct(RightStruct))) return false;
2813
2814//Make sure both structures have the same keys
2815LeftStructKeys = ListSort(StructKeyList(LeftStruct),"TextNoCase","ASC");
2816RightStructKeys = ListSort(StructKeyList(RightStruct),"TextNoCase","ASC");
2817if(LeftStructKeys neq RightStructKeys) return false;
2818
2819// Loop through the keys and compare them one at a time
2820for (key in LeftStruct) {
2821//Key is a structure, call structCompare()
2822if (isStruct(LeftStruct[key])){
2823result = structCompare(LeftStruct[key],RightStruct[key]);
2824if (NOT result) return false;
2825//Key is an array, call arrayCompare()
2826} else if (isArray(LeftStruct[key])){
2827result = arrayCompare(LeftStruct[key],RightStruct[key]);
2828if (NOT result) return false;
2829// A simple type comparison here
2830} else {
2831if(LeftStruct[key] IS NOT RightStruct[key]) return false;
2832}
2833}
2834return true;
2835}
2836
2837/**
2838 * Takes a struct of simple values and returns the structure with the values and keys inverted.
2839 *
2840 * @param st Structure of simple name/value pairs you want inverted.
2841 * @return Returns a structure.
2842 * @author Craig Fisher (&#99;&#114;&#97;&#105;&#103;&#64;&#97;&#108;&#116;&#97;&#105;&#110;&#116;&#101;&#114;&#97;&#99;&#116;&#105;&#118;&#101;&#46;&#99;&#111;&#109;)
2843 * @version 1, November 13, 2001
2844 */
2845function StructInvert(st) {
2846var stn=StructNew();
2847var lKeys="";
2848var nkey="";
2849var i=1;
2850var eflg=0;
2851if (NOT IsStruct(st)) {
2852eflg=1;
2853}
2854else {
2855lKeys=StructKeyList(st);
2856for (i=1; i LTE ListLen(lKeys); i=i+1) {
2857nKey=listgetat(lkeys, i);
2858if (IsSimpleValue(st[nKey]))
2859stn[st[nKey]]=nKey;
2860else {
2861eflg=1;
2862break;
2863}
2864}
2865}
2866if (eflg is 1) {
2867writeoutput("Error in <Code>InvertStruct()</code>! Correct usage: InvertStruct(<I>Structure</I>) -- Returns a structure with the values and keys of <I>Structure</I> inverted when <i>Structure</i> is a structure of simple values.");
2868return 0;
2869}
2870else {
2871return stn;
2872}
2873}
2874
2875/**
2876 * Converts a structure of arrays to a CF Query.
2877 *
2878 * @param theStruct The structure of arrays you want converted to a query.
2879 * @return Returns a query object.
2880 * @author Casey Broich (&#99;&#97;&#98;&#64;&#112;&#97;&#103;&#101;&#120;&#46;&#99;&#111;&#109;)
2881 * @version 1, March 27, 2002
2882 */
2883function StructOfArraysToQuery(thestruct){
2884   var fieldlist = structkeylist(thestruct);
2885   var numrows = arraylen( thestruct[listfirst(fieldlist)] );
2886   var thequery = querynew(fieldlist);
2887   var fieldname="";
2888   var thevalue="";
2889   var row=1;
2890   var col=1;
2891   for(row=1; row lte numrows; row = row + 1)
2892   {
2893      queryaddrow(thequery);
2894      for(col=1; col lte listlen(fieldlist); col = col + 1)
2895      {
2896fieldname = listgetat(fieldlist,col);
2897thevalue = thestruct[fieldname][row];
2898querysetcell(thequery,fieldname,thevalue);
2899      }
2900   }
2901return(thequery); }
2902
2903/**
2904 * Converts a structure of arrays to a keyed structure of structs.
2905 *
2906 * @param struct Struct to examine. (Required)
2907 * @param theKey Key in structure to use as new primary key. (Required)
2908 * @param cols Keys from original structure to use. Defaults to all. (Optional)
2909 * @return Returns a struct.
2910 * @author Casey Broich (&#99;&#97;&#98;&#64;&#112;&#97;&#103;&#101;&#120;&#46;&#99;&#111;&#109;)
2911 * @version 1, August 2, 2003
2912 */
2913function StructOfArraysToStructOfStructs(struct,thekey){
2914   var i = "";
2915   var ii = "";
2916   var new = structNew();
2917   var value = "";
2918   var cols = "";
2919
2920   if(arrayLen(arguments) GT 2) cols = listToArray(arguments[3]);
2921   else cols = structkeyarray(struct);
2922
2923   for(i = 1; i lte arraylen(struct[thekey]); i = i + 1){
2924      new[struct[thekey][i]] = structNew();
2925      for(ii = 1; ii lte arraylen(cols); ii = ii + 1){
2926      if(structKeyExists(struct,cols[ii])){
2927         value = struct[cols[ii]][i];
2928      }else{
2929         value = "";
2930      }
2931      new[struct[thekey][i]][cols[ii]] = value;
2932      }
2933   }
2934   return new;
2935}
2936
2937/**
2938 * Converts a structure of Lists to an Array of structures.
2939 *
2940 * @param struct Struct of lists. (Required)
2941 * @param delim List delimiter. Defaults to a comma. (Optional)
2942 * @param cols Struct keys to use. Defaults to all. (Optional)
2943 * @return Returns an array.
2944 * @author Casey Broich (&#99;&#97;&#98;&#64;&#112;&#97;&#103;&#101;&#120;&#46;&#99;&#111;&#109;)
2945 * @version 1, August 2, 2003
2946 */
2947function StructOfListsToArrayOfStructs(Struct){
2948   var delim = ",";
2949   var theArray = arraynew(1);
2950   var row = 1;
2951   var i = "";
2952   var cols = structkeyarray(Struct);
2953   var count = 0;
2954   var value = "";
2955   var strow = "";
2956
2957   if(arrayLen(arguments) GT 1) delim = arguments[2];
2958   if(arrayLen(arguments) GT 2) cols = listToArray(arguments[3]);
2959   count = listlen(struct[cols[1]],delim);
2960   if(arraylen(cols) gt 0) {
2961      for(row=1; row LTE count; row = row + 1){
2962      strow = structnew();
2963      for(i=1; i lte arraylen(cols); i=i+1) {
2964         if(structKeyExists(Struct,cols[i])){
2965            if(listlen(Struct[cols[i]],delim) gte row){
2966               value = listgetat(Struct[cols[i]],row,delim);
2967            }else{
2968               value = "";
2969            }
2970         }else{
2971            value = "";
2972         }
2973         strow[cols[i]] = value;
2974      }
2975      arrayAppend(theArray,strow);
2976      }
2977   }
2978   return theArray;
2979}
2980
2981/**
2982 * Converts a structure of structures to a CF Query.
2983 *
2984 * @param theStruct The structure to translate. (Required)
2985 * @param primaryKey The query column name to use for the primary key. (Required)
2986 * @return Returns a query.
2987 * @author Shawn Seley (&#115;&#104;&#97;&#119;&#110;&#115;&#101;&#64;&#97;&#111;&#108;&#46;&#99;&#111;&#109;)
2988 * @version 1, June 28, 2002
2989 */
2990function StructOfStructuresToQuery(theStruct, primaryKey){
2991var primary_key_list = StructKeyList(theStruct);
2992var field_list = StructKeyList(theStruct[ListFirst(primary_key_list)]);
2993var num_rows = ListLen(primary_key_list);
2994var the_query = QueryNew(primaryKey & "," & field_list);
2995var primary_key_value = "";
2996var field_name = "";
2997var the_value = "";
2998var row = 0;
2999var col = 0;
3000
3001for(row=1; row LTE num_rows; row=row+1) {
3002QueryAddRow(the_query);
3003primary_key_value = ListGetAt(primary_key_list, row);
3004QuerySetCell(the_query, primaryKey, primary_key_value);
3005for(col=1; col LTE ListLen(field_list); col=col+1) {
3006field_name = ListGetAt(field_list, col);
3007the_value = theStruct[primary_key_value][field_name];
3008QuerySetCell(the_query, field_name, the_value);
3009}
3010}
3011
3012return(the_query);
3013}
3014
3015/**
3016 * Renames a specified key in the specified structure.
3017 *
3018 * @param struct The structure to modify. (Required)
3019 * @param key The key to rename. (Required)
3020 * @param newkey The new name of the key. (Required)
3021 * @param allowOverwrite Boolean to determine if an existing key can be overwritten. Defaults to false. (Optional)
3022 * @return Returns a structure.
3023 * @author Erki Esken (&#101;&#114;&#107;&#105;&#64;&#100;&#114;&#101;&#97;&#109;&#100;&#114;&#117;&#109;&#109;&#101;&#114;&#46;&#99;&#111;&#109;)
3024 * @version 1, June 26, 2002
3025 */
3026function StructRenameKey(struct, key, newkey) {
3027// Allow overwriting existing keys or not?
3028var AllowOverWrite = false;
3029switch (ArrayLen(Arguments)) {
3030case "4":
3031AllowOverWrite = Arguments[4];
3032}
3033
3034// No key or keys are the same? Return.
3035if (NOT StructKeyExists(struct, key) OR CompareNoCase(key, newkey) EQ 0)
3036return struct;
3037
3038if (NOT AllowOverWrite AND StructKeyExists(struct, newkey)) {
3039// New key already exists and overwriting is off? Return.
3040return struct;
3041} else {
3042// Duplicate and delete old. Return.
3043struct[newkey] = Duplicate(struct[key]);
3044StructDelete(struct, key);
3045return struct;
3046}
3047}
3048
3049/**
3050 * Converts struct into delimited key/value list.
3051 *
3052 * @param s Structure. (Required)
3053 * @param delim List delimeter. Defaults to a comma. (Optional)
3054 * @return Returns a string.
3055 * @author Greg Nettles (&#103;&#114;&#101;&#103;&#110;&#101;&#116;&#116;&#108;&#101;&#115;&#64;&#99;&#97;&#108;&#118;&#97;&#114;&#121;&#99;&#104;&#97;&#112;&#101;&#108;&#46;&#99;&#111;&#109;)
3056 * @version 2, July 25, 2006
3057 */
3058function structToList(s) {
3059var delim = "&";
3060var i = 0;
3061var newArray = structKeyArray(arguments.s);
3062
3063if (arrayLen(arguments) gt 1) delim = arguments[2];
3064
3065for(i=1;i lte structCount(arguments.s);i=i+1) newArray[i] = newArray[i] & "=" & arguments.s[newArray[i]];
3066
3067return arraytoList(newArray,delim);
3068}
3069
3070/**
3071 * Converts a structure to a URL query string.
3072 *
3073 * @param struct Structure of key/value pairs you want converted to URL parameters
3074 * @param keyValueDelim Delimiter for the keys/values. Default is the equal sign (=).
3075 * @param queryStrDelim Delimiter separating url parameters. Default is the ampersand (&).
3076 * @return Returns a string.
3077 * @author Erki Esken (&#101;&#114;&#107;&#105;&#64;&#100;&#114;&#101;&#97;&#109;&#100;&#114;&#117;&#109;&#109;&#101;&#114;&#46;&#99;&#111;&#109;)
3078 * @version 1, December 17, 2001
3079 */
3080function StructToQueryString(struct) {
3081  var qstr = "";
3082  var delim1 = "=";
3083  var delim2 = "&";
3084
3085  switch (ArrayLen(Arguments)) {
3086    case "3":
3087      delim2 = Arguments[3];
3088    case "2":
3089      delim1 = Arguments[2];
3090  }
3091
3092  for (key in struct) {
3093    qstr = ListAppend(qstr, URLEncodedFormat(LCase(key)) & delim1 & URLEncodedFormat(struct[key]), delim2);
3094  }
3095  return qstr;
3096}
3097
3098/**
3099 * Converts a structure into a key/value pair list.
3100 *
3101 * @param struct Structure to list. (Required)
3102 * @param delimiter Delimiter. Defaults to a comma. (Optional)
3103 * @return Returns a string.
3104 * @author Kit Brandner (&#107;&#105;&#116;&#46;&#98;&#114;&#97;&#110;&#100;&#110;&#101;&#114;&#64;&#115;&#101;&#114;&#101;&#98;&#114;&#97;&#46;&#99;&#111;&#109;)
3105 * @version 1, November 6, 2006
3106 */
3107function structValueList(struct) {
3108var delimiter = ",";
3109var element = 0;
3110var kvName = "";
3111var kvValue = "";
3112var returnList = "";
3113
3114if(arrayLen(arguments) gt 1) delimiter = arguments[2];
3115
3116if (isStruct(struct)) {
3117for (; element lt listLen(structKeyList(struct, delimiter)) ; element=element+1) {
3118kvName = listGetAt(structKeyList(struct, delimiter), element+1, delimiter);
3119kvValue = "";
3120if(isSimpleValue(struct[kvName])) kvValue = struct[kvName];
3121returnList = listAppend(returnList, kvName & iif(len(trim(kvValue)) gt 0, de("=" & kvValue), de("")));
3122}
3123}
3124
3125return returnList;
3126}
3127
3128/**
3129 * Like structInsert() but does not throw an error if the key exists and you choose not to overwrite.
3130 *
3131 * @param structure Struct to be modified. (Required)
3132 * @param key Key to modify. (Required)
3133 * @param value Value to use. (Required)
3134 * @param allowOverwrite Boolean. If false and key exists, value will not be changed. Defaults to false. (Optional)
3135 * @return Returns yes or no indicating if a change was made.
3136 * @author Anthony Cooper (&#97;&#110;&#116;&#64;&#98;&#108;&#117;&#101;&#118;&#97;&#110;&#46;&#99;&#111;&#46;&#117;&#107;)
3137 * @version 1, May 9, 2003
3138 */
3139function structWrite(structure, key, value) {
3140var valueWritten = false;
3141var allowOverwrite = false;
3142
3143if(arrayLen(arguments) gte 4) allowOverwrite = arguments[4];
3144
3145if ( structKeyExists( structure, key ) IMP allowOverwrite ) {
3146valueWritten = structInsert( structure, key, value, allowOverwrite );
3147}
3148return yesNoFormat(valueWritten);
3149}
3150
3151/**
3152 * Function to translate Macromedia's XML Resource Feed into ColdFusion variables.
3153 *
3154 * @param s variable containing the contents of the Macromedia XML feed. Usually CFHTTP.FileContent (Required)
3155 * @return Returns an array of structures.
3156 * @author Jeffry Houser (&#106;&#101;&#102;&#102;&#64;&#102;&#97;&#114;&#99;&#114;&#121;&#102;&#108;&#121;&#46;&#99;&#111;&#109;)
3157 * @version 2, August 12, 2002
3158 */
3159function TranslateMacromediaResourceFeed(S) {
3160
3161// the current token we are looking at
3162var Token = GetToken(S,1,"<>");
3163
3164// LoopControl needs to be initialized
3165var LoopControl = 1;
3166
3167// Initialize the Current Query Row
3168var RowNumber = 1;
3169
3170// the number of the next token we are looking at
3171var NextToken = 2;
3172
3173var ResourceStruct = StructNew();
3174
3175// Initialize Return Query
3176var ResultQuery = QueryNew("Type, Title, Author, URL, ProductName");
3177
3178// loop until we are out of tokens
3179while(Token is not "/macromedia_resources"){
3180
3181switch(Left(Token,7)){
3182
3183case "resourc":{
3184// if we are getting a resource token, we want to:
3185// create a new blank structure
3186// and define the structure's type
3187
3188// define new structure
3189ResourceStruct = StructNew();
3190
3191// add the type of entry to the structure
3192StructInsert(ResourceStruct, "Type", GetToken(Token, 2, """"));
3193
3194// increment next token
3195NextToken = NextToken + 1;
3196break;
3197} // end resource case
3198
3199case "/resour":{
3200// if we are getting a /resource token, we want to:
3201// Create a new row in the query for each product
3202// Assuming the structure isn't empty
3203
3204// copy existing structure into result array
3205if (not StructIsEmpty(ResourceStruct)){
3206
3207for (LoopControl = 1 ;
3208LoopControl LTE ArrayLen(ResourceStruct.Products) ;
3209LoopControl = LoopControl+1){
3210
3211// add a new row to the query
3212RowNumber = QueryAddRow(ResultQuery);
3213
3214// populate Query
3215QuerySetCell(ResultQuery, "Type", ResourceStruct.Type, RowNumber);
3216QuerySetCell(ResultQuery, "Title", ResourceStruct.Title, RowNumber);
3217QuerySetCell(ResultQuery, "Author", ResourceStruct.Author, RowNumber);
3218QuerySetCell(ResultQuery, "URL", ResourceStruct.URL, RowNumber);
3219QuerySetCell(ResultQuery, "ProductName", ResourceStruct.Products[LoopControl], RowNumber);
3220
3221}
3222
3223}
3224
3225// increment next token
3226NextToken = NextToken + 1;
3227break;
3228} // end resource case
3229
3230
3231case "title":{
3232// if we are getting the title token, then we want to:
3233// add the next token to our structure, because that will be our title text
3234// increment the 'nexttoken' variable two increments past the end title token
3235
3236// add the title to the structure
3237StructInsert(ResourceStruct, "Title", GetToken(S, NextToken+1, "<>"));
3238
3239// increment next token
3240NextToken = NextToken + 2;
3241
3242break;
3243} // end title case
3244
3245case "author":{
3246// if we are getting the author token, then we want to:
3247// add the next token to our structure, because that will be our author text
3248// increment the 'nexttoken' variable two increments past the end Author token
3249
3250// add the title to the structure
3251StructInsert(ResourceStruct, "Author", GetToken(S, NextToken+1, "<>"));
3252
3253// increment next token
3254NextToken = NextToken + 2;
3255
3256break;
3257} // end author case
3258
3259
3260case "url":{
3261// if we are getting the url token, then we want to:
3262// add the next token to our structure, because that will be our url text
3263// increment the 'nexttoken' variable two increments past the end url token
3264
3265// add the title to the structure
3266StructInsert(ResourceStruct, "URL", GetToken(S, NextToken+1, "<>"));
3267
3268// increment next token
3269NextToken = NextToken + 2;
3270
3271break;
3272} // end url case
3273
3274case "product":{
3275// if the case is a product, we want to:
3276// Pick out the name from the token and add it to our product array
3277// increment the nexttoken variable once
3278
3279// if product array doesn't exist, create it
3280if (not IsDefined("ResourceStruct.Products")){
3281StructInsert(ResourceStruct, "Products", ArrayNew(1));
3282}
3283
3284// add product name to array
3285ArrayAppend(ResourceStruct.Products,GetToken(Token, 2, """"));
3286
3287// increment next token
3288NextToken = NextToken + 1;
3289
3290break;
3291} // end product case
3292
3293
3294// case "/title","/author","/url"
3295// these cases or anything else not defined, we just ignore, but we still wanna get the
3296// next token
3297default: {
3298Token = GetToken(S,NextToken,"<>");
3299NextToken = NextToken + 1;
3300break;
3301} // end default case
3302} // end switch
3303Token = GetToken(S,NextToken,"<>");
3304} // end while
3305
3306return (ResultQuery);
3307}
3308
3309/**
3310 * Trim spaces from all records in a query.
3311 *
3312 * @param qry The query to trim. (Required)
3313 * @return Returns a query.
3314 * @author Giampaolo Bellavite (&#103;&#105;&#97;&#109;&#112;&#97;&#111;&#108;&#111;&#64;&#98;&#101;&#108;&#108;&#97;&#118;&#105;&#116;&#101;&#46;&#99;&#111;&#109;)
3315 * @version 1, February 26, 2004
3316 */
3317function trimQuery(qry) {
3318var col="";
3319var i=1;
3320var j=1;
3321for(;i lte qry.recordCount;i=i+1) {
3322for(j=1;j lte listLen(qry.columnList);j=j+1) {
3323col=listGetAt(qry.columnList,j);
3324querySetCell(qry,col,trim(qry[col][i]),i);
3325}
3326}
3327return qry;
3328}
3329
3330/**
3331 * Trims spaces from all keys in a structure.
3332 * Version 2 by Raymond Camden
3333 * Version 3 by author - he mentioned the need for isSimpleValue
3334 *
3335 * @param st Structure to trim. (Required)
3336 * @param excludeList List of keys to exclude. (Optional)
3337 * @return Returns a structure.
3338 * @author Mike Gillespie (&#109;&#105;&#107;&#101;&#64;&#115;&#116;&#114;&#105;&#107;&#105;&#110;&#103;&#46;&#99;&#111;&#109;)
3339 * @version 3, July 11, 2002
3340 */
3341function TrimStruct(st) {
3342var excludeList = "";
3343var key = "";
3344
3345if(arrayLen(arguments) gte 2) excludeList = arguments[2];
3346for(key in st) {
3347if(not listFindNoCase(excludeList,key) and isSimpleValue(st[key])) st[key] = trim(st[key]);
3348}
3349return st;
3350}
3351
3352/**
3353 * Returns the type of the variable.
3354 * Made it cf5/mx compat with use of getFunctionList
3355 *
3356 * @param x The data to inspect. (Required)
3357 * @return Returns a string.
3358 * @author Jordan Clark (&#74;&#111;&#114;&#100;&#97;&#110;&#67;&#108;&#97;&#114;&#107;&#64;&#84;&#101;&#108;&#117;&#115;&#46;&#110;&#101;&#116;)
3359 * @version 3, August 16, 2002
3360 */
3361function TypeOf(x) {
3362   if(isArray(x)) return "array";
3363   if(isStruct(x)) return "structure";
3364   if(isQuery(x)) return "query";
3365   if(isSimpleValue(x) and isWddx(x)) return "wddx";
3366   if(isBinary(x)) return "binary";
3367   if(isCustomFunction(x)) return "custom function";
3368   if(isDate(x)) return "date";
3369   if(isNumeric(x)) return "numeric";
3370   if(isBoolean(x)) return "boolean";
3371   if( listFindNoCase( structKeyList( GetFunctionList() ), "isXMLDoc" ) AND
3372isXMLDoc(x)) return "xml";
3373   if(isSimpleValue(x)) return "string";
3374   return "unknown";
3375}
3376
3377/**
3378 * Returns a list of unique values from a query column.
3379 *
3380 * @param queryname Query to scan. (Required)
3381 * @param columnname Column to use. (Required)
3382 * @param cs If true, the unique list will check the case of the values. Defaults to false. (Optional)
3383 * @return Returns a string.
3384 * @author Nick Giovanni (&#110;&#103;&#105;&#111;&#118;&#97;&#110;&#110;&#105;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
3385 * @version 1, March 27, 2007
3386 */
3387function uniqueValueList(queryName, columnName) {
3388var cs = 0;
3389var curRow = 1;
3390var uniqueList = "";
3391
3392if(arrayLen(arguments) GTE 3 AND isBoolean(arguments[3])) cs = arguments[3];
3393
3394for(; curRow LTE queryName.recordCount; curRow = curRow +1){
3395if((not cs AND not listFindNoCase(uniqueList, trim(queryName[columnName][curRow]))) OR (cs AND not listFind(uniqueList, trim(queryName[columnName][curRow])))){
3396uniqueList = ListAppend(uniqueList, trim(queryName[columnName][curRow]));
3397}
3398}
3399return uniqueList;
3400}
3401
3402/**
3403 * Reverses a CF variable into CFScript.
3404 *
3405 * @param lObj The object to be recreated in script. (Required)
3406 * @param lName The name for the object. (Required)
3407 * @return Returns a string.
3408 * @author Bert Dawson (&#98;&#100;&#97;&#119;&#115;&#111;&#110;&#64;&#114;&#101;&#100;&#98;&#97;&#110;&#110;&#101;&#114;&#46;&#99;&#111;&#109;)
3409 * @version 1, September 18, 2002
3410 */
3411function VarToScript(lObj,lName) {
3412var i="";
3413var j="";
3414var k="";
3415var l="";
3416var crlf=chr(13) & chr(10);
3417var s="";
3418var t="";
3419var u='",##';
3420var v='"",####';
3421
3422if (IsStruct(lObj)) {
3423s = s & crlf & lName & "=StructNew();";
3424for (i IN lObj) {
3425if (IsSimpleValue( lObj[i] )) {
3426s = s & crlf & lName & "[""" & i & """]=""" & ReplaceList(lObj[i],u,v) & """;";
3427} else {
3428s = s & varToScript(lObj[i], lName & "[""" & i & """]");
3429}
3430}
3431
3432} else if (IsArray(lObj)) {
3433s = s & crlf & lName & "=ArrayNew(1);";
3434for(i=1; i LTE ArrayLen(lObj); i=i+1) {
3435if (IsSimpleValue( lObj[i] )) {
3436s = s & crlf & lName & "[" & i & "]=""" & ReplaceList(lObj[i],u,v) & """;";
3437} else {
3438s = s & varToScript(lObj[i], lName & "[""" & i & """]");
3439}
3440}
3441
3442} else if (IsQuery(lObj)) {
3443l = lObj.columnList;
3444
3445s = s & crlf & lName & "=QueryNew(""" & l & """);";
3446s = s & crlf & "QueryAddRow(" & lName & ", " & lObj.recordcount & ");";
3447
3448for(i=1; i LTE lObj.recordcount; i=i+1) {
3449for(j=1; j LTE ListLen(l); j=j+1) {
3450k = lObj[ListGetAt(l,j)][i];
3451if (IsSimpleValue(k)) {
3452s = s & crlf & "QuerySetCell(" & lName & ",""" & ListGetAt(l,j) & """, """ & ReplaceList(k,u,v) & """," & i & ");";
3453} else {
3454t = "request.var2script_" & Replace(CreateUUID(),'-','_','all');
3455s = s & crlf & "QuerySetCell(" & lName & ",""" & ListGetAt(l,j) & """, " & t & "," & i & ");";
3456s = varToScript(k, t) & s;
3457s = s & crlf & "StructDelete(variables,""#t#"");";
3458}
3459}
3460}
3461
3462} else if (IsSimpleValue(lObj)) {
3463s = s & crlf & lName & "=""" & ReplaceList(lObj,u,v) & """;";
3464
3465} else if (IsCustomFunction(lObj)) {
3466s = s & crlf & "/* " & lName & " is a custom fuction, but i can't cfscript it */";
3467
3468} else {
3469s = s & crlf & "/* " & lName & " - not sure what it is.... */";
3470}
3471
3472return s;
3473}
3474
3475/**
3476 * Produces output used by the vCalendar standard for PIM's (such as Outlook).
3477 * There are other tags available such as (CF_AdvancedEmail) that will support multi-part mime encoding where the text of the attachment can be imbeded right into the email
3478 *
3479 * @param stEvent Structure containg the key/value pairs comprising the vCalendar data. Keys are shown below:
3480 * @param stEvent.description Description for the event.
3481 * @param stEvent.subject Subject of the event.
3482 * @param stEvent.location Location for the event.
3483 * @param stEvent.startTime Event's start time in GMT.
3484 * @param stEvent.endTime Event's end time in GMT.
3485 * @param stEvent.priority Numeric priority for the event (1,2,3).
3486 * @return Returns a string.
3487 * @author Chris Wigginton (&#99;&#119;&#105;&#103;&#103;&#105;&#110;&#116;&#111;&#110;&#64;&#109;&#97;&#99;&#114;&#111;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
3488 * @version 1.1, April 10, 2002
3489 */
3490function vCal(stEvent)
3491{
3492
3493var description = "";
3494var vCal = "";
3495
3496var CRLF=chr(13)&chr(10);
3497
3498if (NOT IsDefined("stEvent.startTime"))
3499stEvent.startTime = DateConvert('local2Utc', Now());
3500
3501if (NOT IsDefined("stEvent.endTime"))
3502stEvent.endTime = DateConvert('local2Utc', Now());
3503
3504if (NOT IsDefined("stEvent.location"))
3505stEvent.location = "N/A";
3506
3507if (NOT IsDefined("stEvent.subject"))
3508stEvent.subject = "Auto vCalendar Generated";
3509
3510if (NOT IsDefined("stEvent.description"))
3511stEvent.description = "Autobot VCalendar Generated";
3512
3513if (NOT IsDefined("stEvent.priority"))
3514stEvent.priority = "1";
3515
3516
3517vCal = "BEGIN:VCALENDAR" & CRLF;
3518vCal = vCal & "PRODID:-//Microsoft Corporation//OutlookMIMEDIR//EN" & CRLF;
3519vCal = vCal & "VERSION:1.0" & CRLF;
3520vCal = vCal & "BEGIN:VEVENT" & CRLF;
3521vCal = vCal & "DTSTART:" &
3522DateFormat(stEvent.startTime,"yyyymmdd") & "T" &
3523TimeFormat(stEvent.startTime, "HHmmss") & "Z" & CRLF;
3524vCal = vCal & "DTEND:" & DateFormat(stEvent.endTime, "yyyymmdd") & "T" &
3525TimeFormat(stEvent.endTime, "HHmmss") & "Z" & CRLF;
3526vCal = vCal & "LOCATION:" & stEvent.location & CRLF;
3527vCal = vCal & "SUMMARY;ENCODING=QUOTED-PRINTABLE:" & stEvent.subject & CRLF;
3528
3529vCal = vCal & "DESCRIPTION;ENCODING=QUOTED-PRINTABLE:";
3530// Convert CF_CRLF (13_10) into =0D=0A with CR/LF and indent sequences
3531description = REReplace(stEvent.description,"[#Chr(13)##Chr(10)#]", "=0D=0A=#Chr(13)##Chr(10)# ", "ALL");
3532vCal = vCal & description & CRLF;
3533
3534vCal = vCal & "PRIORITY:" & stEvent.priority & CRLF;
3535vCal = vCal & "END:VEVENT" & CRLF;
3536vCal = vCal & "END:VCALENDAR" & CRLF;
3537
3538return vCal;
3539
3540}
3541
3542/**
3543 * Merges one xml document into another
3544 * Fix sent in by Scott Talmsa
3545 *
3546 * @param xml1 The XML object into which you want to merge (Required)
3547 * @param xml2 The XML object from which you want to merge (Required)
3548 * @param overwriteNodes Boolean value for whether you want to overwrite (default is true) (Optional)
3549 * @return void (changes the first XML object)
3550 * @author Nathan Dintenfass (&#110;&#97;&#116;&#104;&#97;&#110;&#64;&#99;&#104;&#97;&#110;&#103;&#101;&#109;&#101;&#100;&#105;&#97;&#46;&#99;&#111;&#109;)
3551 * @version 2, March 7, 2008
3552 */
3553function xmlMerge(xml1,xml2){
3554var readNodeParent = arguments.xml2;
3555var writeNodeList = arguments.xml1;
3556var writeNodeDoc = arguments.xml1;
3557var readNodeList = "";
3558var writeNode = "";
3559var readNode = "";
3560var nodeName = "";
3561var ii = 0;
3562var writeNodeOffset = 0;
3563var toAppend = 0;
3564var nodesDone = structNew();
3565//by default, overwrite nodes
3566var overwriteNodes = true;
3567//if there's a 3rd arguments, that's the overWriteNodes flag
3568if(structCount(arguments) GT 2)
3569overwriteNodes = arguments[3];
3570//if there's a 4th argument, it's the DOC of the writeNode -- not a user provided argument -- just used when doing recursion, so we know the original XMLDoc object
3571if(structCount(arguments) GT 3)
3572writeNodeDoc = arguments[4];
3573//if we are looking at the whole document, get the root element
3574if(isXMLDoc(arguments.xml2))
3575readNodeParent = arguments.xml2.xmlRoot;
3576//if we are looking at the whole Doc for the first element, get the root element
3577if(isXMLDoc(arguments.xml1))
3578writeNodeList = arguments.xml1.xmlRoot;
3579//loop through the readNodeParent (recursively) and override all xmlAttributes/xmlText in the first document with those of elements that match in the second document
3580for(nodeName in readNodeParent){
3581writeNodeOffset = 0;
3582//if we haven't yet dealt with nodes of this name, do it
3583if(NOT structKeyExists(nodesDone,nodeName)){
3584readNodeList = readNodeParent[nodeName];
3585//if there aren't any of this node, we need to append however many there are
3586if(NOT structKeyExists(writeNodeList,nodeName)){
3587toAppend = arrayLen(readNodeList);
3588}
3589//if there are already at least one node of this name
3590else{
3591//if we are overwriting nodes, we need to append however many there are minus however many there were (if there none new, it will be 0)
3592if(overWriteNodes){
3593toAppend = arrayLen(readNodeList) - arrayLen(writeNodeList[nodeName]);
3594}
3595//if we are not overwriting, we need to add however many there are
3596else{
3597toAppend = arrayLen(readNodeList);
3598//if we are not overwriting, we need to make the offset of the writeNode equal to however many there already are
3599writeNodeOffset = arrayLen(writeNodeList[nodeName]);
3600}
3601}
3602//append however many nodes necessary of the name
3603for(ii = 1; ii LTE toAppend; ii = ii + 1){
3604arrayAppend(writeNodeList.xmlChildren,xmlElemNew(writeNodeDoc,nodeName));
3605}
3606//loop through however many of this nodeName there are, writing them to the writeNodes
3607for(ii = 1; ii LTE arrayLen(readNodeList); ii = ii + 1){
3608writeNode = writeNodeList[nodeName][ii + writeNodeOffset];
3609readNode = readNodeList[ii];
3610//set the xmlAttributes and xmlText to this child's values
3611writeNode.xmlAttributes = readNode.xmlAttributes;
3612//deal with the CDATA scenario to properly preserve (though, if it contains CDATA and text nodes, this won't work!!
3613if(arrayLen(readNode.xmlNodes) AND XmlGetNodeType(readNode.xmlNodes[1]) IS "CDATA_SECTION_NODE"){
3614writeNode.xmlCData = readNode.xmlcdata;
3615}
3616else{
3617//modify to check to see if it's cData or not
3618writeNode.xmlText = readNode.xmlText;
3619}
3620//if this element has any children, recurse
3621if(arrayLen(readNode.xmlChildren)){
3622xmlMerge(writeNode,readNode,overwriteNodes,writeNodeDoc);
3623}
3624}
3625//add this node name to those nodes we have done -- we need to do this because an XMLDoc object can have duplicate keys
3626nodesDone[nodeName] = true;
3627}
3628}
3629}
3630
3631/**
3632 * Processes an XSL Template against an XML document and returns the transformed content.
3633 *
3634 * @param Source XML Source (Required)
3635 * @param Style XML Stylesheet (Required)
3636 * @return Returns the XML data with formatting.
3637 * @author Joshua Miller (&#106;&#111;&#115;&#104;&#64;&#106;&#111;&#115;&#104;&#117;&#97;&#115;&#109;&#105;&#108;&#108;&#101;&#114;&#46;&#99;&#111;&#109;)
3638 * @version 1, September 20, 2004
3639 */
3640function xsltcf5(source,style){
3641// Instantiate COM Objects
3642var objSource=CreateObject("COM", "Microsoft.XMLDOM", "INPROC");
3643var objStyle=CreateObject("COM", "Microsoft.XMLDOM", "INPROC");
3644var sourceReturn = "";
3645var styleReturn = "";
3646var styleRoot = "";
3647var xsloutput = "";
3648// Parse XML
3649objSource.async = "false";
3650sourceReturn = objSource.load("#source#");
3651// Parse XSL
3652objStyle.async = "false";
3653styleReturn = objStyle.load("#style#");
3654// Transform Document
3655styleRoot = objStyle.documentElement;
3656xsloutput = objSource.transformNode(styleRoot);
3657// Output Results
3658return xsloutput;
3659}
3660</cfscript>
3661
3662<!---
3663 Excludes numeric items from an array.
3664 V2 by Raymond Camden
3665 
3666 @param aObj Array to filter. (Required)
3667 @return Returns an array.
3668 @author Marcos Placona (&#109;&#97;&#114;&#99;&#111;&#115;&#46;&#112;&#108;&#97;&#99;&#111;&#110;&#97;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
3669 @version 2, July 6, 2006
3670--->
3671<cffunction name="arrayExcludeNumeric" returntype="array">
3672<cfargument name="aObj" type="array" required="Yes">
3673<cfset var ii = "">
3674
3675<!--- Looping through the array --->
3676<cfloop to="1" from="#arrayLen(aObj)#" index="ii" step="-1">
3677<!--- Checking if it's a number --->
3678<cfif isNumeric(aObj[ii])>
3679<cfset arrayDeleteAt(arguments.aObj, ii) />
3680</cfif>
3681</cfloop>
3682
3683<cfreturn aObj />
3684</cffunction>
3685
3686<!---
3687 Excludes string items from an array.
3688 
3689 @param aObj Array to filter. (Required)
3690 @return Returns an array.
3691 @author Marcos Placona (&#109;&#97;&#114;&#99;&#111;&#115;&#46;&#112;&#108;&#97;&#99;&#111;&#110;&#97;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
3692 @version 1, July 11, 2006
3693--->
3694<cffunction name="arrayExcludeString" returntype="array">
3695<cfargument name="aObj" type="array" required="Yes">
3696<cfset var ii = "">
3697
3698<!--- Looping through the array --->
3699<cfloop to="1" from="#arrayLen(aObj)#" index="ii" step="-1">
3700<!--- Checking if it's a number --->
3701<cfif not isNumeric(aObj[ii])>
3702<cfset arrayDeleteAt(arguments.aObj, ii) />
3703</cfif>
3704</cfloop>
3705
3706<cfreturn aObj />
3707</cffunction>
3708
3709<!---
3710 The arrayFind function uses the java.util.list indexOf function to find an element in an array.
3711 v1 by Nathan Dintenfas.
3712 
3713 @param array Array to search. (Required)
3714 @param valueToFind Value to find. (Required)
3715 @return Returns a number, 0 if no match is found.
3716 @author Larry C. Lyons (&#108;&#97;&#114;&#114;&#121;&#99;&#108;&#121;&#111;&#110;&#115;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
3717 @version 2, August 10, 2007
3718--->
3719<cffunction name="arrayFind" access="public" hint="returns the index number of an item if it is in the array" output="false" returntype="numeric">
3720
3721<cfargument name="array" required="true" type="array">
3722<cfargument name="valueToFind" required="true" type="string">
3723
3724<cfreturn (arguments.array.indexOf(arguments.valueToFind)) + 1>
3725</cffunction>
3726
3727<!---
3728 Search a multidimensional array for a value.
3729 
3730 @param arrayToSearch Array to search. (Required)
3731 @param valueToFind Value to find. (Required)
3732 @param dimensionToSearch Dimension to search. (Required)
3733 @return Returns a number.
3734 @author Grant Szabo (&#103;&#114;&#97;&#110;&#116;&#64;&#113;&#117;&#97;&#103;&#109;&#105;&#114;&#101;&#46;&#99;&#111;&#109;)
3735 @version 1, September 23, 2004
3736--->
3737<cffunction name="ArrayFindByDimension" access="public" returntype="numeric" output="false">
3738<cfargument name="arrayToSearch" type="array" required="Yes">
3739<cfargument name="valueToFind" type="string" required="Yes">
3740<cfargument name="dimensionToSearch" type="numeric" required="Yes">
3741<cfscript>
3742var ii = 1;
3743
3744//loop through the array, looking for the value
3745for(; ii LTE arrayLen(arguments.arrayToSearch); ii = ii + 1){
3746//if this is the value, return the index
3747if(NOT compareNoCase(arguments.arrayToSearch[ii][arguments.dimensionToSearch], arguments.valueToFind))
3748return ii;
3749}
3750//if we've gotten this far, it means the value was not found, so return 0
3751return 0;
3752</cfscript>
3753</cffunction>
3754
3755<!---
3756 This function recurse through a structure and makes all fields as empty string
3757 
3758 @param s Structure to clear. (Required)
3759 @return Returns a structure.
3760 @author Qasim Rasheed (&#113;&#97;&#115;&#105;&#109;&#114;&#97;&#115;&#104;&#101;&#101;&#100;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
3761 @version 1, January 28, 2005
3762--->
3763<cffunction name="clearStructureNested" returntype="void" output="false">
3764<cfargument name="s" type="struct" required="true" />
3765<cfset var i = "">
3766<cfloop collection="#arguments.s#" item="i">
3767<cfif isstruct(arguments.s[i])>
3768<cfset clearStructureNested(arguments.s[i])>
3769<cfelse>
3770<cfset structupdate(arguments.s, i,"")>
3771</cfif>
3772</cfloop>
3773</cffunction>
3774
3775<!---
3776 Takes a .Net dataset and converts it to a CF structure of queries.
3777 
3778 @param dataset Dot net dataset. (Required)
3779 @return Returns a structure.
3780 @author Anthony Petruzzi (&#116;&#111;&#110;&#121;&#112;&#64;&#114;&#111;&#108;&#105;&#115;&#116;&#46;&#99;&#111;&#109;)
3781 @version 1, May 17, 2007
3782--->
3783<cffunction name="convertDotNetDataset" access="public" returnType="struct" output="false"
3784hint="takes a .Net dataset and converts it to a CF structure of queries">
3785<cfargument name="dataset" required="true">
3786<cfset var Local = StructNew()>
3787<cfset Local.result = structNew() />
3788<cfset Local.aDataset = arguments.dataset.get_any() />
3789<cfset Local.xSchema = xmlParse(Local.aDataset[1]) />
3790<cfset Local.xData = xmlParse(Local.aDataset[2]) />
3791
3792<!--- Create Queries --->
3793<cfset Local.xTables = Local.xSchema["xs:schema"]["xs:element"]["xs:complexType"]["xs:choice"] />
3794<cfloop from="1" to="#arrayLen(Local.xTables.xmlChildren)#" index="Local.i">
3795<cfset Local.tableName = Local.xTables.xmlChildren[Local.i].xmlAttributes.name />
3796<cfset Local.xColumns = Local.xTables.xmlChildren[Local.i].xmlChildren[1].xmlChildren[1].xmlChildren/>
3797<cfset Local.result[Local.tableName] = queryNew("") />
3798<cfloop from="1" to="#arrayLen(Local.xColumns)#" index="Local.j">
3799<cfset queryAddColumn(Local.result[Local.tableName], Local.xColumns[Local.j].xmlAttributes.name, arrayNew(1)) />
3800</cfloop>
3801</cfloop>
3802
3803<!--- see if there are any row of data, if not exit --->
3804<cfif NOT StructKeyExists(Local.xData["diffgr:diffgram"], "NewDataSet")>
3805<cfreturn Local.result>
3806</cfif>
3807
3808<!--- Populate Queries --->
3809<cfset Local.xRows = Local.xData["diffgr:diffgram"]["NewDataSet"] />
3810<cfloop from="1" to="#arrayLen(Local.xRows.xmlChildren)#" index="Local.i">
3811<cfset Local.thisRow = Local.xRows.xmlChildren[Local.i] />
3812<cfset Local.tableName = Local.thisRow.xmlName />
3813<cfset queryAddRow(Local.result[Local.tableName], 1) />
3814<cfloop from="1" to="#arrayLen(Local.thisRow.xmlChildren)#" index="Local.j">
3815<cfset querySetCell(Local.result[Local.tableName], Local.thisRow.xmlChildren[Local.j].xmlName, Local.thisRow.xmlChildren[Local.j].xmlText, Local.result[Local.tableName].recordCount) />
3816</cfloop>
3817</cfloop>
3818
3819<cfreturn Local.result>
3820</cffunction>
3821
3822<!---
3823 Converts fixed width string to a ColdFusion query.
3824 Modified by Raymond Camden for missing var, and support newlines better.
3825 
3826 @param columnNames A list of column names. (Required)
3827 @param widths A corresponding list of widths. (Required)
3828 @param data The data to parse. (Required)
3829 @param customRegex A regular expression to be used to parse the line. (Optional)
3830 @return Returns a query.
3831 @author Umer Farooq (&#117;&#109;&#101;&#114;&#64;&#111;&#99;&#116;&#97;&#100;&#121;&#110;&#101;&#46;&#99;&#111;&#109;)
3832 @version 1, December 20, 2007
3833--->
3834<cffunction name="fixedWidthToQuery" hint="I turn fixed width data to query">
3835<cfargument name="columnNames" required="Yes" type="string">
3836<cfargument name="widths" required="Yes" type="string">
3837<cfargument name="data" required="Yes" type="string">
3838<cfargument name="customRegex" required="No" type="string">
3839<cfset var tempQuery = QueryNew(arguments.columnNames)>
3840<cfset var regEx = "">
3841<cfset var findResults = "">
3842<cfset var i = "">
3843<cfset var line = "">
3844<cfset var x = "">
3845
3846<!--- build our regex --->
3847<cfif NOT isDefined("arguments.customRegEx")>
3848<cfloop list="#arguments.widths#" index="i">
3849<cfset regex = regex & "(.{" & i & "})">
3850</cfloop>
3851<cfelse>
3852<cfset regEx = arguments.customRegex>
3853</cfif>
3854
3855<!--- fix newlines for different os --->
3856<cfset arguments.data = replace(arguments.data,chr(10),chr(13),"all")>
3857<cfset arguments.data = replace(arguments.data,chr(13)&chr(13),chr(13),"all")>
3858
3859<!--- loop the data --->
3860<cfloop list="#arguments.data#" delimiters="#chr(13)#" index="line">
3861<!--- run our regex --->
3862<cfset findResults = refind(regEx, line, 1, true)>
3863<!--- find our that our match records equals number of columns plus one. --->
3864<cfif arrayLen(findResults.pos) eq listLen(arguments.columnNames)+1>
3865<cfset QueryAddRow(tempQuery)>
3866<!--- loop the find resuls array from postion 2...
3867and get the column name x-1 as our regex results are number of columsn plus 1
3868and load that data into the query --->
3869<cfloop from="2" to="#arrayLen(findResults.pos)#" index="x">
3870<cfset QuerySetCell(tempQuery, listGetAt(arguments.columnNames, x-1), trim(mid(line, findResults.pos[x], findResults.len[x])))>
3871</cfloop>
3872</cfif>
3873</cfloop>
3874<cfreturn tempQuery>
3875</cffunction>
3876
3877<!---
3878 Pass in an XML Node and attribute reference to receive the attribute's value.
3879 
3880 @param node XML note to retrieve the attribute from. (Required)
3881 @param attribute Attribute to retrieve. (Required)
3882 @param default If attribute does not exist, return this default. (Optional)
3883 @return Returns a string.
3884 @author Kreig Zimmerman (&#107;&#107;&#122;&#64;&#102;&#111;&#117;&#114;&#101;&#121;&#101;&#115;&#46;&#99;&#111;&#109;)
3885 @version 1, December 23, 2002
3886--->
3887<cffunction name="GetXmlAttribute" output="false" returntype="any">
3888<cfargument name="node" type="any" required="yes">
3889<cfargument name="attribute" type="string" required="Yes">
3890<cfargument name="default" type="string" default="" required="false">
3891<cfset var myResult="#arguments.default#">
3892<cfif StructKeyExists(node.XmlAttributes, attribute)>
3893<cfset myResult=node.XmlAttributes["#attribute#"]>
3894</cfif>
3895<cfreturn myResult>
3896</cffunction>
3897
3898<!---
3899 Return true if the queryname passed was a cached query.
3900 
3901 @param queryname Name of query to check. (Required)
3902 @return Returns a boolean.
3903 @author Qasim Rasheed (&#113;&#97;&#115;&#105;&#109;&#114;&#97;&#115;&#104;&#101;&#101;&#100;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
3904 @version 1, February 11, 2005
3905--->
3906<cffunction name="isCachedQuery" returntype="boolean" output="false">
3907<cfargument name="queryname" required="true" type="string" />
3908
3909<cfset var events = "">
3910<cfset var result = false>
3911<cfset var temp = "">
3912
3913<cfif isdebugmode()>
3914<cfset events = createobject('java','coldfusion.server.ServiceFactory').getDebuggingService().getDebugger().getData()>
3915<cfquery name="temp" dbtype="query">
3916select cachedquery
3917from events
3918WHERE type='SqlQuery'
3919and name='#arguments.queryname#'
3920</cfquery>
3921<cfset result = yesnoformat(temp.cachedquery)>
3922</cfif>
3923
3924<cfreturn result />
3925</cffunction>
3926
3927<!---
3928 Adds a column filled with a value to a query object.
3929 V2 by Raymond Camden
3930 v3 by author
3931 
3932 @param query Query to manipulate. (Required)
3933 @param column_name Name of new column. (Required)
3934 @param value Value to use. Defaults to nothing. (Optional)
3935 @return Returns a boolean.
3936 @author Gabriele Bernuzzi (&#103;&#97;&#98;&#114;&#105;&#101;&#108;&#101;&#46;&#98;&#101;&#114;&#110;&#117;&#122;&#122;&#105;&#64;&#103;&#114;&#117;&#112;&#112;&#111;&#116;&#101;&#115;&#105;&#46;&#99;&#111;&#109;)
3937 @version 3, December 13, 2005
3938--->
3939<cffunction name="queryAddColumnWithValue" returntype="boolean" output="false">
3940<cfargument name="query" type="query" required="true" />
3941<cfargument name="column_name" type="string" required="true" />
3942<cfargument name="value" type="string" required="false" default="" />
3943<cfset var arr=arrayNew(1) />
3944
3945<cfscript>
3946arraySet(arr,1,arguments.query.recordCount,arguments.value);
3947queryAddColumn(arguments.query, arguments.column_name, arr);
3948</cfscript>
3949
3950<cfreturn true>
3951</cffunction>
3952
3953<!---
3954 This function will compare two queries and returns a struct which shows the difference between two queries if any.
3955 Fix by Rob Schimp
3956 
3957 @param query1 First query. (Required)
3958 @param query2 Second query. (Required)
3959 @return Returns a struct.
3960 @author Qasim Rasheed (&#113;&#97;&#115;&#105;&#109;&#114;&#97;&#115;&#104;&#101;&#101;&#100;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
3961 @version 2, November 4, 2005
3962--->
3963<cffunction name="queryCompare" returntype="struct" output="false">
3964<cfargument name="query1" type="query" required="true" />
3965<cfargument name="query2" type="query" required="true" />
3966
3967<cfset var rStruct = StructNew()>
3968<cfset var q1 = arguments.query1>
3969<cfset var q2 = arguments.query2>
3970<cfset var q3 = QueryNew( q1.columnlist )>
3971<cfset var q4 = QueryNew( q2.columnlist )>
3972<cfset var message = "">
3973<cfset var rowch = false>
3974<cfset var colArray = listtoarray(q1.columnlist)>
3975<cfset var thisCol = "">
3976<cfset var count = 1>
3977<cfset var i = "">
3978<cfset var j = "">
3979
3980<cfloop from="1" to="#listlen(q1.columnlist)#" index="thisCol">
3981<cfif listfindnocase(q2.columnlist,listgetat(q1.columnlist,thisCol)) eq 0>
3982<cfset message = "Columns in query1 (#q1.columnlist#) and query2 (#q2.columnlist#) doesn't match">
3983</cfif>
3984</cfloop>
3985<cfif not len(trim(message))>
3986<cfloop from="1" to="#listlen(q2.columnlist)#" index="thisCol">
3987<cfif listfindnocase(q1.columnlist,listgetat(q2.columnlist,thisCol)) eq 0>
3988<cfset message = "Columns in query1 (#q1.columnlist#) and query2 (#q2.columnlist#) doesn't match">
3989</cfif>
3990</cfloop>
3991</cfif>
3992
3993<cfif not len(trim(message))>
3994<cfloop from="1" to="#q1.recordcount#" index="i">
3995<cfset rowch = false>
3996<cfloop from="1" to="#arraylen(colArray)#" index="j">
3997<cfif comparenocase(q1[colArray[j]][i],q2[colArray[j]][i])>
3998<cfset rowch = true>
3999</cfif>
4000</cfloop>
4001<cfif rowch>
4002<cfset queryaddrow(q3)>
4003<cfloop from="1" to="#arraylen(colArray)#" index="k">
4004<cfset querysetcell( q3, colArray[k], q1[colArray[k]][count] )>
4005</cfloop>
4006</cfif>
4007<cfset count = count + 1>
4008</cfloop>
4009<cfset count = 1>
4010<cfloop from="1" to="#q2.recordcount#" index="i">
4011<cfset rowch = false>
4012<cfloop from="1" to="#arraylen(colArray)#" index="j">
4013<cfif comparenocase(q1[colArray[j]][i],q2[colArray[j]][i])>
4014<cfset rowch = true>
4015</cfif>
4016</cfloop>
4017<cfif rowch>
4018<cfset queryaddrow(q4)>
4019<cfloop from="1" to="#arraylen(colArray)#" index="k">
4020<cfset querysetcell( q4, colArray[k], q2[colArray[k]][count] )>
4021</cfloop>
4022</cfif>
4023<cfset count = count + 1>
4024</cfloop>
4025<cfif q4.recordcount OR q3.recordcount>
4026<cfset message = "Records do not match">
4027</cfif>
4028</cfif>
4029<cfif len(trim(message))>
4030<cfset structinsert(rStruct,"message",message)>
4031<cfset structinsert(rStruct,"in_query1_butnotin_query2",q3)>
4032<cfset structinsert(rStruct,"in_query2_butnotin_query1",q4)>
4033<cfelse>
4034<cfset structinsert(rStruct,"message","Query 1 and Query 2 are identical")>
4035</cfif>
4036<cfreturn rStruct />
4037</cffunction>
4038
4039<!---
4040 Remove a list of columns from a specified query.
4041 
4042 @param theQuery The query to manipulate. (Required)
4043 @param columnsToRemove A list of columns to remove. (Required)
4044 @return Returns a query.
4045 @author Giampaolo Bellavite (&#103;&#105;&#97;&#109;&#112;&#97;&#111;&#108;&#111;&#64;&#98;&#101;&#108;&#108;&#97;&#118;&#105;&#116;&#101;&#46;&#99;&#111;&#109;)
4046 @version 1, April 14, 2005
4047--->
4048<cffunction name="queryRemoveColumns" output="false" returntype="query">
4049<cfargument name="theQuery" type="query" required="yes">
4050<cfargument name="columnsToRemove" type="string" required="yes">
4051<cfset var columnList=theQuery.columnList>
4052<cfset var columnPosition="">
4053<cfset var c="">
4054<cfset var newQuery="">
4055<cfloop list="#arguments.columnsToRemove#" index="c">
4056<cfset columnPosition=ListFindNoCase(columnList,c)>
4057<cfif columnPosition NEQ 0>
4058<cfset columnList=ListDeleteAt(columnList,columnPosition)>
4059</cfif>
4060</cfloop>
4061<cfquery name="newQuery" dbtype="query">
4062SELECT #columnList# FROM theQuery
4063</cfquery>
4064<cfreturn newQuery>
4065</cffunction>
4066
4067<!---
4068 Sets the values for one or more columns in the specified query row.
4069 
4070 @param query Query to manipulate. (Required)
4071 @param columnlist List of columns to update. (Required)
4072 @param valuelist Values for the new data. (Required)
4073 @param rownumber Row number to modify. If not specified, row is added to end of query. (Optional)
4074 @param delimiter Delimiter for columnlist and valuelist. Defaults to a comma. (Optional)
4075 @param trimElements If true, trims all values. Defaults to true. (Optional)
4076 @return Returns void..
4077 @author Ell Cord (&#108;&#117;&#110;&#97;&#114;&#101;&#99;&#108;&#105;&#112;&#115;&#101;&#49;&#51;&#64;&#101;&#97;&#114;&#116;&#104;&#108;&#105;&#110;&#107;&#46;&#110;&#101;&#116;)
4078 @version 1, October 18, 2005
4079--->
4080<cffunction name="querySetRow" returntype="void" output="false">
4081<cfargument name="query" type="query" required="true" />
4082<cfargument name="columnList" type="string" required="true" />
4083<cfargument name="valuesList" type="string" required="true" />
4084<cfargument name="rowNumber" type="numeric" required="false" default="0" />
4085<cfargument name="delimiter" type="string" required="false" default="," />
4086<cfargument name="trimElements" type="boolean" required="false" default="true" />
4087
4088<cfset var i = 0>
4089<cfset var col = "">
4090<cfset var value = "">
4091
4092<cfif arguments.rowNumber gt 0 and arguments.rowNumber gt arguments.query.recordCount>
4093<cfthrow type="InvalidArgument" message="Invalid rowNumber [#arguments.rowNumber#]. The specified query contains [#arguments.query.RecordCount#] records.">
4094</cfif>
4095<cfif ListLen(arguments.columnList, arguments.delimiter) NEQ ListLen(arguments.valuesList, arguments.delimiter)>
4096<cfthrow type="InvalidArgument" message="[columnList] and [valuesList] do not contain the same number of elements.">
4097</cfif>
4098
4099<cfscript>
4100//by default, append new row to end of query
4101if (val(arguments.rowNumber) lt 1) {
4102QueryAddRow(arguments.query, 1);
4103rowNumber = arguments.query.recordCount;
4104}
4105
4106//set values for each column
4107for (i = 1; i lte ListLen(arguments.columnList, arguments.delimiter); i = i + 1) {
4108if (arguments.trimElements) {
4109col = Trim(ListGetAt(arguments.columnList, i, arguments.delimiter));
4110value = Trim(ListGetAt(arguments.valuesList, i, arguments.delimiter));
4111}
4112else {
4113col = ListGetAt(arguments.columnList, i, arguments.delimiter);
4114value = ListGetAt(arguments.valuesList, i, arguments.delimiter);
4115}
4116query[col][arguments.rowNumber] = value;
4117}
4118</cfscript>
4119</cffunction>
4120
4121<!---
4122 Returns specific number of records starting with a specific row.
4123 Renamed by RCamden
4124 Version 2 with column name support by Christopher Bradford, &#99;&#104;&#114;&#105;&#115;&#116;&#111;&#112;&#104;&#101;&#114;&#46;&#98;&#114;&#97;&#100;&#102;&#111;&#114;&#100;&#64;&#97;&#108;&#105;&#118;&#101;&#111;&#110;&#108;&#105;&#110;&#101;&#46;&#99;&#111;&#109;
4125 
4126 @param theQuery The query to work with. (Required)
4127 @param StartRow The row to start on. (Required)
4128 @param NumberOfRows The number of rows to return. (Required)
4129 @param ColumnList List of columns to return. Defaults to all the columns. (Optional)
4130 @return Returns a query.
4131 @author Kevin Bridges (&#99;&#104;&#114;&#105;&#115;&#116;&#111;&#112;&#104;&#101;&#114;&#46;&#98;&#114;&#97;&#100;&#102;&#111;&#114;&#100;&#64;&#97;&#108;&#105;&#118;&#101;&#111;&#110;&#108;&#105;&#110;&#101;&#46;&#99;&#111;&#109;&#99;&#121;&#98;&#101;&#114;&#115;&#119;&#97;&#116;&#64;&#111;&#114;&#108;&#97;&#110;&#100;&#111;&#97;&#114;&#116;&#105;&#115;&#116;&#114;&#121;&#46;&#99;&#111;&#109;)
4132 @version 2, May 23, 2005
4133--->
4134<cffunction name="QuerySliceAndDice" returntype="query" output="false">
4135<cfargument name="theQuery" type="query" required="true" />
4136<cfargument name="StartRow" type="numeric" required="true" />
4137<cfargument name="NumberOfRows" type="numeric" required="true" />
4138<cfargument name="ColumnList" type="string" required="false" default="" />
4139
4140<cfscript>
4141var FinalQuery = "";
4142var EndRow = StartRow + NumberOfRows;
4143var counter = 1;
4144var x = "";
4145var y = "";
4146
4147if (arguments.ColumnList IS "") {
4148arguments.ColumnList = theQuery.ColumnList;
4149}
4150FinalQuery = QueryNew(arguments.ColumnList);
4151
4152if(EndRow GT theQuery.recordcount) {
4153EndRow = theQuery.recordcount+1;
4154}
4155
4156QueryAddRow(FinalQuery,EndRow - StartRow);
4157
4158for(x = 1; x LTE theQuery.recordcount; x = x + 1){
4159if(x GTE StartRow AND x LT EndRow) {
4160for(y = 1; y LTE ListLen(arguments.ColumnList); y = y + 1) {
4161QuerySetCell(FinalQuery, ListGetAt(arguments.ColumnList, y), theQuery[ListGetAt(arguments.ColumnList, y)][x],counter);
4162}
4163counter = counter + 1;
4164}
4165}
4166
4167return FinalQuery;
4168</cfscript>
4169
4170</cffunction>
4171
4172<!---
4173 Sorts a query using Query of Query.
4174 Updated for CFMX var syntax.
4175 
4176 @param query The query to sort. (Required)
4177 @param column The column to sort on. (Required)
4178 @param sortDir The direction of the sort. Default is "ASC." (Optional)
4179 @return Returns a query.
4180 @author Raymond Camden (&#114;&#97;&#121;&#64;&#99;&#97;&#109;&#100;&#101;&#110;&#102;&#97;&#109;&#105;&#108;&#121;&#46;&#99;&#111;&#109;)
4181 @version 2, October 15, 2002
4182--->
4183<cffunction name="QuerySort" output="no" returnType="query">
4184<cfargument name="query" type="query" required="true">
4185<cfargument name="column" type="string" required="true">
4186<cfargument name="sortDir" type="string" required="false" default="asc">
4187
4188<cfset var newQuery = "">
4189
4190<cfquery name="newQuery" dbType="query">
4191select * from query
4192order by #column# #sortDir#
4193</cfquery>
4194
4195<cfreturn newQuery>
4196
4197</cffunction>
4198
4199<!---
4200 Transpose a query.
4201 
4202 @param inputQuery The query to transpose. (Required)
4203 @param includeHeaders Determines if headers should be included as a column. Defaults to true. (Optional)
4204 @return Returns a query.
4205 @author Glenn Buteau (&#103;&#108;&#101;&#110;&#110;&#46;&#98;&#117;&#116;&#101;&#97;&#117;&#64;&#114;&#111;&#103;&#101;&#114;&#115;&#46;&#99;&#111;&#109;)
4206 @version 1, August 24, 2005
4207--->
4208<cffunction name="queryTranspose" returntype="query">
4209<cfargument name="inputQuery" type="query" required="true">
4210<cfargument name="includeHeaders" type="boolean" default="true" required="false">
4211
4212<cfset var outputQuery = QueryNew("")>
4213<cfset var columnsList = inputQuery.ColumnList>
4214<cfset var newColumn = ArrayNew(1)>
4215<cfset var row = 1>
4216<cfset var zeroString = "000000">
4217<cfset var padFactor = int(log10(inputQuery.recordcount)) + 1 >
4218<cfset var i = "">
4219
4220<cfif includeHeaders>
4221<cfset queryAddColumn(OutputQuery,"col_#right(zeroString & row, padFactor)#",listToArray(ColumnsList))>
4222<cfset row = row + 1>
4223</cfif>
4224
4225<cfloop query="inputQuery">
4226<cfloop index="i" from="1" to="#listlen(columnsList)#">
4227<cfset newColumn[i] = inputQuery[ListGetAt(columnsList, i)][currentRow]>
4228</cfloop>
4229<cfset queryAddColumn(outputQuery,"col_#right(zeroString & row, padFactor)#",newColumn)>
4230<cfset row = row + 1>
4231</cfloop>
4232
4233<cfreturn outputQuery>
4234</cffunction>
4235
4236<!---
4237 QueryTreeSort takes a query and efficiently (O(n)) resorts it hierarchically (parent-child), adding a Depth column that can then be used when displaying the data.
4238 
4239 @param stuff Query to sort. (Required)
4240 @param parentid Column containing parent id. Defaults to parentid. (Optional)
4241 @param itemid Column containing ID value. Defaults to itemid. (Optional)
4242 @param basedepth Base depth of data. Defaults to 0. (Optional)
4243 @param depthname Name for new column to use for depth. Defaults to TreeDepth. (Optional)
4244 @return Returns a query.
4245 @author Rick Osborne (&#100;&#101;&#108;&#105;&#118;&#101;&#114;&#56;&#114;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
4246 @version 1, April 9, 2007
4247--->
4248<cffunction name="queryTreeSort" returntype="query" output="No">
4249<cfargument name="Stuff" type="query" required="Yes">
4250<cfargument name="ParentID" type="string" required="No" default="ParentID">
4251<cfargument name="ItemID" type="string" required="No" default="ItemID">
4252<cfargument name="BaseDepth" type="numeric" required="No" default="0">
4253<cfargument name="DepthName" type="string" required="No" default="TreeDepth">
4254<cfset var RowFromID=StructNew()>
4255<cfset var ChildrenFromID=StructNew()>
4256<cfset var RootItems=ArrayNew(1)>
4257<cfset var Depth=ArrayNew(1)>
4258<cfset var ThisID=0>
4259<cfset var ThisDepth=0>
4260<cfset var RowID=0>
4261<cfset var ChildrenIDs="">
4262<cfset var ColName="">
4263<cfset var Ret=QueryNew(ListAppend(Stuff.ColumnList,Arguments.DepthName))>
4264<!--- Set up all of our indexing --->
4265<cfloop query="Stuff">
4266<cfset RowFromID[Stuff[Arguments.ItemID][Stuff.CurrentRow]]=CurrentRow>
4267<cfif NOT StructKeyExists(ChildrenFromID, Stuff[Arguments.ParentID][Stuff.CurrentRow])>
4268<cfset ChildrenFromID[Stuff[Arguments.ParentID][Stuff.CurrentRow]]=ArrayNew(1)>
4269</cfif>
4270<cfset ArrayAppend(ChildrenFromID[Stuff[Arguments.ParentID][Stuff.CurrentRow]], Stuff[Arguments.ItemID][Stuff.CurrentRow])>
4271</cfloop>
4272<!--- Find parents without rows --->
4273<cfloop query="Stuff">
4274<cfif NOT StructKeyExists(RowFromID, Stuff[Arguments.ParentID][Stuff.CurrentRow])>
4275<cfset ArrayAppend(RootItems, Stuff[Arguments.ItemID][Stuff.CurrentRow])>
4276<cfset ArrayAppend(Depth, Arguments.BaseDepth)>
4277</cfif>
4278</cfloop>
4279<!--- Do the deed --->
4280<cfloop condition="ArrayLen(RootItems) GT 0">
4281<cfset ThisID=RootItems[1]>
4282<cfset ArrayDeleteAt(RootItems, 1)>
4283<cfset ThisDepth=Depth[1]>
4284<cfset ArrayDeleteAt(Depth, 1)>
4285<cfif StructKeyExists(RowFromID, ThisID)>
4286<!--- Add this row to the query --->
4287<cfset RowID=RowFromID[ThisID]>
4288<cfset QueryAddRow(Ret)>
4289<cfset QuerySetCell(Ret, Arguments.DepthName, ThisDepth)>
4290<cfloop list="#Stuff.ColumnList#" index="ColName">
4291<cfset QuerySetCell(Ret, ColName, Stuff[ColName][RowID])>
4292</cfloop>
4293</cfif>
4294<cfif StructKeyExists(ChildrenFromID, ThisID)>
4295<!--- Push children into the stack --->
4296<cfset ChildrenIDs=ChildrenFromID[ThisID]>
4297<cfloop from="#ArrayLen(ChildrenIDs)#" to="1" step="-1" index="i">
4298<cfset ArrayPrepend(RootItems, ChildrenIDs[i])>
4299<cfset ArrayPrepend(Depth, ThisDepth + 1)>
4300</cfloop>
4301</cfif>
4302</cfloop>
4303<cfreturn Ret>
4304</cffunction>
4305
4306<!---
4307 Sends a SQL Batch script and reports results.
4308 
4309 @param BathCode Set of SQL statements. (Required)
4310 @param DSN The Datasource. (Required)
4311 @param sSep Separator. Defaults to GO. (Optional)
4312 @return Returns a struct.
4313 @author Joseph Flanigan (&#106;&#111;&#115;&#101;&#112;&#104;&#64;&#115;&#119;&#105;&#116;&#99;&#104;&#45;&#98;&#111;&#120;&#46;&#111;&#114;&#103;)
4314 @version 2, November 9, 2006
4315--->
4316<cffunction name="SQLBatcher" access="public" returntype="string" hint="Runs a set of queries based on sql string" output="false">
4317<cfargument name="BatchCode" type="string" required="yes">
4318<cfargument name="theDSN" type="string" required="yes">
4319<cfargument name="sSep" type="string" required="no" default="GO">
4320
4321<cfscript>
4322var CleanedBatchCode = ReReplaceNoCase(BatchCode, "--.*?\r", "", "all");// clean sql comments
4323var arBatchBlocks = ArrayNew(1); // index of each block and it's SQL string
4324var separator = REFindNoCase("#arguments.sSep#\r",CleanedBatchCode,1,1); // looks for separators
4325var pos = separator.pos[1]; // 0 or position of first separator
4326var oldpos = 1;
4327var Batch = 0; // count of separator blocks
4328var Block = ""; // Code block of SQL
4329var sSQL = ""; // string to be returned
4330
4331// make sure arguments have length
4332if ( (Len(Trim(theDSN)) EQ 0) OR (Len(Trim(CleanedBatchCode)) EQ 0) ) {
4333sSQL = "<<<ERROR>>> Invalid parameters";
4334return sSQL; // if there is an error stop batcher and return to caller
4335}
4336
4337// if no separator blocks, just query on the one block
4338if(not pos) arBatchBlocks[1] = CleanedBatchCode;
4339// loop around the separator blocks to get the code block for each separator
4340while(pos gt 0) {
4341block = mid(CleanedBatchCode,oldpos,pos-oldpos);
4342// only add a block if there are characters in it.
4343if (ReFind("[[:alnum:]]",block,1,"False")) arrayAppend(arBatchBlocks,block);
4344oldpos = pos + separator.len[1];
4345separator = REFindNoCase("#arguments.sSep#\r|$",CleanedBatchCode,oldpos+1,1);
4346pos = separator.pos[1];
4347}
4348</cfscript>
4349
4350<!--- build return string --->
4351<cfsavecontent variable="sSQL">
4352
4353<cfoutput>#Chr(60)#cftransaction#Chr(62)##Chr(10)##Chr(10)#</cfoutput>
4354<cfloop index="Batch" from="1" to="#ArrayLen(arBatchBlocks)#" step="1">
4355<cfset Block = arBatchBlocks[Batch]>
4356<cfif Len(Trim(Block))><cfoutput>#Chr(60)#cfquery name="q#BATCH#" datasource="#Arguments.theDSN#"#Chr(62)##Chr(10)##Trim(PreserveSingleQuotes(Block))##Chr(10)##Chr(60)#/cfquery#Chr(62)##Chr(10)##Chr(10)#</cfoutput></cfif>
4357</cfloop>
4358<cfoutput>#Chr(60)#/cftransaction#Chr(62)#</cfoutput>
4359</cfsavecontent>
4360
4361<cfreturn sSQL>
4362</cffunction>
4363
4364<!---
4365 Converts a query of XML generated by MSSQL to readable XML string.
4366 
4367 @param doc Name for root level element. (Required)
4368 @param qry Query to convert. (Required)
4369 @return Returns a string.
4370 @author Russel Brown (&#114;&#117;&#115;&#115;&#101;&#108;&#46;&#98;&#114;&#111;&#119;&#110;&#64;&#117;&#110;&#105;&#118;&#101;&#114;&#115;&#97;&#108;&#109;&#105;&#110;&#100;&#46;&#99;&#111;&#109;)
4371 @version 1, April 9, 2007
4372--->
4373<cffunction name="sqlXMLToCFXML" access="public" output="false" returntype="Any" hint="This function will take a multiple row query result and turn it into a CF XML var.">
4374      <cfargument name="doc" type="String" required="false" default="xml" />
4375      <cfargument name="qry" type="Query" required="true" />
4376
4377      <cfset var x = "" />
4378      <cfset var y = "" />
4379      <cfset var retXML = "" />
4380
4381      <cfset x = listFirst(arguments.qry.columnList)>
4382      <cfloop index="y" from="1" to="#arguments.qry.recordCount#">
4383         <cfset retXML = retXML & arguments.qry[x][y]>
4384      </cfloop>
4385
4386      <cfset retXML = "<#arguments.doc#>" & retXML & "</#arguments.doc#>">
4387
4388      <cfreturn retXML>
4389</cffunction>
4390
4391<!---
4392 Merge two simple structures in one combining keys or creating new ones.
4393 
4394 @param struct1 The first struct. (Required)
4395 @param struct2 The second struct. (Required)
4396 @return Returns a struct.
4397 @author Marcos Placona (&#109;&#97;&#114;&#99;&#111;&#115;&#46;&#112;&#108;&#97;&#99;&#111;&#110;&#97;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;)
4398 @version 1, March 2, 2006
4399--->
4400<cffunction name="structMerge" output="false">
4401<cfargument name="struct1" type="struct" required="true">
4402<cfargument name="struct2" type="struct" required="true">
4403<cfset var ii = "" />
4404
4405<!--- Loop over the second structure passed --->
4406<cfloop collection="#arguments.struct2#" item="ii">
4407<cfif structKeyExists(struct1,ii)>
4408<!--- In case it already exists, we just update it --->
4409<cfset struct1[ii] = listAppend(struct1[ii], struct2[ii])>
4410<cfelse>
4411<!--- In all the other cases, just create a new key with the values or list of values --->
4412<cfset struct1[ii] = struct2[ii] />
4413</cfif>
4414</cfloop>
4415<cfreturn struct1 />
4416</cffunction>
4417
4418<!---
4419 Update one structure values with values from another structure for those keys that match.
4420 
4421 @param struct1 The structure to be modified. (Required)
4422 @param struct2 The structure to copy values from. (Required)
4423 @return Returns a structure.
4424 @author Jorge Loyo (&#108;&#111;&#121;&#111;&#106;&#64;&#102;&#105;&#117;&#46;&#101;&#100;&#117;)
4425 @version 1, December 22, 2005
4426--->
4427<cffunction name="structUpdateVals" returntype="struct" output="false">
4428<cfargument name="struct1" required="yes" type="struct" />
4429<cfargument name="struct2" required="yes" type="struct" />
4430
4431<cfloop collection="#struct2#" item="key">
4432<cfif structKeyExists(struct1, key)>
4433<cfset structUpdate(struct1, key, struct2[key]) />
4434</cfif>
4435</cfloop>
4436<cfreturn struct1 />
4437</cffunction>
4438
4439<!---
4440 Validate an XML file against a DTD.
4441 
4442 @param xmlUrl File location of the XML document. (Required)
4443 @param throwOnError Determines if the UDF should throw an error if the XML file doesn't validate. Defaults to false. (Optional)
4444 @param fileLocktimeout Specifies how long CF should wait to obtain a lock on the file. Defaults to 5. (Optional)
4445 @return Returns a boolean.
4446 @author Massimo Foti (&#109;&#97;&#115;&#115;&#105;&#109;&#111;&#64;&#109;&#97;&#115;&#115;&#105;&#109;&#111;&#99;&#111;&#114;&#110;&#101;&#114;&#46;&#99;&#111;&#109;)
4447 @version 1, February 18, 2004
4448--->
4449<cffunction name="validateXMLFile" output="false" returntype="boolean" hint="Validate an XML file against a DTD">
4450<cfargument name="xmlUrl" type="string" required="true" hint="XML document url">
4451<cfargument name="throwerror" type="boolean" required="false" default="false" hint="Throw an exception if the document isn't valid">
4452<cfargument name="fileLockTimeout" type="numeric" required="false" default="5" hint="Specifies the maximum amount of time, to wait to obtain a lock on the file">
4453<cfset var isValid=true>
4454<cfset var saxFactory="">
4455<cfset var xmlReader="">
4456<cfset var eHandler="">
4457<!--- Better to be sure the file exist --->
4458<cfif NOT FileExists(arguments.xmlUrl)>
4459<cfthrow message="validateXMLFile: #arguments.xmlUrl# doesn't exist" type="validateXMLFile">
4460</cfif>
4461<cftry>
4462<cfscript>
4463//Call the SAX parser factory
4464saxFactory = CreateObject("java","javax.xml.parsers.SAXParserFactory").newInstance();
4465//Creates a SAX parser and get the XML Reader
4466xmlReader = saxFactory.newSAXParser().getXMLReader();
4467//Turn on validation
4468xmlReader.setFeature("http://xml.org/sax/features/validation",true);
4469//Create an error handler
4470eHandler = CreateObject("java","org.apache.xml.utils.DefaultErrorHandler").init();
4471//Assign the error handler
4472xmlReader.setErrorHandler(eHandler);
4473</cfscript>
4474<!--- Throw an exception in case any Java initialization failed --->
4475<cfcatch type="Object">
4476<cfthrow message="validateXMLFile: failed to initialize Java objects" type="validateXMLFile">
4477</cfcatch>
4478</cftry>
4479<cftry>
4480<!---
4481Since we are reading the file, we better lock it.
4482Safer thing to do is to use the file's url as name for the lock
4483--->
4484<cflock name="#arguments.xmlUrl#" timeout="#arguments.fileLockTimeout#" throwontimeout="yes" type="readonly">
4485<cfset xmlReader.parse(arguments.xmlUrl)>
4486</cflock>
4487<!--- Catch SAX's exception and set the flag --->
4488<cfcatch type="org.xml.sax.SAXParseException">
4489<!--- The SAX parser failed to validate the document --->
4490<cfset isValid=false>
4491<cfif arguments.throwerror>
4492<!--- Throw an exception with the error message if required --->
4493<cfthrow message="validateXMLFile: Failed to validate the document, #cfcatch.Message#" type="validateXMLFile">
4494</cfif>
4495</cfcatch>
4496</cftry>
4497<!--- Return the boolean --->
4498<cfreturn isValid>
4499</cffunction>
4500
4501<!---
4502 Validate a formatted XML string against a DTD.
4503 
4504 @param xmlString XML to validate. (Required)
4505 @param throwError Determines if the UDF should throw an error if the XML string doesnt validate. Defaults to false. (Optional)
4506 @param baseURL Needed to resolve url found in the DOCTYPE declaration and external entity references. Format must be: http://www.mydomain.com/xmldirectory/ (Optional)
4507 @return Returns a boolean.
4508 @author Massimo Foti (&#109;&#97;&#115;&#115;&#105;&#109;&#111;&#64;&#109;&#97;&#115;&#115;&#105;&#109;&#111;&#99;&#111;&#114;&#110;&#101;&#114;&#46;&#99;&#111;&#109;)
4509 @version 1, February 18, 2004
4510--->
4511<cffunction name="validateXMLString" output="false" returntype="boolean" hint="Validate a formatted XML string against a DTD">
4512<cfargument name="xmlString" type="string" required="true" hint="XML document as string">
4513<cfargument name="throwerror" type="boolean" required="false" default="false" hint="Throw an exception if the document isn't valid">
4514<cfargument name="baseUrl" type="string" required="false" default="" hint="Needed to resolve url found in the DOCTYPE declaration and external entity references. Format must be: http://www.mydomain.com/xmldirectoty/">
4515<cfset var isValid=true>
4516<cfset var jStringReader="">
4517<cfset var xmlInputSource="">
4518<cfset var saxFactory="">
4519<cfset var xmlReader="">
4520<cfset var eHandler="">
4521<cftry>
4522<cfscript>
4523//Use Java string reader to read the CFML variable
4524jStringReader = CreateObject("java","java.io.StringReader").init(arguments.xmlString);
4525//Turn the string into a SAX input source
4526xmlInputSource = CreateObject("java","org.xml.sax.InputSource").init(jStringReader);
4527//Call the SAX parser factory
4528saxFactory = CreateObject("java","javax.xml.parsers.SAXParserFactory").newInstance();
4529//Creates a SAX parser and get the XML Reader
4530xmlReader = saxFactory.newSAXParser().getXMLReader();
4531//Turn on validation
4532xmlReader.setFeature("http://xml.org/sax/features/validation",true);
4533//Add a system id if required
4534if(IsDefined("arguments.baseUrl")){
4535xmlInputSource.setSystemId(arguments.baseUrl);
4536}
4537//Create an error handler
4538eHandler = CreateObject("java","org.apache.xml.utils.DefaultErrorHandler").init();
4539//Assign the error handler
4540xmlReader.setErrorHandler(eHandler);
4541</cfscript>
4542<!--- Throw an exception in case any Java initialization failed --->
4543<cfcatch type="Object">
4544<cfthrow message="validateXMLString: failed to initialize Java objects" type="validateXMLString">
4545</cfcatch>
4546</cftry>
4547<cftry>
4548<cfset xmlReader.parse(xmlInputSource)>
4549<!--- Catch SAX's exception and set the flag --->
4550<cfcatch type="org.xml.sax.SAXParseException">
4551<!--- The SAX parser failed to validate the document --->
4552<cfset isValid=false>
4553<cfif arguments.throwerror>
4554<!--- Throw an exception with the error message if required --->
4555<cfthrow message="validateXMLString: Failed to validate the document, #cfcatch.Message#" type="validateXMLString">
4556</cfif>
4557</cfcatch>
4558</cftry>
4559<!--- Return the boolean --->
4560<cfreturn isValid>
4561</cffunction>
4562
4563<!---
4564 Converts an CF XML objects to string without the XML declaration.
4565 
4566 @param xmlDoc Either a XML document or string. (Required)
4567 @return Returns a string.
4568 @author Massimo Foti (&#109;&#97;&#115;&#115;&#105;&#109;&#111;&#64;&#109;&#97;&#115;&#115;&#105;&#109;&#111;&#99;&#111;&#114;&#110;&#101;&#114;&#46;&#99;&#111;&#109;)
4569 @version 1, August 2, 2003
4570--->
4571<cffunction name="xmlDoctoString" output="no" returntype="string" displayname="xmlDoctoString" hint="Extract the root element inside an XML Doc and return it as a string">
4572<cfargument name="xmlDoc" type="string" required="true" displayname="xmlDoc" hint="An XML Doc or a well formed XML string">
4573<cfset var xmlToParse="">
4574<!--- Check to see if the argument is already an XMLDoc --->
4575<cfif IsXmlDoc(arguments.xmlDoc)>
4576<cfset xmlToParse=arguments.xmlDoc>
4577<cfelse>
4578<!--- We need a parsed XML doc, not just a simple string --->
4579<cftry>
4580<cfset xmlToParse=XmlParse(arguments.xmlDoc, "yes")>
4581<!--- Failed parsing, the string culd be not a well formed XML, throw an exception --->
4582<cfcatch type="Any">
4583<cfthrow message="xmlDoctoString: failed to parse argument.xmlDoc" type="xmlDoctoString">
4584</cfcatch>
4585</cftry>
4586</cfif>
4587<cfreturn xmlToParse.getDocumentElement().toString()>
4588</cffunction>
4589
4590<!---
4591 Extracts the text of named XML elements and returns it in a list.
4592 
4593 @param inString Either an XML object or a string representation. (Required)
4594 @param tagName Tag to look for. (Required)
4595 @param delimiter Delimiter for returned string. Defaults to a comma. (Optional)
4596 @return Returns a string.
4597 @author Samuel Neff (&#115;&#97;&#109;&#64;&#115;&#101;&#114;&#110;&#100;&#101;&#115;&#105;&#103;&#110;&#46;&#99;&#111;&#109;)
4598 @version 1, March 16, 2004
4599--->
4600<cffunction name="xmlExtractList" returnType="string" output="no">
4601   <cfargument name="inString" type="any">
4602   <cfargument name="tagName" type="string">
4603   <cfargument name="delim" default=",">
4604   
4605   <cfset var inXML = "">
4606   
4607   <cfset var elementsArray = "">
4608   <cfset var valuesArray = arrayNew(1)>
4609   <cfset var i=1>
4610   <cfset var j=1>
4611   <cfset var ret = "">
4612   
4613   <cfif isXmlDoc(arguments.inString)>
4614      <cfset inXML = arguments.inString>
4615   <cfelse>
4616      <cfset inXML = xmlParse(arguments.inString)>
4617   </cfif>
4618   
4619   <cfset elementsArray = xmlSearch(inXML, "//" & arguments.tagName)>
4620
4621   
4622   <cfloop index="j" from="1" to="#arrayLen(elementsArray)#">
4623      <cfif elementsArray[j].xmlText neq "">
4624         <cfset valuesArray[i] = elementsArray[j].xmlText>
4625         <cfset i=i+1>
4626      </cfif>
4627   </cfloop>
4628   
4629   <cfset ret = arrayToList(valuesArray, arguments.delim)>
4630   <cfreturn ret>
4631</cffunction>
4632
4633<!---
4634 Validates an XML file against an XML Schema (XSD).
4635 
4636 @param xmlPath Path to XML file. (Required)
4637 @param noNamespaceXsdUri Path to XML Schema file. (Required)
4638 @param namespaceXsdUri Name space. (Required)
4639 @param parseError Struct to contain error information. (Required)
4640 @return Returns a boolean.
4641 @author Samuel Neff (&#115;&#97;&#109;&#64;&#98;&#108;&#105;&#110;&#101;&#120;&#46;&#99;&#111;&#109;)
4642 @version 1, April 14, 2005
4643--->
4644<cffunction name="xsdValidate" returnType="boolean" output="false">
4645  <cfargument name="xmlPath" type="string">
4646  <cfargument name="noNamespaceXsdUri" type="string">
4647  <cfargument name="namespaceXsdUri" type="string">
4648  <cfargument name="parseError" type="struct">
4649 
4650  <cfscript>
4651    var parser = createObject("java","org.apache.xerces.parsers.SAXParser");
4652   
4653    var err = structNew();
4654    var k = "";
4655    var success = true;
4656   
4657    var eHandler = createObject(
4658                     "java",
4659                     "org.apache.xml.utils.DefaultErrorHandler");
4660   
4661    var apFeat = "http://apache.org/xml/features/";
4662    var apProp = "http://apache.org/xml/properties/";
4663   
4664    eHandler.init();
4665   
4666    if (structKeyExists(arguments, "parseError")) {
4667       err = arguments.parseError;
4668     }
4669   
4670   
4671    try {
4672       parser.setErrorHandler(eHandler);
4673       
4674       parser.setFeature(
4675          "http://xml.org/sax/features/validation",
4676          true);
4677         
4678       parser.setFeature(
4679          apFeat & "validation/schema",
4680          true);
4681         
4682       parser.setFeature(
4683          apFeat & "validation/schema-full-checking",
4684          true);
4685       
4686       if (structKeyExists(arguments, "noNamespaceXsdUri") and
4687           arguments.noNamespaceXsdUri neq "") {
4688         
4689          parser.setProperty(
4690            apProp & "schema/external-noNamespaceSchemaLocation",
4691            arguments.noNamespaceXsdUri
4692         
4693          );
4694        }
4695       
4696       if (structKeyExists(arguments, "namespaceXsdUri") and
4697           arguments.namespaceXsdUri neq "") {
4698         
4699          parser.setProperty(
4700            apProp & "schema/external-schemaLocation",
4701            arguments.namespaceXsdUri
4702          );
4703        }
4704       
4705       
4706       parser.parse(arguments.xmlPath);
4707     } catch (Any ex) {
4708       structAppend(err, ex, true);
4709       success = false;
4710     }
4711  </cfscript>
4712
4713  <cfreturn success>
4714 
4715</cffunction>
4716
4717<!---
4718 Provides CFMX native XSL transformations using Java, with support for parameter pass through and relative &amp;lt;xsl:import&amp;gt; tags.
4719 Version 1 was by Dan Switzer
4720 
4721 @param xmlSource The XML Source. (Required)
4722 @param xslSource The XSL Source. (Required)
4723 @param stParameters XSL Parameters. (Optional)
4724 @return Returns a string.
4725 @author Mark Mandel (&#109;&#97;&#114;&#107;&#64;&#99;&#111;&#109;&#112;&#111;&#117;&#110;&#100;&#116;&#104;&#101;&#111;&#114;&#121;&#46;&#99;&#111;&#109;)
4726 @version 2, January 16, 2006
4727--->
4728<cffunction name="xslt" returntype="string" output="No">
4729<cfargument name="xmlSource" type="string" required="yes">
4730<cfargument name="xslSource" type="string" required="yes">
4731<cfargument name="stParameters" type="struct" default="#StructNew()#" required="No">
4732
4733<cfscript>
4734var source = ""; var transformer = ""; var aParamKeys = ""; var pKey = "";
4735var xmlReader = ""; var xslReader = ""; var pLen = 0;
4736var xmlWriter = ""; var xmlResult = ""; var pCounter = 0;
4737var tFactory = createObject("java", "javax.xml.transform.TransformerFactory").newInstance();
4738
4739//if xml use the StringReader - otherwise, just assume it is a file source.
4740if(Find("<", arguments.xslSource) neq 0)
4741{
4742xslReader = createObject("java", "java.io.StringReader").init(arguments.xslSource);
4743source = createObject("java", "javax.xml.transform.stream.StreamSource").init(xslReader);
4744}
4745else
4746{
4747source = createObject("java", "javax.xml.transform.stream.StreamSource").init("file:///#arguments.xslSource#");
4748}
4749
4750transformer = tFactory.newTransformer(source);
4751
4752//if xml use the StringReader - otherwise, just assume it is a file source.
4753if(Find("<", arguments.xmlSource) neq 0)
4754{
4755xmlReader = createObject("java", "java.io.StringReader").init(arguments.xmlSource);
4756source = createObject("java", "javax.xml.transform.stream.StreamSource").init(xmlReader);
4757}
4758else
4759{
4760source = createObject("java", "javax.xml.transform.stream.StreamSource").init("file:///#arguments.xmlSource#");
4761}
4762
4763//use a StringWriter to allow us to grab the String out after.
4764xmlWriter = createObject("java", "java.io.StringWriter").init();
4765
4766xmlResult = createObject("java", "javax.xml.transform.stream.StreamResult").init(xmlWriter);
4767
4768if(StructCount(arguments.stParameters) gt 0)
4769{
4770aParamKeys = structKeyArray(arguments.stParameters);
4771pLen = ArrayLen(aParamKeys);
4772for(pCounter = 1; pCounter LTE pLen; pCounter = pCounter + 1)
4773{
4774//set params
4775pKey = aParamKeys[pCounter];
4776transformer.setParameter(pKey, arguments.stParameters[pKey]);
4777}
4778}
4779
4780transformer.transform(source, xmlResult);
4781
4782return xmlWriter.toString();
4783</cfscript>
4784</cffunction>