ColdFusion Recursive Functions

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:

<cfsetting enablecfoutputonly="true" requesttimeout="8000">
<!---
===========================================================================
// CLASS/COMPONENT:

// DirRecursion.cfm

//
// DESCRIPTION:

// This template will recurse through all directories and subdirectories, beginning

// from the root directory defined in 'VARIABLES.projectRoot', and write

// the resultant output to a designated XML file. It includes an absurd

// requesttimeout directive, in the event of a very large folder structure.

//
// AUTHOR:

// Steve 'Cutter' Blades, no.junkATcutterscrossingDOTcom

//
// REVISION HISTORY:

// ******************************************************************************

// User: SGB Date: 2/27/2007

// Initial Creation

// ******************************************************************************
=========================================================================== --->


<cfset VARIABLES.srcOutput = "" />
<cfset VARIABLES.destOutput = "" />
<cfset VARIABLES.mkDirOutput = "" />
<cfset VARIABLES.subLevel = 0 />
<cfset VARIABLES.projectRoot = "/" />
<cfset VARIABLES.CrLf = Chr(13) & Chr(10) />

<cffunction name="recurseDir" access="public" output="false" returntype="void">
    <cfargument name="testPath" required="true" type="string" />
    <cfargument name="dotPath" required="true" type="string" />
    <cfargument name="level" required="true" type="numeric" />
    <cfset var rootDir = "" />
    <cfset var tempPath = "" />
    <cfset var tempDot = "" />
    <cfset var tempVal = "" />
    <cfdirectory action="list" name="rootDir" directory="#expandpath("#ARGUMENTS.testPath#")#" />
    <cfloop query="rootDir">
        <cfif rootDir.type eq "Dir">
            <cfif ARGUMENTS.testPath neq "/">
                <cfset tempPath = ARGUMENTS.testPath & "/" & rootDir.name />
                <cfset tempDot = ARGUMENTS.dotPath & "." & LCase(Replace(rootDir.name,".","","all")) />
                <cfset tempVal = '<property name="src.#tempDot#" location="${src.#LCase(ARGUMENTS.dotPath)#}\#rootDir.name#" />' />
                <cfset VARIABLES.srcOutput = VARIABLES.srcOutput & tempVal & VARIABLES.CrLf />
                <cfset VARIABLES.destOutput = VARIABLES.destOutput & Replace(tempVal,"src","dest","all") & VARIABLES.CrLf />
            <cfelse>
                <cfset tempPath = ARGUMENTS.testPath & rootDir.name />
                <cfset tempDot = LCase(Replace(rootDir.name,".","","all")) />
                <cfset tempVal = '<property name="src.#tempDot#" location="${src}\#rootDir.name#" />' />
                <cfset VARIABLES.srcOutput = VARIABLES.srcOutput & tempVal & VARIABLES.CrLf />
                <cfset VARIABLES.destOutput = VARIABLES.destOutput & Replace(tempVal,"src","dest","all") & VARIABLES.CrLf />
            </cfif>
            <cfset VARIABLES.mkDirOutput = VARIABLES.mkDirOutput & '<mkdir dir="${dest.#tempDot#}" />' & VARIABLES.CrLf />
            <cfset recurseDir(tempPath,tempDot,ARGUMENTS.level+1) />
        </cfif>
    </cfloop>
</cffunction>

<cfset recurseDir(VARIABLES.projectRoot,"",VARIABLES.subLevel) />
<cfsavecontent variable="VARIABLES.totalContent">
<cfoutput>
#VARIABLES.srcOutput#
#VARIABLES.CrLf##VARIABLES.CrLf#
#VARIABLES.destOutput#
#VARIABLES.CrLf##VARIABLES.CrLf#
#VARIABLES.mkDirOutput#
</cfoutput>
</cfsavecontent>
<cffile action="write" file="#expandpath(VARIABLES.projectRoot)#\aTemp\new_bu.xml" output="#VARIABLES.totalContent#">
<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.

TweetBacks
Comments
Dave Konopka's Gravatar Nice use of recursion. I've just been getting into ANT myself, and this will be useful.

I wrote something similar recently to compare files between two folders:
http://www.lifelikeweeds.com/tech/2007/02/22/file-...
# Posted By Dave Konopka | 2/28/07 9:12 AM
Cutter's Gravatar Dave,

Thanks for your comments. I'm going to read your article more in depth, it sounds very interesting. This was just something very down and dirty, for which I've already discovered some minor glitches (like additional dots in my dot notation paths). I'll probably have to come up with some sort of regular expression to sanitize a directory name prior to adding it to the dot notation path, but I hope you find it useful.
# Posted By Cutter | 2/28/07 9:49 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.3.006. Contact Blog Owner. Layout inspired by bluerobot.com., with some JQuery thrown in for fun.