Duncan’s blog

March 1, 2013

Adobe ColdFusion Developer Center

Filed under: Coldfusion — duncan @ 10:40 am
Tags: , ,

I’d heard there was a hotfix change to Adobe ColdFusion 9 & 10 for the CFMap tag, to start using Google Maps API v3 instead of the deprecated (and very-soon to be unavailable) Google Maps API v2. So I decided to make an infrequent visit to the Developer Center and see if I could find anything about it. Nothing obvious, but I noticed this:

CFDev Center

Aha, a useful example on ‘working with Google maps’ using cfmap!  Unfortunately that link just displays a map. It doesn’t show any code, so is useless as an example of using CFMap.  Similarly the other link on using cfspreadsheet merely shows me a dumped query and a table… very useful I’m sure.

January 22, 2013

Converting UUID to Base36

Filed under: Coldfusion — duncan @ 10:40 am
Tags: , , , , , ,
Base36
Base 36

I needed to generate a random unique string of no more than 30 characters length for integrating with an external API.  Usually for something like this we’d use a UUID/GUID, which are 36 characters long (or 35 in ColdFusion).  A colleague came up with the idea of taking that and converting it to Base 32.  He was using C# and this class, but then this part of the work got transferred to the ColdFusion team, so here’s the approach I took.

Firstly, a UUID is a hexadecimal representation of a 128-bit number.  Being hexadecimal that’s Base 16 obviously; the characters 0-9A-F.

A 128-bit number = 2^128, which is a very big number (340,282,366,920,938,463,463,374,607,431,768,211,456 possible values).  ColdFusion can struggle with integers this size in certain functions, e.g. InputBaseN (and its counterpart FormatBaseN), otherwise that would be ideal for what I’m trying to do here

There’s already a function in CFLib which could do this, but it’s compatible with ColdFusion 5, i.e. it’s not got access to any of the underlying Java classes which CFMX does.  My colleague Adam also wrote a different function which can convert between any two bases, but it’s more fully-featured than I require as well.

UUID’s also contain hyphens.  Typically a UUID format is in a 8-4-4-4-12 pattern, i.e. the string is 36 characters long.  ColdFusion does it differently; 8-4-4-16, only 35 characters long.  Apparently this difference was a mistake, which they didn’t want to amend later due to backwards-compatibility. However for the purposes of this code, I don’t care either way, I just want to get rid of the hyphen separators.

So, step 1 is remove the hyphens from a UUID:

<cfset strUUID = createUUID()>
<cfset strUUID = replace(strUUID, "-", "", "ALL")>

Now I want to turn this hexadecimal string into a BigInteger, which is the Java class which can deal with numbers this size (Integer only goes up to 2^31-1, Long goes up to 2^63-1, so BigInteger it has to be).

<cfset bigInt = createObject("java", "java.math.BigInteger").init(strUUID, 16)>

So we use the BigInteger constructor, specifying our string and the Base 16 radix as the second parameter.

Step 3, we want to then convert that BigInteger to Base 36. This is the characters A-Z0-9, and is ‘the most compact case-insensitive alphanumeric numeral system using ASCII characters’. Which I took to be a good thing; the advantage that Base 32 (that my colleague had chosen) would have given is it eliminates certain characters which might be easily-confused if you were reading the values after encoding in that base. So it’s ideal for generating codes used for humans, but that wasn’t going to be an issue here, so I chose Base 36 instead.

<cfset strBase36 = bigInt.toString(36)>

Putting it all together:

<cfset strUUID = createUUID()>
<cfset strUUID = replace(strUUID, "-", "", "ALL")>
<cfset bigInt = createObject("java", "java.math.BigInteger").init(strUUID, 16)>
<cfset strBase36 = bigInt.toString(36)>

<cfoutput>
strUUID: #strUUID#, #len(strUUID)#<br>
strBase36: #strBase36#, #len(strBase36)#
</cfoutput>

Gives me something like:

strUUID: AFA64721B8AC6F8CD055DE2527EB20F9, 32
strBase36: aecxzys9dnwydn6ejgkxwgb95, 25

PS: apologies to Base36 (the company) for borrowing their funky (and let’s be honest, better-executed) Base36 mosaic idea.

October 18, 2012

ColdFusion cfloop versus cfscript for loop performance

Filed under: Coldfusion — duncan @ 7:01 pm
Tags: , ,

I’m sure most web developers are aware that, in Javascript, it’s generally considered best practice to do this:

for (var i = 0, len = array.length; i < len; i++) { }

instead of this:

for (var i = 0; i < array.length; i++) { }

The theory being that you improve performance by caching the array length once, instead of each time you iterate over the loop (although it’s open to debate how important this is; it really only benefits IE).

I decided to see if there was any benefit in doing the same thing with array loops in ColdFusion. Initially just tested with CFML, then again with CFScript, to see if there were any differences.

<!--- start with a blank file each time --->
<cfif fileExists("arraylen.txt")>
	<cffile action="delete" file="arraylen.txt">
</cfif>

<cfloop index="j" from="1" to="1000">
	<!--- create a large array to loop over later --->
	<cfset myArray = []>	
	<cfloop index="i" from="1" to="10000000"><cfset arrayAppend(myArray, "")></cfloop>

	<cfscript>
		// Method 1
		tick = GetTickCount();

		for (i=1; i <= arrayLen(myArray); i++) {}

		recordTime(
			type	= "CFScript (arrayLen)",
			tick	= tick
		);

		// Method 2
		tick = GetTickCount();

		len = arrayLen(myArray);

		for (i=1; i <= len; i++) {}

		recordTime(
			type	= "CFScript (var'ed)",
			tick	= tick
		);
	</cfscript>

	<!--- Method 1 --->
	<cfset tick = GetTickCount()>

	<cfloop index="i" from="1" to="#arrayLen(myArray)#"></cfloop>

	<cfset recordTime(
		type	= "CFML (arrayLen)",
		tick	= tick
	)>

	<!--- Method 2 --->
	<cfset tick = GetTickCount()>

	<cfset len = arrayLen(myArray)>

	<cfloop index="i" from="1" to="#len#"></cfloop>

	<cfset recordTime(
		type	= "CFML (var'ed)",
		tick	= tick
	)>
</cfloop>

<cffunction name="recordTime" output="false" returntype="void">
	<cfargument name="type" type="string" required="true">
	<cfargument name="tick" type="numeric" required="true">

	<cfset var tock = GetTickCount()>
	<cfset var time = tock - arguments.tick>

	<cffile action="append" output="#arguments.type##chr(9)##time#ms" file="arraylen.txt">
</cffunction>

What I did find was that the two methods, of either calculating arrayLen() within the FOR loop, or storing it as a variable, hardly differed at all in performance.

However there was a big difference in performance between CFML and CFScript. In this case, the looping was much slower in CFScript. This isn’t something new, I’m sure I’ve heard this before, but it was an interesting reminder for me that sometimes it’s not always best to write everything in CFScript.

So here’s the results showing the average times from running this 1000 times:

CFML (arrayLen) 257.883ms
CFML (var’ed) 258.284ms
CFScript (arrayLen) 1259.294ms
CFScript (var’ed) 1180.07ms

As we can see, the difference between caching the array length in a variable, or using arrayLen() for the loop limit, is negligible in CFML, less than 0.5 of a millisecond after 1000 iterations!

However in CFScript, it seems slightly quicker to use a variable instead of referring to arrayLen() within the iterator. But not so much that I’d say I’d favour one method over the other. Although I’ll certainly think twice before writing array loops in CFScript instead of CFML… although in reality the difference in performance is relatively minor (unless you’re working on massive arrays and/or have a high-traffic application).

PS: I’m running this on Adobe ColdFusion 9,0,1,274733 on Windows 7, YMMV :-)

March 12, 2012

ColdFusion regular expression backreferences and numeric strings

Supposing you have a string that starts with a number, e.g. “25% discount this weekend”, and you’re using that along with a regular expression.  If you’ve got a backreference immediately prior to that “25%…”, ColdFusion treats it as \125 instead of \1. 

For instance:

<cfset myString = "25% Discount this weekend">

<cfset originalString = "Look out for our special offers, including XXX and much more">

<cfset newString = reReplace(originalString, "(.*)XXX(.*)", "\1#myString#\2")>

<cfoutput>#newString#</cfoutput>

This just outputs:
% Discount this weekend and much more
as it thinks that you’re trying to do \125 instead of \1 followed by “25%…”

To get around this, you can use \E to separate the backreference from the rest of the string.
\E is meant to indicate when you’ve reached the end of an uppercase or lowercase block as set by \U or \L. However in this case it simply indicates to the regular expression process where the end of the backreference occurs.

<cfset newString = reReplace(originalString, "(.*)XXX(.*)", "\1\E#myString#\2")>

<cfoutput>#newString#</cfoutput>

This time it correctly outputs:
Look out for our special offers, including 25% Discount this weekend and much more

November 10, 2011

ColdFusion query-of-queries, tips for adding dynamic columns

Filed under: Coldfusion — duncan @ 5:35 pm
Tags: , , ,

Supposing you’re doing a query of queries, and you want to add a dynamic column, perhaps based on a mixture of existing CF variables and values from the original query. There are a few gotchas to watch out for.

Firstly, you have to use single quotes ‘ ‘ instead of double quotes ” ” for anything in the string. This is contrary to ColdFusion’s normal allowance for any mixture, e.g. this is usually perfectly fine:

<cfset x = "hi" & 'hi'>

However when trying to use ” ” in the query-of-queries, it throws the error:

Query Of Queries syntax error.
Encountered “. Lexical error at line 3, column 25. Encountered: “\”" (34), after : “”

This query will throw that error:

<cfquery name="rstGetStuff" dbtype="query">
	SELECT 	"Hello World" AS columnName
	FROM rstOriginalQuery
</cfquery>

But this is fine:

<cfquery name="rstGetStuff" dbtype="query">
	SELECT 	'Hello World' AS columnName
	FROM rstOriginalQuery
</cfquery>

Secondly, if you’re wanting to refer to variables within that string, just add them into the string surrounded by # #. Again, contrary to normal CFML, where these are equivalent:

<cfset x = "hi " & variables.strName>
<cfset x = "hi #variables.strName#">

So this throws the error:

Query Of Queries syntax error.
Encountered “.. Incorrect Select Statement, Expecting a ‘FROM’, but encountered ‘.’ instead, A select statement should have a ‘FROM’ construct.

<cfset strName = "Duncan">

<cfquery name="rstGetStuff" dbtype="query">
	SELECT 	'Hello ' + strName AS columnName
	FROM rstOriginalQuery
</cfquery>

Instead you have to do

<cfquery name="rstGetStuff" dbtype="query">
	SELECT 	'Hello #strName#' AS columnName
	FROM rstOriginalQuery
</cfquery>

Thirdly, if you’re wanting to add values from the original query into that dynamic column, you might need to CAST it to the correct type. In this case I have a column in the original query, intIDColumn, that is a numeric ID.

This throws the error:

Query Of Queries runtime error.
Cannot mix types INTEGER and VARCHAR in a + binary operation.

<cfquery name="rstGetStuff" dbtype="query">
	SELECT 	'/some/url/path/' + intIDColumn + '/' AS strURL
	FROM rstOriginalQuery
</cfquery>

Instead I have to cast it to a VARCHAR when trying to add it into my string.

<cfquery name="rstGetStuff" dbtype="query">
	SELECT 	'/some/url/path/' + CAST(intIDColumn AS VARCHAR) + '/' AS strURL
	FROM rstOriginalQuery
</cfquery>

September 21, 2011

ColdFusion LSCurrencyFormat inserts hard spaces?

Filed under: Coldfusion — duncan @ 8:31 pm
Tags: , , , , , ,

Today I was doing some work with the LSCurrencyFormat function for the very first time. Along the way I discovered something slightly unexpected. If the locale you specify uses spaces for a thousands separator (e.g. French), then Adobe ColdFusion (no idea for Railo or BlueDragon), doesn’t just add a normal space, it uses a ‘hard’ space.

I wouldn’t have found this out at all if it wasn’t for the fact I was doing some unit tests with MXUnit at the time. My test case was something like this:

<cfset strPrice = lsCurrencyFormat("12345678.9", "none", "fr_FR")>
<cfset assertEquals("12 345 678,90",  strPrice)>

So initially that failed, giving me the useful message that
Expected ’12 345 678,90′ but received ’12 345 678,90′. These values should be the same.
Puzzling. I was doing this in Firefox; if I selected just the error message and then chose View Selection Source, I could see it actually said
Expected ’12 345 678,90′ but received ’12&nbsp;345&nbsp;678,90′.

Ahah, so I assumed it was inserting the ‘&nbsp;’ non-breaking space HTML entity into the value instead of just a normal single space. So I updated my unit test assertion to be like this:

<cfset assertEquals("12&nbsp;345&nbsp;678,90",  strPrice)>

But that also failed, and this time when checking the source, it said
Expected ’12&nbsp;345&nbsp;678,90′ but received ’12&nbsp;345&nbsp;678,90′.
Curiouser and curiouser!

So at this point, I decided to loop over the actual string returned by lsCurrencyFormat to examine it more closely:

<cfoutput>
	<cfset french = lsCurrencyFormat("12345678.90", "none", "fr_FR")>
	
	<cfloop index="i" from="1" to="#Len(french)#">
		<cfset char = mid(french, i, 1)>
		
		#char#:#Asc(char)#<br/>
	</cfloop>
</cfoutput>

Which produced output that looked like:

1:49
2:50
 :160
3:51
4:52
5:53
 :160
6:54
7:55
8:56
,:44
9:57
0:48

The normal HTML space is ASCII character 32. But in this case it’s inserting ASCII character 160, which the browser interprets as &nbsp;. So for my unit test to work, I’ll have to do something like:

<cfset assertEquals("12" & Chr(160) & "345" & Chr(160) & "678,90",  strPrice)>

Dunno why Adobe in their infinite wisdom (to be honest this probably dates back to Allaire or Macromedia) would use character 160 instead just a normal single space character 32. And I expect other locale-specific formatting functions must do the same thing.
I’m getting exactly the same results with java.text.DecimalFormatSymbols.getGroupingSeparator(), so I expect ColdFusion is using that functionality in the background.

I suppose it shouldn’t really cause any problems, apart from perhaps when you’re needing to unit test (or perhaps if parsing some HTML you’ve received by CFHTTP). Just, unexpected is all.

September 16, 2010

cfdump label attribute

Filed under: Coldfusion — duncan @ 9:06 pm
Tags: , ,

The <cfdump> tag in ColdFusion has an optional label attribute which doesn’t seem to be commonly used. Sometimes if you’re dumping out several items at the same time, and potentially from separate files, it can be handy to identify the source of each one.

However I just noticed that the label seems to get applied to all complex data structures within the dumped variable. For example this code:

<cfset a.b.x = {a='one', b='two', c='three'}>
<cfset a.b.y = [1,2,3]>
<cfset a.b.z = 7>
<cfdump var="#a#" label="my struct">

Produces this output:

No big deal, just something I’d noticed. I guess ColdFusion recursively applies the label to all structures as it dumps them out.

December 13, 2009

Project Euler problem 58

Filed under: Coldfusion,Project Euler — duncan @ 9:00 am
Tags: , , ,

Problem 58:

Starting with 1 and spiralling anticlockwise in the following way, a square spiral with side length 7 is formed.

37 36 35 34 33 32 31
38 17 16 15 14 13 30
39 18  5  4  3 12 29
40 19  6  1  2 11 28
41 20  7  8  9 10 27
42 21 22 23 24 25 26
43 44 45 46 47 48 49

It is interesting to note that the odd squares lie along the bottom right diagonal, but what is more interesting is that 8 out of the 13 numbers lying along both diagonals are prime; that is, a ratio of 8/13 ≈ 62%.

If one complete new layer is wrapped around the spiral above, a square spiral with side length 9 will be formed. If this process is continued, what is the side length of the square spiral for which the ratio of primes along both diagonals first falls below 10%?

This problem has some similarities to problem 28. In that case the spiral went clockwise; this one it goes anti-clockwise. For our purposes that’s a red herring.

We want to loop round, calculating the value for the corners of our spiral.
For each value, work out if it is prime (re-using the IsPrime function I first used back in problem 3).
Keep count of how many primes there are.
Also keep track of how long the side of our spiral is.
At each loop round the spiral, check if the ratio of primes / length is less than 10%.

<cfset numPrimes = 0>
<cfset length = 1>
<cfset number = 1>
<cfset increment = 2>
<cfset ratio = 100>

<cfloop condition="ratio GT 10">
	<cfset length += 2>

	<!--- add our increment 4 times, calculating each time if it's prime --->
	<cfloop index="i" from="1" to="4">
		<cfset number += increment>

		<!--- the 4th corner is never prime --->
		<cfif i LT 4 AND IsPrime(number)>
			<cfset numPrimes++>
		</cfif>
	</cfloop>

	<cfset ratio = numPrimes / ((2*length)-1) * 100>

	<cfset increment += 2>
</cfloop>

<cfoutput>#length#</cfoutput>

<cfscript>
function isPrime(x)
{
	var isPrime = true;
	var i = 0; 

	if (x LT 2)
	{
		return false;
	}

	if ((NOT x MOD 2) AND (x GT 2))
	{	// a multiple of 2, but not 2 itself
		return false;
	}

	for (i = 3; i LTE SQR(x); i = i + 2)
	{
		if (NOT x MOD i)
		{	// found a factor of x
			return false;
		}
	}

	return isPrime;
}
</cfscript>

Like in problem 28, we can see the corner values go:
3, 5, 7, 9, [increment: 2]
13, 17, 21, 25, [increment: 4]
31, 37, 43, 49 [increment: 6]
etc.

So we can work out the corner values by starting at 1, adding 2 to our increment each time round the spiral, then adding the increment four times to calculate the corners.

We don’t need to work out if the 4th corner is prime; it will always be a square number. e.g. on spiral with length 3, the 4th corner is 32 = 9. On length 5, the 4th corner is 52 = 25, and so on. So the if statement <cfif i LT 4 AND IsPrime(number)> will only execute the IsPrime() function if i LT 4 evaluates to true.

August 18, 2009

Coldfusion and XML – an introduction

Filed under: Coldfusion — duncan @ 9:21 pm
Tags: , , ,

I was doing a bit of investigation into XML in ColdFusion. This is pretty simple stuff, but I didn’t really see any good information about this on the web, so thought it would be worth blogging, as much for my future reference as anyone else’s.

<CFSaveContent> versus <CFXML>

There are various ways of creating XML in ColdFusion. The two simplest ways are cfsavecontent and cfxml.
Method 1 – CFSaveContent:

<cfsavecontent variable="myXML_1">
<country>
	<name>United Kingdom</name>
	<population>61,113,205</population>
	<capital>London</capital>
</country>
</cfsavecontent>

Method 2 – CFXML:

<cfxml variable="myXML_2">
<country>
	<name>United States</name>
	<population>307,212,123</population>
	<capital>Washington, DC</capital>
</country>
</cfxml>

Look almost identical, right? There’s a difference though. CFSaveContent returns a string. CFXML returns a ColdFusion XML object. What’s the difference? Firstly, try outputting both variables.

Variable 1:

<cfoutput>
#HTMLEditFormat(myXML_1)#
</cfoutput>

<country> <name>United Kingdom</name> <population>61,113,205</population> <capital>London</capital> </country>

Variable 2:

<cfoutput>
#HTMLEditFormat(myXML_2)#
</cfoutput>

<?xml version=”1.0″ encoding=”UTF-8″?> <country> <name>United States</name> <population>307,212,123</population> <capital>Washington, DC</capital> </country>

As you can see, the CFXML example has got an XML declaration prepended onto it ( <?xml version=”1.0″ encoding=”UTF-8″?> ). If you were using CFSaveContent you’d usually have to add this in yourself. But that’s not the only difference. This time try dumping out the variables:

<cfdump var="#myXML_1#">

<country> <name>United Kingdom</name> <population>61,113,205</population> <capital>London</capital> </country>

Dumping the first variable is identical to outputting it, because all you have is a string.

<cfdump var="#myXML_2#">

CFDump of CFXML object

Dumping out the variable created with CFXML displays a bit like a structure. [If you click the part that says "xml document" it changes to the 'long version'. Clicking it again hides the cfdump content.]

So we’ve established the difference, but what advantage does one method have over the other? Because the first variable is just a string, you can put anything you like in there, including invalid XML. However if you try doing that with CFXML, it’ll throw an error (which is a good thing, as you don’t want to accidentally be using something invalid, right?)

XMLParse()

Any time you’re working with XML in ColdFusion, you’ll probably come across the XMLParse function. This takes a block of XML and turns it into an XML object. If you used CFSaveContent, then passed it into XMLParse, you’d get back an object identical to what you’d get if you’d just used CFXML.

You can also pass in an XML object to XMLParse, and it won’t change it. In other words, regardless of if your variable came from CFSaveContent or CFXML, calling XMLParse on it will return the same object.

If you’re getting some XML from anywhere other than CFXML, you probably always want to call XMLParse() on it to turn it into an XML object. If the XML is invalid, it’ll throw an error. You can also pass in a path to a DTD or XML Schema to validate it further.

Functions

When you declare a function in ColdFusion, you can specify an optional returntype attribute. This determines what type of variable is returned from your function (or ‘void‘ if nothing is returned).

When your function is returning XML, you can set returntype=”string” or returntype=”XML” (or returntype=”any”, but that’s a whole other discussion).
If your function is using XMLParse or CFXML to create an XML object, it doesn’t matter if you specify string or XML, it will always return as an XML object.
If you’re using CFSaveContent to create your XML as a string, you can still specify either string or XML, and it will return your XML as a string.
So in other words, regardless of which returntype you use, the variable that’s returned is of a type that matches how it’s created (a string if using CFSaveContent; an XML object if using CFXML).

However if you specify returntype=”XML”, it has to be valid XML, or it will throw an error.

If your function is returning XML to anything other than ColdFusion, e.g. as a webservice, then you want to return it as a string, not an XML object. I’d recommend still using returntype=”XML”, and CFXML to create it (therefore forcing you to create valid XML), but then call ToString() on it when returning.

You probably also want to specify output=”false” (which is good practice most of the time anyway) to avoid getting any extra whitespace appearing at the start of your XML object.

Arguments

Similarly, if you are passing in some XML as a function argument, you can specify type=”string” or type=”XML”. Again, regardless of whether your XML came from CFSaveContent (or elsewhere) as a string, or CFXML / XMLParse() as an XML object, either type=”string” or type=”XML” will be fine. However if you specify type=”XML”, the argument must be valid XML.

If you specify type=”XML”, but it’s an optional argument, I’d recommend doing it like so:

<cfargument name="myXML" type="XML" required="false">

Typically you often see in code default=”" on non-required cfarguments. However it’s not best practice, and if using type=”XML” will thrown an error. If you really need to specify the default attribute, put in a valid bit of XML, e.g.

<cfargument name="myXML" type="XML" required="false" default="<foo>bar</foo>">

Summary

  • Use CFXML instead of CFSaveContent; you won’t have to specify an XML declaration, and it will only work with a valid XML packet
  • CFSaveContent is just a string, so it doesn’t have to be valid XML
  • Use type=”xml” on the argument, even if the value you pass in is a string. It will only accept a valid XML packet
  • If the value could come from anywhere other than CFXML, use XMLParse() to turn what will be a string into a ColdFusion XML object
  • You can use XMLParse() even on a valid XML object (it won’t change it)
  • Even with returntype=”string” if you use CFXML it will return a ColdFusion XML object
  • If you might be returning data to something other than ColdFusion, you would need to turn it into a string first using ToString()

April 15, 2009

CFSchedule bug

Filed under: Coldfusion — duncan @ 8:06 pm
Tags: , , , , ,

In a follow-up to yesterday’s post about a bug with scheduled tasks in ColdFusion Administrator, I decided to see if the same bug existed if you used the <cfschedule> tag.

And I can confirm it does, at least on Adobe ColdFusion 8 developer edition.

This code replicates it. Setting a one-time only scheduled task with a start date/time in the past. The task runs immediately.

<cfschedule 
	action="UPDATE"
	task="TestTask"
	operation="HTTPRequest"
	startDate="#CreateDate(2009, 04, 01)#"
	startTime="21:00"
	url="http://127.0.0.1:8500/TestTask.cfm"
	interval="once">

And similar to the problem with scheduled tasks in CF Administrator; if you create a one-time scheduled task to run in the future, then update it to be in the past, it will run immediately.

I’ve reported this as a separate bug.

Next Page »

Theme: Rubric. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.