Checking To See If A Struct Is Of Type Ordered / Linked In Lucee CFML 5.3.6.61
One of the really excited features of ColdFusion is that it can create linked / ordered Structs. These are Structs (objects, hashes, maps) in which the key-iteration order matches the order in which the keys were originally defined. As I discussed last year, ordered Structs are perfect for MongoDB interactions, where the order of the key-iteration determines the underlying database interaction behaviors. The other day, I was building a Gateway wrapper to a MongoDB database; and, due to the importance of the Struct implementation, I wanted to see if I could, perhaps, require the MongoDB query documents to be Ordered / Linked Structs. ColdFusion doesn't provide decision functions around Struct type; so, I wanted to see how I might determine if a Struct is of type Ordered / Linked in Lucee CFML 5.3.6.61.
CAUTION: I am using undocumented, implementation details here - but, I am encapsulating them within Function boundaries.
To figure out a plan of attack, I started looking around in the Lucee CFML source code for the StructImpl
class. I was hoping that each "type" of Struct was actually a different class; and then, maybe, I could just use the isInstanceOf()
ColdFusion function. But, it looks like the StructImpl
class just wraps a map
of a given Type. That said, it does have a .getType()
method, which exposes the Type of said underlying map
. So, I ended up using the .getType()
method to determine what type of Struct I had:
<cfscript>
normal = {};
linked = [:];
weak = structNew( "weak" );
// As a test case, let's try a value that isn't a Struct at all.
none = "hello";
echo( "<strong> Normal : </strong>" );
echo( "#isNormalStruct( normal )# , #isLinkedStruct( normal )# , #isWeakStruct( normal )#" );
echo( "<br />" );
echo( "<strong> Linked : </strong>" );
echo( "#isNormalStruct( linked )# , #isLinkedStruct( linked )# , #isWeakStruct( linked )#" );
echo( "<br />" );
echo( "<strong> Weak : </strong>" );
echo( "#isNormalStruct( weak )# , #isLinkedStruct( weak )# , #isWeakStruct( weak )#" );
echo( "<br />" );
echo( "<strong> None : </strong>" );
echo( "#isNormalStruct( none )# , #isLinkedStruct( none )# , #isWeakStruct( none )#" );
echo( "<br />" );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
/**
* I determine if the given value is a "normal" Struct.
*
* @value I am the value being inspected.
*/
public boolean function isNormalStruct( required any value ) {
return(
isStructOfType( value, "TYPE_UNDEFINED" ) ||
isStructOfType( value, "TYPE_REGULAR" )
);
}
/**
* I determine if the given value is a "linked" or "ordered" Struct.
*
* @value I am the value being inspected.
*/
public boolean function isLinkedStruct( required any value ) {
return( isStructOfType( value, "TYPE_LINKED" ) );
}
/**
* I determine if the given value is a "weak" Struct.
*
* @value I am the value being inspected.
*/
public boolean function isWeakStruct( required any value ) {
return( isStructOfType( value, "TYPE_WEAKED" ) );
}
/**
* I determine if the given value is a Struct of the given Type.
*
* @value I am the value being inspected.
* @valueType I am the name of the INTERNAL TYPE being tested.
*/
public boolean function isStructOfType(
required any value,
required string valueType
) {
// Since we're about to dip into the INTERNAL IMPLEMENTATION details of the
// Lucee CFML Struct object, let's try to short-circuit the check to avoid any
// invalid object references.
if (
! isStruct( value ) ||
! isInstanceOf( value, "lucee.runtime.type.StructImpl" )
) {
return( false );
}
// At this point, we know that we're dealing with a "StructImpl" instance. As
// such, we should be able to access its Type, which is an enumerated value.
var StructTypes = createObject( "java", "lucee.runtime.type.Struct" );
switch ( valueType ) {
case "TYPE_UNDEFINED": // -1
case "TYPE_WEAKED": // 0
case "TYPE_LINKED": // 1
case "TYPE_SYNC": // 2
case "TYPE_REGULAR": // 3
case "TYPE_SOFT": // 4
return( value.getType() == StructTypes[ valueType ] );
break;
default:
return( false );
break;
}
}
</cfscript>
As you can see, I am defining three high-level functions:
isNormalStruct( value )
isLinkedStruct( value )
isWeakStruct( value )
But, each of these just turns around and defers to a low-level method, isStructOfType()
, which, in turn, dips into the underlying Java implementation details. The low-level part is the part that might break when Lucee CFML changes its implementation. But, given that it is wrapped up in a Function, it gives us a place to change it later on.
That said, when we run this ColdFusion code, we get the following output:
As you can see, by using our custom User Defined Functions (UDFs), we are able to take a ColdFusion Struct and determine which type it is. I could - theoretically - now take these decision functions and build a MongoDB gateway component that requires linked / ordered Structs to be used to define query document specifications in Lucee CFML 5.3.6.61.
Want to use code from this post? Check out the license.
Reader Comments
I was determining whether an Adobe ColdFusion struct was ordered by examining the canonical Name.
canonicalName = getMetaData(myStruct).getCanonicalName();
For structs, It would return one of the following:
If using a LinkedHashMap, it would return:
@James,
Oh nice, that's good to know for ACF. And actually, when I started this post, I was assuming that Lucee CFML was going to work the same way. But, it turned out that the low-level Struct was actually a property of the "Struct Implementation", so I had to dip down into the
.getType()
method. But, nice to see this can be achieved in both runtimes.