Recursion in Practice


First we streamline the shownode function. (This function is independent of the recursion but part of tightening up the code.) The function is renamed trace(). The flabby generic node printer is replaced by specialized single-line traces for element, text node, and attribute.

But the real action happens after the node data is printed on the screen. Trace scans through the array of child nodes and installs itself in each as the . trace() method ”which it immediately invokes. (Alternatively, we could have added trace() to XML.prototype .)

Of course, this method attaches itself to the next generation and its children and propagates through the entire XML object , displaying itself no matter how broad or how deep the XML object is.

ActionScript
 xml.trace = function() {   if(this.nodeType==1)  trace(" [Element]  "+ this.nodeName);   if(this.nodeType==3)  trace("      text  "+ this.nodeValue);   for(name in this.attributes)                     trace("attr>"+ name+":  "+this.attributes[name]);   for(var i=0; i<this.childNodes.length; i++){     this.childNodes[i].trace    = this.trace;     this.childNodes[i].trace();     } } 

And here is something very beautiful. All that odious onLoad code, with its childNodes of childNodes of childNodes , has disappeared. In fact, except for a check of the success flag (demonstrated later), the onLoad functionality has disappeared into the trace function. And this gives us not only a neater (and far more flexible) code base but one that is completely free of the arbitrary limits imposed by the hardcoded method. Note that the trace function, after tracing the appropriate type, value, and attributes, merely traces all the subsequent nodes. These in turn trace the appropriate type, value, and attributes and then trace their children. This recursive approach allows us to move through infinite levels of complexity while keeping the code neat and clean.

ActionScript
 xml.onLoad = function (ok) {    if(ok) this.trace();    else trace ("load failure"); } 

Indentation

It is inevitable (but unnecessary) that we want to pretty up this printout, now that it is so smart. An easy way to do so is to install an indentation string at each node. Each time trace installs itself into a new generation, it concatenates some whitespace onto the current indentation string. (This solution is not the most memory-conserving design.)

ActionScript
 xml.trace = function() {   if(this.nodeType==1)       trace(this.indent + " [Element]  " . . .   if(this.nodeType==3)       trace(this.indent + " text)   " +  . . .   for(name in this.attributes) trace(this.indent + "   +-attr-->" . . .   for(var i=0; i<this.childNodes.length; i++){     this.childNodes[i].indent     = "   " + this.indent;     this.childNodes[i].trace   = this.trace;     this.childNodes[i].trace();     } }  [d] 

Filtering Blank Nodes: Three Strategies

As we use this on a number of XML files we notice a problem. Many editors produce files with extraneous whitespace: line breaks, indentation, and visual spacing meant to format the XML for the human eye. This meaningless space seems to be outside tags and is thought to be as invisible to the XML processor as similar whitespace is to the processor of C or JavaScript. Or ActionScript or HTML, for that matter.

In modern ActionScript, this problem can be avoided by setting the XML ignoreWhite property to true . But some of the old ActionScript parsers properly render these as annoying text nodes containing only whitespace. Although this was a technically correct way to handle whitespace in XML, it was useless and annoying. Several ways of filtering out the whitespace nodes were invented. Three are included, as these approaches are valuable instructive tools.

This will be our first step away from just representing the actual received data structure and instead trying to manipulate it.

O UTPUT F ILTERS

First we make a boolean function that identifies nodes that we consider blanks. A blank node has no printable characters (ASCII value greater than 0x20(32)).

ActionScript
 xml.isBlank= function () {    for(var i=0; i<this.nodeValue.length; i++)         if(this.nodeValue.charCodeAt(i)>32) return false;    return true; } 

Using this function we do not trace out text nodes that are blank.

ActionScript
 xml.trace = function() {   if(this.nodeType==1)                     trace(this. . .   if(this.nodeType==3 && !this.isBlank())   trace(this. . .   for(name in this.attributes)               trace(this. . .   for(var i=0; i<this.childNodes.length; i++){     this.childNodes[i].isBlank = this.isBlank;     this.childNodes[i].ind     = "   " + this.ind;     this.childNodes[i].trace   = this.trace;     this.childNodes[i].trace();     } } 

This makes for a lovely output where structure is indicated quite clearly.

Output
 [Element]  null     [Element]  Quiz        [Element]  Name           text)   Dov's quiz        [Element]  Question           [Element]  Q              text)   The Prime Meridian intersects which of these?           [Element]  A              text)   Greenwich,  Connecticut 

However, it is disquieting. The problem of meaningless whitespace nodes is real. But this solution violates our principal of honestly visualizing the Flash data structure of our XML. In fact, what we are doing here is arguably the opposite . We are diagramming what we wish the data structure were, rather than what it really is. It is inevitable that this ostrich reflex will bring us sooner or later into a painful blindside collision with reality.

It is more honest to show exactly the in-memory image and to fix what is in memory. By far the optimum solution is to use clean files. The problem goes away if we are in a position to insure that our files will not have been formatted for human eyes with whitespace. But that is not possible in many environments. We may not own the data: it might be legacy or shared data or data being actively published by a third party. Or the tools used to generate and maintain the XML might not allow us to create pure files.

C HARACTER F ILTERS

Early encounters with the clean file issue led to clever solutions that cleaned the code. A newsgroup posting signed only G.W., for instance, used xml.toString() to generate a new document in memory. Using (actually abusing ) String.split() , he filtered out linefeeds, returns, and tabs, then created new XML structure with XML.parse() . This method solved the problem, but it is tediously slow and dangerously inacccurate. It has failures of both commission and omission. As a simple filter for three characters, it commits the error of removing the characters when they appear (with perfect legitmacy) inside text blocks:

XML
 <?xml version="1.0"?><haiku ><content>Caprices of spring--     sunglasses and umbrella     both in my suitcase. </content > <author>Walter Vereertbrugghen</author></haiku> 

Additionally, it omits a solution for the problematic space character essential to almost all text but also frequently used for indentation.

N ODE F ILTERS

The problem is not bad characters, but bad nodes. Our solution is to scan through the XML in memory and delete each artifact node. Our definition of an artifact node is a node that is a leaf node of text type that contains no printable characters.

This approach is still imperfect ” it assumes an ASCII-like alphabet and it ignores the possibility of intended empty nodes. But it is fast, neat, and honest.

ActionScript
 xml.cleanse= function () {     if(this.nodeType == 3){       for(var i=0; i<this.nodeValue.length; i++)         if(this.nodeValue.charCodeAt(i)>32) return;         this.removeNode();        }     else {       for(var i=this.childNodes.length; i--;){          this.childNodes[i].cleanse = this.cleanse;          this.childNodes[i].cleanse();          }        } } 

If it is a text node, it scans the text, quitting as soon as it encounters a printable character (anything higher than 32, the column of the ASCII table that divides the control characters from the printables). If it is not text, we scan backward through the array of children. The backward scan is essential, since this is a destructive operation. It is safe to chop away at the tail if we no longer need that index. We are in the position of a tree surgeon who is sitting on the same limb he is cutting. It is safe enough as long as we stay on the right side of the cut.

Note the postdecrement test ( i- ), which insures that we do not miss the zeroth member of the array.

C ONCLUSION

We use the node filter throughout this book. Although it would be easy to merely set the ignoreWhite property to true, doing so would cause problems if any of our viewers were using a version of the FlashPlayer earlier than version 5, build 4.1.



Flash and XML[c] A Developer[ap]s Guide
Flash and XML[c] A Developer[ap]s Guide
ISBN: 201729202
EAN: N/A
Year: 2005
Pages: 160

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net