sorting XML into an Object OOP and finding reference loops
Posted: Thu Nov 11, 2010 5:42 pm
So, XML is a great way to store simple data, and get it into a program. That being said, it is also really hard to read, parse, unless you are up on your regular expressions, looping with endless loop bugs, etc. Letting AS2 and the much better AS3 is a great idea. But, if you ever wanted to understand how to parse something in XML with AS2 and the lost, missing, warped, incomplete tutorials have you hanging, here is my simple import engine I wrote. A SIMPLE version not meant for heavy use, just a few branches on a few hundred nodes is about what it can handle.
Scripting in AS2 is a hassle for sorting and node ordering because of it's limit of 256 loop branches or jumps per 'frame' of 'video' (fps). So, to get around that I just wrote a callback that freezes after counting how many branchings happened. I modulated the number until it always worked and it isn't perfect or incredibly fast like a compiled code module would be in C++ but it works, and you can see the inner workings of XML to an Object Model Node.
http://www.supercala.net/tuts/xml_oop/gallery.zip
Here is the code for now:
loadXML2.as
XML Loader function
the actual module sitting simply in a MC [and the needed *.as files]
All as a zip
Scripting in AS2 is a hassle for sorting and node ordering because of it's limit of 256 loop branches or jumps per 'frame' of 'video' (fps). So, to get around that I just wrote a callback that freezes after counting how many branchings happened. I modulated the number until it always worked and it isn't perfect or incredibly fast like a compiled code module would be in C++ but it works, and you can see the inner workings of XML to an Object Model Node.
http://www.supercala.net/tuts/xml_oop/gallery.zip
Here is the code for now:
loadXML2.as
XML Loader function
Code: Select all
//loadXML2.as by kristoffe brodeur
//v2.0 01-11-2007
//----------
function loadXML(lX_targetObj)
{
trace("[loadXML]("+lX_targetObj.filename+")");
lX_targetObj.onLoad=function(success)
{
//success is a binary function; positive on a good load
if(success)
{
tF_debug.text+="XML loaded.";
//this refers to the onLoad parent, dataXML, which is the actual XML data
//therefore, onLoad is just an attribute and fucntion of the main object dataXML
//tF_debug.text+=newline+"data:"+lX_targetObj;
lX_targetObj.loadAction();
}//if
}//f()
lX_targetObj.ignoreWhite=true;
lX_targetObj.load(lX_targetObj.filename);
}//f()
Code: Select all
#include "../../../createMovieClip2.as"
#include "../../../loadXML2.as"
#include "../../../deepClone3.as"
#include "../../../DispTree.as"
//#include "../arrayFunctions.as" removed to get rid of unnecessary array functions
//-----
//09-15-2008 parse xml v2 by Kristoffe Brodeur. ©2008 All Rights Reserved.
//10-27-2008 adding object->xml for output back to normal xml parsing and php output
//10-30-2008 added T1.AP['cn'] to list the child node XML nodes that a parent object would have, saves wasted looping later in conversion
//03-01-2009 making the output very clear, clean, and a Class
//03-04-2009 rewrote entire Back2XMl for proper branch searching and optimal speed
//-----
Back2XML.prototype.stackChk=function()
{
//trace("stackChk>");
len=this.nodeStack.length;
tgt=this.nodeStack[len-1];
tgt.pos++;
//loop
if(tgt.pos<tgt.cnt)
{
//trace("cnt "+tgt.cnt+" | "+tgt.pos);
//array of node names
if(tgt.bNode.cn!=undefined)
{
//trace("cn=true");
nN=tgt.bNode.cn[tgt.pos];
this.AP=tgt.bNode[nN];
//
if(tgt.pos>0)
{
nN_prev=tgt.bNode.cn[tgt.pos-1];
this.xmlStr+="</"+nN_prev+">";
}
this.stackAdd();
}
//array of object nodes
else
{
tgt2=this.nodeStack[len-2];
this.AP=tgt.bNode[tgt.pos];
nN=tgt2.bNode.cn[tgt2.pos];
//
if(tgt.pos>0)
{
this.xmlStr+="</"+nN+">";
}
//trace("cn=false");
this.xmlStr+="<"+nN+">";
//trace("**"+this.xmlStr);
this.stackAdd();
}
}
//pop stack or add text string
else
{
//trace("end of loop.");
//
if(tgt.cnt!=undefined)
{
//
if(tgt.bNode.cn!=undefined)
{
nN=tgt.bNode.cn[tgt.pos-1];
//trace("CN!");
this.xmlStr+="</"+nN+">";
//trace(this.xmlStr);
}
//
else
{
//this.xmlStr+="</#>";
}
this.nodeStack.pop();
}
//
else
{
//trace("textual node.");
this.xmlStr+=this.AP.DATA
//this.xmlStr+="</"+this.AP.ID+">";
//trace(this.xmlStr+newline);
this.nodeStack.pop();
}
//
if(this.nodeStack.length>0)
{
this.stackChk();
}
//
else
{
//trace("END.");
trace(this.xmlStr);
this._caller.outputAction(this.xmlStr);
}
}
}
//-----
Back2XML.prototype.stackAdd=function()
{
//trace("stackAdd>");
len=this.nodeStack.length;
//
if(this.AP.cn!=undefined)
{
tgt=this.nodeStack[len]=new Object();
tgt.cnt=this.AP.cn.length;
tgt.pos=-1;
tgt.bNode=this.AP;
//trace("nN["+len+"]---->"+"cn[A]("+this.AP.cn+") cnt "+tgt.cnt+" | pos "+tgt.pos);
this.stackChk();
}
//
else
{
tgt=this.nodeStack[len]=new Object();
tgt.cnt=this.AP.length;
tgt.pos=-1;
tgt.bNode=this.AP;
//trace("nN["+len+"]---->obj[A] cnt "+tgt.cnt+" | pos "+tgt.pos);
this.stackChk();
}
}
//-----
Back2XML.prototype.init=function()
{
//trace("init>");
this.AP=_XML;//array pointer
this.recursion=0;
clearArray(this.nodeStack);
this.AP=_XML;//defined outside as an Object that the XML file is OOP parsed into
this.xmlStr="";
this.stackAdd();
}
//-----
//same as arrayFunctions2.as clearArray
function clearArray(sArr)
{
lenSA=sArr.length;
//
for(clrA=0;clrA<lenSA;clrA++)
{
sArr.pop();
}
}
//-----
function Back2XML(sCaller)
{
this._caller=sCaller;
//trace("Back2XML>");
this.nodeStack=new Array();
this.init();
}
//-----
function done_sort()
{
trace("done_sort>");
trace("--------------------");
this.parseAction();
/*
T1.tmp1=new DeepClone();
//pushCopy(base array,node to copy,new node to copy to,should new text nodes be clear?)
T1.tmp1.pushCopy(_XML.db[0].branch,0,null,null);
T1.tmp1.cloneAction=function()
{
trace("cloning complete!");
//E1=new DispTree(_XML);
test1=new Back2XML();
}
T1.tmp1.go();
//show_videos();
//T1.test=new Back2XML();
trace("--------------------");
//Btest=new DispTree(_XML);
*/
}
//-----
function coolDown()
{
T1.recursion=-1;
T1.recPause=new TimerA(T1.root,11);
T1.recPause.timerAction=function()
{
//trace("DONE!");
incr_stack1();
}
}
//-----
function check_recursion()
{
//trace("check_recursion>");
T1.recursion++;
//
if(T1.recursion>25)
{
//trace("cooling down");
coolDown();
}
else{incr_stack1();}
}
//-----
function node_pop()
{
//remove selected node
//trace("node_pop>");
}
//-----
function node_push(sBase,sNode)
{
//duplicate selected node
//trace("node_push>");
//trace(sBase);
//trace(sNode);
T1.cloned=new DeepClone(sBase,sNode,null,"push");
T1.cloned._par=T1.root;
T1.cloned.cloneAction=function()
{
//trace("finished node_push.");
this._par.cloneAction();
}
T1.cloned.go();
}
//-----
function Node(sName,sData)
{
this.ID=sName;
this.DATA=sData;
}
//-----
function addObjNode()
{
cnt=T1.nodeStack.length;
nName=T1.nodeStack[cnt-1];//0 counts in an array, so cnt-1 is the position of the end node
nExist=false;
//
if(T1.AP[nName]==undefined)
{
T1.AP[nName]=new Array();
}
//
else
{
nExist=true;
}
if(T1.AP['cn']==undefined){T1.AP['cn']=new Array();}//holds xml node names in parent
cnt=T1.AP[nName].length;
T1.AP[nName][cnt]=new Node(nName,"");
//T1.AP['cn'].push(nName);
//
if(nExist==false)
{
T1.AP['cn'].push(nName);
}
//
//only the first node parent doesnt have an ID or DATA
begN="<"+nName+">";
//trace(begN);
T1.stringXML+=begN;
T1.AP.chi_pnt=T1.AP[nName][cnt];
parPtr=T1.AP;
T1.AP=T1.AP[nName][cnt];
T1.AP.par_pnt=parPtr;
}
//-----
function addTextNode()
{
tgt=T1.xPtr.childNodes[T1.xPtr.pos];
text1=tgt.nodeValue;
//trace(text1);
text1fix="";
tl=text1.length;
for(i=0;i<tl;i++)
{
c1=text1.substr(i,1);
//
if(c1=="&")
{
text1fix+="&";
}
else
{
text1fix+=c1;
}
}
cnt=T1.nodeStack.length;
nName=T1.nodeStack[cnt-1];
//T1.AP already points to the new node in the dynamic array one step previous when called and made in addObjNode
cnt=T1.AP[nName].length;
T1.AP.DATA=text1fix;
T1.stringXML+=text1fix;
}
//-----
function decr_stack1()
{
//trace("decr_stack1>");
//
if(T1.nodeStack.length>0)
{
endX="</"+T1.nodeStack[T1.nodeStack.length-1]+">";
//trace(endX);
T1.stringXML+=endX;
}
T1.nodeStack.pop();
T1.xPtr=T1.xPtr.par_pnt;
T1.AP=T1.AP.par_pnt;
incr_stack1();
}
//-----
function change_pointer()
{
//trace("change_pointer>");
//
if(T1.xPtr.pos<T1.xPtr.cnt)
{
//trace("changing...");
par1=T1.xPtr;
T1.xPtr=T1.xPtr.childNodes[T1.xPtr.pos];
T1.xPtr.par_pnt=par1;
check_recursion();
}
}
//-----
function incr_stack2()
{
//trace("incr_stack2>");
cnt=T1.xPtr.childNodes.length;
//trace(cnt);
//
if(T1.xPtr.pos==undefined)
{
T1.xPtr.pos=-1;//start at -1 so it will roll to 0 with ++
T1.xPtr.cnt=cnt;//how many children are there?
}
T1.xPtr.pos++;
//
if(T1.xPtr.pos<T1.xPtr.cnt)
{
//trace(T1.xPtr.pos);
pos=T1.xPtr.pos;
nName=T1.xPtr.childNodes[pos].nodeName;
//
if(nName!=null)
{
//trace("?"+nName);
T1.nodeStack.push(nName);
addObjNode();
//trace("added");
change_pointer();
}
else
{
addTextNode();
decr_stack1();
}
}
else{decr_stack1();}
}
//-----
function incr_stack1()
{
//trace("incr_stack1>");
len=T1.xPtr.childNodes.length;
//
if(len>0){incr_stack2();}
//
else{
stackPos=T1.nodeStack.length;
//
if(stackPos>0){decr_stack1();}
//
else{done_sort();}
}
}
//-----
function parse_XML()
{
trace("f(parse_XML)");
T1.stringXML="";
T1.recursion=0;
T1.nodeStack=new Array();
T1.xPtr=T1.e_XML;
incr_stack1(T1.xPtr);
}
//-----
function l_XML()
{
trace("f(l_XML)");
T1.e_XML=new XML();
T1.e_XML.filename=T1.filename;
T1.e_XML.loadAction=function(){parse_XML();}
loadXML(T1.e_XML);
}
//-----
function main(sFilename)
{
T1.filename=sFilename;
tF_debug.text="main>"+sFilename;
//trace("main>"+sFilename);
T1.XML_ok=false;
delete T1.AP;
delete T1.root._XML;
T1.root._XML=new Object();
T1.AP=_XML;
l_XML();
}
//-----
var T1=new Object();
T1.root=this;
//trace("parser ready. send the *.xml filename to main(*); to begin.");
//main("ev1.xml");
//main("branchtest.xml");