ColdFusion 10 Backport: ArraySlice & GetApplicationMetadata

Another two functions from ColdFusion 10 recreated for CF8+ (7 if you’re lucky). Since it’s a Friday I’ve picked two easy ones: ArraySlice and GetApplicationMetadata. If you haven’t spotted GetApplicationMetadata, you’ve probably only looked at the CF10 docs “New Functions in ColdFusion 10”, as it’s currently missing off that list but not Charlie Arehart’s massive list.

ArraySlice

This is an easy one to explain, it simply returned a specified “slice” of an array. You provide it an array, a starting offset and optionally a length. To recreate the function, as with most of my efforts, it’s best to take a look at what a ColdFusion array actually is.

Looking through those methods (a lot of them being familiar functions), nothing looks that useful for slicing arrays. Time to check the the parent class of java.lang.Vector.

Result! A method called subList) which accepts two integers for parameters. Reading the documentation it starts to feel like it’s not exactly what we’re after. The method returns a “view” of the original array where anything you change is mirrored in the original array. Also, after trying the method out it seems that ColdFusion doesn’t like the returned value (with a class of java.util.Collections$SynchronizedRandomAccessList) being used as an array, so it throws an error. All is not lost! Take a look at the java.util.Vector class again and you’ll see a method called addAll) that accepts a parameter of the class java.util.Collections. To take advantage of this we simply need to create a new ColdFusion array and then use the .addAll() method to add our slice. Note that if you went ahead and created an object of java.util.Vector you might inadvertently cause that array to be passed by reference (Ben Nadel strikes again), instead of the usual CF behaviour of value for arrays.

ar = ListToArray("c,o,l,d,f,u,s,i,o,n");
sub = ar.subList(4, 8);
// sub contains [f,u,s,i]
slice = ArrayNew(1);
slice.addAll(sub);

Above is an example of slicing the array and turning the slice back into a ColdFusion array type. Note the usage of subList however, since you’re using a Java method you have to remember that their indexes start at 0 instead of 1. Plus the parameters of subList don’t match those of ArraySlice, start (inclusive, index starts 0) and end (exclusive) instead of CF10’s start (inclusive, index starts 1) and length (optional, otherwise to the end). Which means we still have a little work to do when wrapping it into our ArraySlice backport.

var lc = StructNew();
if (Not StructKeyExists(arguments, "length")) {
  lc.from = arguments.offset - 1;
  arguments.length = ArrayLen(arguments.array) - lc.from;
} else if (arguments.offset Lt 0) {
  lc.from = ArrayLen(arguments.array) + arguments.offset;
} else {
  lc.from = arguments.offset - 1;
}
lc.to = lc.from + arguments.length;
// subList(from [inclusive], to [exclusive]), start index is 0
lc.slice = arguments.array.subList(lc.from, lc.to);
// Slice is the wrong type java.util.Collections$SynchronizedRandomAccessList#
// Recreate as a normal CF array
lc.array = ArrayNew(1);
lc.array.addAll(lc.slice);
return lc.array;

As you can see, there’s a little bit of working out done to turn thelength argument into end one and handle a negative offset. Ideally I should probably throw some error catching in there, like offsets or lengths higher than the actual array length but the basic function is there.

GetApplicationMetadata

This is one of the easy ones. To find what we’re looking for, take a peak inside the application scope class.

See that method getApplicationSettings()? That’s our target so let’s get a look at what that returns.

Nice, looks like what we’re after… almost. An important step is to Duplicate the returned structure, as during my testing it was a reference. This meant that messing around with the returned data was changing what was returned the next time I called that method.

Taking a look at the output from ColdFusion 10’s getApplicationMetadata() we see that they’re not returning the on* functions. We’ll need to filter those out. Also, in CF10, the .scriptProtect key has its values as an array, instead of a list.

settings = Duplicate(application.getApplicationSettings());
for (key in settings) {
  if (IsCustomFunction(settings[key])) {
    StructDelete(settings, key);
  }
}
if (StructKeyExists(settings, "scriptProtect") And Len(settings.scriptProtect)) {
  settings.scriptProtect = ListToArray(UCase(settings.scriptProtect));
}

That looks a lot more like it now. Just the final step of wrapping it into a function.

var lc = StructNew();
if (IsDefined("application")) {
  lc.settings = Duplicate(application.getApplicationSettings());
  for (lc.key in lc.settings) {
    if (IsCustomFunction(lc.settings[lc.key])) {
      StructDelete(lc.settings, lc.key);
    }
  }
  if (StructKeyExists(lc.settings, "scriptProtect") And Len(lc.settings.scriptProtect)) {
    lc.settings.scriptProtect = ListToArray(UCase(lc.settings.scriptProtect));
  }
  return lc.settings;
}
return StructNew();

CFBackport on Github

These functions have been thrown into the CF10.cfm template in my CFBackport project on Github, along with all the others I’ve recreated from CF10 so far. I’d really welcome any feedback, additions or suggestions.


Comments


David Boyer
David Boyer

Full-stack web developer from Cardiff, Wales. With a love for JavaScript, especially from within Node.js.

More...