Friday, February 12, 2010

Underlying Java Methods for ColdFusion Data Types

Every now and then in blog posts I'll see people using Java methods rather than ColdFusion functions in their code examples. For instance, they might use <cfset users.add("Tony") /> rather than the typical <cfset arrayAppend(users, "Tony") />. I never gave it much thought before, since I didn't know what all was available. Today I decided to spend a little time playing around with the underlying Java methods available for different ColdFusion data types. I came up with the following script that shows all the available methods.


<cfset dataTypes = {} />
<cfset dataTypes["array"] = [] />
<cfset dataTypes["boolean"] = true />
<cfset dataTypes["date"] = now() />
<cfset dataTypes["integer"] = 1 />
<cfset dataTypes["numeric"] = 1.5 />
<cfset dataTypes["query"] = queryNew("id") />
<cfset dataTypes["string"] = "" />
<cfset dataTypes["struct"] = {} />

<cfset metaData = {} />

<cfloop collection="#dataTypes#" item="dataType">

<cfset metaData[dataType] = {} />
<cfset metaData[dataType].class = dataTypes[dataType].getClass().toString() />
<cfset metaData[dataType].methods = {} />

<cfset classMethods = dataTypes[dataType].getClass().getMethods() />

<cfloop array="#classMethods#" index="classMethod">

<cfset method = {} />
<cfset method.string = classMethod.toString() />
<cfset method.name = listLast(listFirst(method.string, "("), ".")/>

<cfset method.parameters = listToArray(listFirst(listLast(method.string, "("), ")")) />

<cfloop from="1" to="#arrayLen(method.parameters)#" index="i">
<cfset method.parameters[i] = listLast(method.parameters[i], ".") />
</cfloop>

<cfset method.returnType = listToArray(listFirst(method.string, "("), " ") />
<cfset method.returnType = listLast(method.returnType[arrayLen(method.returnType)-1], ".") />

<cfset metaData[dataType].methods[method.name & "(" & arrayToList(method.parameters) & ")"] = method />

</cfloop>

</cfloop>


If you were to <cfdump var="#metaData#" />, you'd get something that looks like this:



I haven't spent too much time playing around with all methods, but let's take a look at a couple examples:

Check to see if a start date is before an end date:

<cfset startDate = now() />
<cfset endDate = dateAdd("d", 1, startDate) />

<cfif dateCompare(startDate, endDate) eq -1>
The start date is before the end date.
<cfelse>
The start date is not before the end date.
</cfif>


Same thing, but using Java:

<cfset startDate = now() />
<cfset endDate = dateAdd("d", 1, startDate) />

<cfif startDate.before(endDate)>
The start date is before the end date.
<cfelse>
The start date is not before the end date.
</cfif>


Check to see if a name ends with "Nelson":

<cfset name = "Tony Nelson" />

<cfif right(name, 6) eq "Nelson">
The name ends with "Nelson".
<cfelse>
The name does not end with "Nelson".
</cfif>


Same thing, but using Java:

<cfset name = "Tony Nelson" />

<cfif name.endsWith("Nelson")>
The name ends with "Nelson".
<cfelse>
The name does not end with "Nelson".
</cfif>


Get the number of elements in a struct:

<cfset states = {} />
<cfset states["MN"] = "Minnesota" />
<cfset states["ND"] = "North Dakota" />

<cfoutput>
#structCount(states)#
</cfoutput>


Same thing, but using Java:

<cfset states = {} />
<cfset states["MN"] = "Minnesota" />
<cfset states["ND"] = "North Dakota" />

<cfoutput>
#states.size()#
</cfoutput>


Append the items of one array onto another array:

<cfset states = [] />
<cfset states[1] = "Minnesota" />
<cfset states[2] = "North Dakota" />

<cfset newStates = [] />
<cfset newStates[1] = "South Dakota" />

<cfloop array="#newStates#" index="state">
<cfset arrayAppend(states, state) />
</cfloop>


Same thing, but using Java:

<cfset states = [] />
<cfset states[1] = "Minnesota" />
<cfset states[2] = "North Dakota" />

<cfset newStates = [] />
<cfset newStates[1] = "South Dakota" />

<cfset states.addAll(newStates) />


Granted the differences are all pretty minor, yet the Java examples all read better to me.

5 comments:

  1. be careful, endsWith() carries out case-sensitive comparison, so it is not the same as

    right(name, 6) eq "Nelson"

    more like

    compare(right(name, 6), "Nelson") == 0

    ReplyDelete
  2. Replace implicit with explicit array and struct creators and you'll get a code good to run on all CFML engines: ACF, Railo and ODB.

    I did it, because I wanted to see how different engines did those implementations.
    It's interesting that CF9 and Open BlueDragon returned java.lang.String class as native interpretation for CF boolean and integer/numeric, whilst Railo returned, as expected, java.lang.Boolean and java.lang.Double.

    So, be careful, again, when using java native method against CF vars, in case you write multi-engine app. I know it's tempting, at least it is to me :)

    Here are few articles that I think are "must read" and cover same subjects:

    Internals of the Adobe ColdFusion Runtime
    http://www.cfug-md.org/meetings/EPresentation.pdf

    Building Hybrid Applications with ColdFusion and Java
    http://www.cfinsider.com/enclosures/CF-Java-2008.pdf

    ReplyDelete
  3. @Marko,

    Good to know about the other CF engines. I haven't done too much work outside of Adobe ColdFusion, but thanks for the heads up. Also, thanks for the links. Interesting stuff.

    ReplyDelete
  4. Keep in mind that, at least in Adobe CF, calling the Java methods is often much slower than using CF built-ins (like ArrayLen or [] indexing notation). The reason is that CF has to reflect over the object's methods when you call a method on it rather than use a built-in.

    ReplyDelete