May 23, 2010

Static Const of the reference not the content

In the middle of helping a co-worker debug a unit test, we had a test method that ran fine by itself but failed when run with the rest of the tests in the same class. Looking over the code there was no obvious reasons why when both test methods were run the second one failed.

Turned out to be a duh moment. At the top of the unit test there were a bunch of XML fragments declared with private static const. Problem was one of the two test methods was modifying the children of the XML. XML is a complex Object, unlike an int or a String, so the constant keyword doesn't prevent changes to the structure within the XML, it just prevents the reference from changing. A couple of simple fixes:

  1. Make the XML fragments be class instance variables, since each unit test creates a new instance of the test class
  2. Before using the XML use its xml.copy() function

Tags: e4x flex testing xml

April 30, 2006

Flex 2, Actionscript 3, and E4X

I've found a feature of Actionscript 3 (AS3) that I've not seen in other languages that I think is pretty cool. It's taken me awhile to come to terms with it, but now that I've getting more comfortable with it I'm really starting to appreciate it. That feature is what is known as E4X or more officially Standard ECMA-357 ECMAScript for XML (E4X) Specification. At its core is the ability to work with and manipulate XML data in AS3 using standard language syntax instead of having to do everything through an API.

For example to create a variable that contains some hard coded XML you could just write:


var xml:XML =
<componentPackage>
<component id="MyButton" class="package1.MyButton"/>
<component id="MyOtherButton" class="package2.MyOtherButton"/>
</componentPackage>;

Notice the lack of any "new XML("")" or other traditional methods one might use to create an XML document. Pretty cool stuff. You can also do variable substitution when creating XML:


var attributeName:String = "class";
var xml:XML =
<componentPackage>
<component id="MyButton" {attributeName}="package1.MyButton"/>
<component id="MyOtherButton" {attributeName}="package2.MyOtherButton"/>
</componentPackage>;

That creates an XML document that looks exactly like the first XML example. Not the most convincing example, but you get the idea. You can substitute element names, attribute names, or whole attribute values. Besides being able to do inline XML document creation you can also do DOM style navigation to get at elements and attributes within the XML (these return XML or XMLList objects). Going back to the first XML example:


xml.component[0]; // <component id="MyButton" class="com.example.ui.MyButton"/>
xml.component[0].@id; // MyButton

Where things get really cool is the XPath like operations, technically called filters since it isn't as expressive as XPath:


xml.component.(@id == "MyButton"); // <component id="MyButton" class="com.example.ui.MyButton"/>

That searches for all component elements under the root that have an id of MyButton. There are a bunch more. In fact the Working with XML (doc link subject to change) section of the Flex 2 documentation has many good examples. It's at this point that I want to offer a hard learned lesson about what doesn't work with E4X (at present anyway, I can only hope that it will be solved in a future Flex 2 release).

The following line of code doesn't compile (again using the first XML example from above):


xml.component[0].@class; // Error: Expecting identifier before class

The reason is that "class" is a reserved keyword. Thankfully E4X provides alternative attribute access syntax, an example is:


xml.component[0].@["class"]; // package1.MyButton

A little clunky, but still reasonable. What I haven't been able to get working is any of the alternative syntaxes when trying to do a filter using the same attribute:


xml.component[0].(@class == "package1.MyButton"); // Error: Expecting identifier before class

I've tried all four listed variations: element.@attrName, element.attribute("attrName"), element["@attrName"], and element.@["attrName"]. None of them work in the above example, adjusted of course to account for the () filtering operation. This afternoon I finally stumbled upon a solution. This is even more clunky then the alternative attribute accessors, but it does eliminate the need to write custom loops to handle filters like the one above:


xml.component.(@\u0063lass == "package1.MyButton"); // <component id="MyButton" class="com.example.ui.MyButton"/>

In this case I'm using the Unicode value for the letter c (0063) which to my surprise worked. As I said earlier I hope this keyword issue is just a beta issue and it will be resolved. If not, consider using this approach to construct E4X filters that might reference AS3 keywords.

Tags: actionscript as3 e4x flex programmin xml