Saturday, 18 May 2013

Another Railo feature for developers: linked structs

G'day:
This extends from my article about a Railo enhancement to replace(), yesterday.

One of the things I observed about the new replace() feature is that given it takes a struct to map its replace-tokens with its replace values, one cannot guarantee which order the replacements are made. This is because structs have no sense of order in their keys, so one ought not infer one or write any code which depends on a sense of ordering in the struct. Here's an example of what I mean:


eg1 = {
    initial = "one",
    second    = "two",
    last    = "three"
};

for (key in eg1){
    writeOutput(key & "<br>");
}
writeOutput("<hr>");

eg2 = {
    one = "one",
    two    = "two",
    three    = "three"
};

for (key in eg2){
    writeOutput(key & "<br>");
}

This results in:

last
second
initial

two
one
three

Note that the key-ordering that comes out when looping is not the same as the order they went in. Also note if one uses <cfdump>, then one gets a false sense of ordering, because - internally - <cfdump> alphabetises the keys:


Array
1
Struct
initial
stringone
last
stringthree
second
stringtwo
2
Struct
one
stringone
three
stringthree
two
stringtwo

So - in theory - one can't pass a struct with a set of tokens that need to be processed in sequence to replace() because the sequence won't necessarily be respected. I say "in theory", because as far as I can tell Railo magically does remember the sequence keys were added to a struct when said struct is used in a call to replace(). Here's a tweaked version of that code:

eg1 = {
    initial = "second",
    second    = "last",
    last    = "final"
};
for (key in eg1){
    writeOutput(key & "<br>");
}

original = "initial";
result = replaceNoCase(original, eg1);
writeDump([original, result]);

writeOutput("<hr>");

eg2 = {
    one = "two",
    two    = "three",
    three    = "final"
};
for (key in eg2){
    writeOutput(key & "<br>");
}

original = "one";
result = replaceNoCase(original, eg2);
writeDump([original, result]);

Output:

last
second
initial
Array
1
stringinitial
2
stringfinal

two
one
three
Array
1
stringone
2
stringfinal

What flummoxes me slightly is that the loop over the keys outputs them in one order, however the replace() operation uses them in a different order (and that order is that which they were created in the struct). This is convenient, but it ought to be nothing more than coincidence. Especially for what I am trying to write about in this article...

So, yeah... um... the ordering of keys in a struct is not guaranteed, which can be inconvenient at times. Railo solves this by having different types of structs that one can use, one of which maintains key ordering.

In Railo one can do this:

normal = structNew("normal");
linked = structNew("linked");
weak = structNew("weak");

Note there are three types of struct one can create. The "normal" one we know about, but "linked" and "weak" are explained as follows:

structNew("linked")
The order of the keys in the struct is guaranteed.
see also:
http://java.sun.com/j2se/1.4.2/docs/api/java/util/LinkedHashMap.html

structNew("weak")
The content of the might be flushed by the garbage collector. Weak
structures can perfectly be used as caches. In case of a memory
emergency the JRE garbage collector removes elements of weak structures
with a higher priority.
see also:
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/ref/WeakReference.html

(copied from the Railo Google Group... I cannot find the actual documentation for these).

So that's quite cool. I'm just focusing on the "linked" option here (maybe I'll do the "weak" one in a different article).

Have a look at the difference between a "normal" and "linked" structs:

writeOutput("<h2>Normal</h2>");
normal = structNew("normal");
normal.initial    = "one";
normal.second    = "two";
normal.last        = "three";
for (key in normal){
    writeOutput(key & "<br>");
}
dump([
{dump=normal},
{keyarray=structKeyArray(normal)}
]);

writeOutput("<hr>");

writeOutput("<h2>Linked</h2>");
linked = structNew("linked");
linked.initial    = "one";
linked.second    = "two";
linked.last        = "three";
for (key in linked){
    writeOutput(key & "<br>");
}
dump([
{dump=linked},
{keyarray=structKeyArray(linked)}
]);

writeOutput("<hr>");

This results in:

Normal

LAST
SECOND
INITIAL
Array
1
Struct
dump
Struct
INITIAL
stringone
LAST
stringthree
SECOND
stringtwo
2
Struct
keyarray
Array
1
stringLAST
2
stringSECOND
3
stringINITIAL

Linked

INITIAL
SECOND
LAST
Array
1
Struct
dump
Struct
INITIAL
stringone
LAST
stringthree
SECOND
stringtwo
2
Struct
keyarray
Array
1
stringINITIAL
2
stringSECOND
3
stringLAST


Note how in the "linked" example, the key-order is maintained. This is great!

One interesting thing though, look at this:

original = structNew();
normal = structNew("normal");
linked = structNew("linked");
weak = structNew("weak");
literal = {};
dump([
    {original=original.getClass().getName()},    
    {normal=normal.getClass().getName()},    
    {linked=linked.getClass().getName()},    
    {weak=weak.getClass().getName()},    
    {literal=literal.getClass().getName()}    
]);

Output:

Array
1
Struct
original
stringrailo.runtime.type.StructImpl
2
Struct
normal
stringrailo.runtime.type.StructImpl
3
Struct
linked
stringrailo.runtime.type.StructImpl
4
Struct
weak
stringrailo.runtime.type.StructImpl
5
Struct
literal
stringrailo.runtime.type.StructImpl

I was kinda expecting them to be different types. I guess the difference is hidden away within the instance of StructImpl, which makes sense now that I think about it.

One last thing that I noticed when plugging a linked struct through ClassViewer, I noticed this:

 /*** FIELDS ***/
public static final int TYPE_WEAKED
public static final int TYPE_LINKED
public static final int TYPE_SYNC
public static final int TYPE_REGULAR
public static final int TYPE_SOFT


So it seems there a coupla other types of struct one can use too: sync and soft. Google can't find any docs for these, but I'll hunt some out and have a look at them later.

That's it. I oughta be paying attention to the presentation I'm sitting in.

--
Adam