<HTML>
<HEAD>
<TITLE>XML to DOM Converter: Listing 3</TITLE>
<APPLET CODE=com.ms.xml.dso.XMLDSO.class WIDTH=100% HEIGHT=30 
             ID=xmldso MAYSCRIPT=true>
<PARAM NAME="url" VALUE="someXMLDocument.xml">
</APPLET>
</HEAD>
<BODY>

<SCRIPT>
_root = xmldso.getDocument().root
results = window.document;


// Simple stack ADT
function stack()
{
     this.depth = 0;
     this.data = new Array();
     this.push = push;
     this.pop = pop;
     this.popTop = popTop;
     this.length = length;
     this.exists = exists;
     return this;
}

// Add an item
function push( item )
{
     this.data[ this.depth ++ ] = item;
}

// Remove last item
function pop()
{
     if ( this.depth )
     {
          this.depth --;
          return this.data[ this.depth ];
     }

     return null;
}

// Remove item at head of stack
function popTop()
{
     if ( this.depth )
     {
          // Pop the top item
         _j = this.data[ 0 ];
         this.depth --;

         // Shuffle the stack up a place
         for ( _i = 0; _i < this.depth; _i ++ )
         {
              this.data[ _i ] = this.data[ _i + 1 ]
          }

              return _j;		
     }
     return null;
}

// Return the size of the stack
function length()
{
     return this.depth;
}

// Does the stack hold the specified item?
function exists( obj )
{
     for ( _i = 0; _i < this.depth; _i ++ )
     {
          if ( this.data[ _i ] == obj )
          {
               return true;
          }
     }

     return false;
}

function xmltodom()
{
     // Cache the XML root, since retrieving it more than once via 	
     // getDocument() causes an error
     var root = _root

     // Holds XML node objects
     var x = new stack();

     // Holds incremental document description tags
     var y = new stack();

     // Holds unique paths of descent to XML leaf nodes 
     var d = new stack();

     x.push( root );
     y.push( "document." + root.tagname + "." );

     while ( x.length() != 0 )
     {
          // Pop top items, so that DOM tree is same way up as XML
          // tree; it would be easier simply to push them in reverse 
          // order, but xmldso.class seems to have problems navigating
               // a node's child-list backwards rather than forwards
               var xmlTag = x.popTop();
                    var docTag = y.popTop();

          // If the XML node is a leaf, assign its DOM equivalent its
          // textual value; otherwise create a new DOM object 
          var docString = docTag.substring( 0, docTag.length - 1 ) + " = ";

          if ( xmlTag.children.length != 0 )
          {
               docString += "new " + xmlTag.tagname + "();<br>";
          }
          else
          {
               docString += "'" + xmlTag.text + "';<br>";
          }

          results.writeln( docString );

          // Check that our XML tag is of type "ELEMENT"
          if ( xmlTag.type == 0 )
          {
               if ( xmlTag.children.length == 0 )
               {
                    // Generate the complete path to the element
                    var iter = xmlTag;
                    var t = new stack();

                    while ( iter.parent != null )
                    {
                         t.push( iter.tagname );
                         iter = iter.parent;
                    }

                    // Reverse the path
                    var path = "";

                    while ( t.length() )
                    {
                         path += t.pop() + ".";
                    }

                    // Check whether this object path has already
                    // been generated
                    if ( !d.exists( path ) )
                    {
                         d.push( path );
                    }
               }
           }

           // Stack children of this XML node
           if ( xmlTag.children )
           {
                // Might seem logical to cache xmlTag.children, but if
                // you do XMLDSO won't work properly
                for ( i = 0; i < xmlTag.children.length; i ++ )
                {
                     x.push( xmlTag.children.item( i ) );

                     // How many instances of a given child node at
                     // this level?
                     var numInstances = 0;

                     for ( j = 0; j < xmlTag.children.length; j ++ )
                     {
                          if ( xmlTag.children.item( i )
                            .tagName == xmlTag.children.item( j ).tagName )
                          {
                               numInstances ++;
                          }
                     }

                     // Get instance index for current node
                     var thisInstance = -1;

                     for ( j = 0; j < i + 1; j ++ )
                     {
                          if ( xmlTag.children.item( j )
                            .tagname == xmlTag.children.item( i ).tagname )
                          {
                               thisInstance ++;
                          }
                     }

                     if ( numInstances > 1 )
                     {
                          y.push( docTag + xmlTag.children.item( i )
                            .tagname + "[ " + thisInstance + " ]." );
                     }
                     else
                     {
                          y.push( docTag + xmlTag.children.item( i )
                            .tagname + "." );
                     }
               }
          }
     }

     // Now generate the ADT function prototypes for this XML document
     results.writeln( "<br>" );

     // Store the tree depth we have parsed
     var objDepth = 0;

     while ( true )
     {
          // Create an array of stacks to hold the collections
          // of sub-elements
          var newTerms = new Array();
          newTerms[ newTerms.length ] = new stack();

          // This will hold the immediate parent of each
          // "newTerms" stack
          var baseTerms = new Array();

          for ( i = 0; i < d.length(); i ++ )
          {
               var str = d.data[ i ];
               var testDepth = 0;
               var lastDot = 0;

               for ( j = 0; j < str.length; j ++ )
               {
                    if ( testDepth == objDepth )
                    {
                         break;
                    }

                    if ( str.charAt( j ) == "." )
                    {
                         testDepth ++;

                         if ( testDepth == objDepth - 1 )
                         {
                              lastDot = j;
                         }
                    }
               }

               if ( j < str.length )
               {
                    var listNum = 0;
                    var baseObject = str.substring( lastDot ? lastDot + 
                                                    1 : 0, j - 1 );

                    // If this parent has already been assigned,
                    // locate it; otherwise assign another one and
                    // add it to the array
                    var fFound = false;

                    for ( k = 0; k < baseTerms.length; k ++ )
                    {
                         if ( baseTerms[ k ] == baseObject )
                         {
                              fFound = true;
                              break;
                         }
                    }

                    if ( !fFound )
                    {
                         listNum = baseTerms.length;
                         newTerms[ listNum ] = new stack();
                         baseTerms[ baseTerms.length ] = baseObject;
                    }
                    else
                    {
                         listNum = k;
                    }

                    str = str.substring( j, str.indexOf( ".", j + 1 ) );

                    // If the sub-element is unique, add it to the
                    // appropriate sub-element collection
                    if ( !newTerms[ listNum ].exists( str ) )
                    {
                         newTerms[ listNum ].push( str );
                    }
               }
          }

          // Parse the two stacks, creating a Javascript object named
          // after each parent, and containing the collection
          // of sub-elements dependent upon that parent
          if ( objDepth != 0 )
          {
               var fItems = false;

               for ( l = 0; l < baseTerms.length; l ++ )
               {
                    if ( baseTerms[ l ].length )
                    {
                         results.writeln( "function " + baseTerms[ l ]
                                          + "()<br>" );
                         results.writeln( "{<br>" );

                         while ( newTerms[ l ].length() )
                         {
                              fItems = true;
                              results.writeln( "&nbsp;&nbsp;&nbsp;&nbsp;
                                               this." + newTerms[ l ].pop()
                                               + " = new Array();<br>" );
                         }

                         results.writeln( "&nbsp;&nbsp;&nbsp;&nbsp;
                                          return this;<br>" );
                         results.writeln( "}<br><br>" );
                    }
               }

               // If there were no items, we have finished traversing
               if ( !fItems )
               {
                    break;
               }
          }

          objDepth ++;
     }
}

// Convert the loaded document
xmltodom()
</SCRIPT>

</BODY>
</HTML>