Recursion is defined as a method calling itself.

A long time ago, in a land far, far away, I was studying Computer Science, taking courses to lead to certifications in Object Oriented Programming. Back then I was introducedto the concept of recursion, which sounded very fascinating, but I never thought I would have a practical use for it. Times have changed, for me.

I am now finding many different cases where recursion meets my needs. Doug Boude wrote a terrific post, some time back, on using recursion when building a hierarchal menu, from which I have adjusted and used several times over. But what other uses might there be?

Do you manage a large site? Do you use Ant for deployments? I love Ant, but writing out the property definitions and mkdir commands for a large site can be more than a little tedious. So, I started thinking this through. How can I recurse through every directory and subdirectory, building the path information and dot notation required for these portions of an Ant script? Well, I won't say it's extremely pretty, but here's what I came up with:

view plain print about
1<cfsetting enablecfoutputonly="true" requesttimeout="8000">
2<!---
3===========================================================================
4// CLASS/COMPONENT:
5
6// DirRecursion.cfm
7
8//
9// DESCRIPTION:
10
11// This template will recurse through all directories and subdirectories, beginning
12
13// from the root directory defined in 'VARIABLES.projectRoot', and write
14
15// the resultant output to a designated XML file. It includes an absurd
16
17// requesttimeout directive, in the event of a very large folder structure.
18
19//
20// AUTHOR:
21
22// Steve 'Cutter' Blades, no.junkATcutterscrossingDOTcom
23
24//
25// REVISION HISTORY:
26
27// ******************************************************************************
28
29// User: SGB Date: 2/27/2007
30
31// Initial Creation
32
33// ******************************************************************************
34=========================================================================== --->

35
36<cfset VARIABLES.srcOutput = "" />
37<cfset VARIABLES.destOutput = "" />
38<cfset VARIABLES.mkDirOutput = "" />
39<cfset VARIABLES.subLevel = 0 />
40<cfset VARIABLES.projectRoot = "/" />
41<cfset VARIABLES.CrLf = Chr(13) & Chr(10) />
42
43<cffunction name="recurseDir" access="public" output="false" returntype="void">
44    <cfargument name="testPath" required="true" type="string" />
45    <cfargument name="dotPath" required="true" type="string" />
46    <cfargument name="level" required="true" type="numeric" />
47    <cfset var rootDir = "" />
48    <cfset var tempPath = "" />
49    <cfset var tempDot = "" />
50    <cfset var tempVal = "" />
51    <cfdirectory action="list" name="rootDir" directory="#expandpath("#ARGUMENTS.testPath#")#" />
52    <cfloop query="rootDir">
53        <cfif rootDir.type eq "Dir">
54            <cfif ARGUMENTS.testPath neq "/">
55                <cfset tempPath = ARGUMENTS.testPath & "/" & rootDir.name />
56                <cfset tempDot = ARGUMENTS.dotPath & "." & LCase(Replace(rootDir.name,".","","all")) />
57                <cfset tempVal = '<property name="src.#tempDot#" location="${src.#LCase(ARGUMENTS.dotPath)#}\#rootDir.name#" />' />

58                <cfset VARIABLES.srcOutput = VARIABLES.srcOutput & tempVal & VARIABLES.CrLf />
59                <cfset VARIABLES.destOutput = VARIABLES.destOutput & Replace(tempVal,"src","dest","all") & VARIABLES.CrLf />
60            <cfelse>
61                <cfset tempPath = ARGUMENTS.testPath & rootDir.name />
62                <cfset tempDot = LCase(Replace(rootDir.name,".","","all")) />
63                <cfset tempVal = '<property name="src.#tempDot#" location="${src}\#rootDir.name#" />' />
64                <cfset VARIABLES.srcOutput = VARIABLES.srcOutput & tempVal & VARIABLES.CrLf />
65                <cfset VARIABLES.destOutput = VARIABLES.destOutput & Replace(tempVal,"src","dest","all") & VARIABLES.CrLf />
66            </cfif>
67            <cfset VARIABLES.mkDirOutput = VARIABLES.mkDirOutput & '<mkdir dir="${dest.#tempDot#}" />' & VARIABLES.CrLf />
68            <cfset recurseDir(tempPath,tempDot,ARGUMENTS.level+1) />
69        </cfif>
70    </cfloop>
71</cffunction>
72
73<cfset recurseDir(VARIABLES.projectRoot,"",VARIABLES.subLevel) />
74<cfsavecontent variable="VARIABLES.totalContent">
75<cfoutput>
76#VARIABLES.srcOutput#
77#VARIABLES.CrLf##VARIABLES.CrLf#
78#VARIABLES.destOutput#
79#VARIABLES.CrLf##VARIABLES.CrLf#
80#VARIABLES.mkDirOutput#
81</cfoutput>
82</cfsavecontent>
83<cffile action="write" file="#expandpath(VARIABLES.projectRoot)#\aTemp\new_bu.xml" output="#VARIABLES.totalContent#">
84<cfsetting enablecfoutputonly="false" />

It's small, open to improvement and ridicule, but it works (and works well). First pass on my production system gave me 10,200 lines of XML, giving me source and destination file properties, as well as the corresponding mkdir command set. Obviously this doesn't generate your entire script for you, but it does a huge bit of the grunt work, and I thought it was a fairly good example of a recursive script in ColdFusion. Any questions/edits/criticisms are always welcome.