#!/usr/bin/perl

#
# This script generates the attribute table for the integration specification.
# The script uses the definitions.xml files of the SVG 1.1 Full and SVG 1.2
# Tiny specifications as the source for the attribute names and links to their
# description. The final output produced is a HTML file containing a list of
# all the attributes (with links and associated elements) from SVG 1.1 Full and
# SVG 1.2 Tiny.
#
# A good understanding of how perl passes objects by reference is required as
# the script uses references to pass objects to functions. Additionally,
# knowledge in XSLT is required as XSLTs are used to pull information from the
# definition.xml files.
#
# This script was originally written by Anthony Grasso
#

#--- Static strings ------------------------------------------------------------

#
# Spec URLs.
#
$SVG_11_FULL_URL = "http\:\/\/www\.w3\.org\/TR\/SVG11\/";
$SVG_12_TINY_URL = "http\:\/\/www\.w3\.org\/TR\/SVGTiny12\/";

$SVG_11_FULL_DEFINITIONS = "../../../profiles/1.1F2/master/definitions.xml";
$SVG_12_TINY_DEFINITIONS = "../../../profiles/1.2T/master/definitions.xml";

$EXTRACT_ATTR_CAT_XSL = "./script-resources/extract_attribute_categories.xsl";
$EXTRACT_ATTR_INFO_XSL = "./script-resources/extract_attribute_information.xsl";
$EXTRACT_ELMNT_INFO_XSL = "./script-resources/extract_element_information.xsl";


#--- Global data ---------------------------------------------------------------

#
# Attribute Main Lookup Table
#
# Hash table to store the attributes and their associated elements. The entries
# in this table will be of type ATM_AttrMainEntry.
#
%attr_main_table_11f;
%attr_main_table_12t;


#
# Attribute Category Lookup Table
#
# Hash table to store the attribute categories and the elements that contain
# attributes that belong in each category. The entries in this table will be of
# type ATM_AttrCatEntry.
#
%attr_cat_table_11f;
%attr_cat_table_12t;


#
# Element Attribute Lookup Table
#
# Hash table to store element names and associated attributes. Not all elements
# will be entered into this table. This table stores any element names specified
# in <attribute> elements that are children of <definitions>. The entries in
# this table will be of type ATM_ElmntAttrEntry.
#
%elmnt_attr_table_11f;
%elmnt_attr_table_12t;


#--- Data types ----------------------------------------------------------------

#
# ATM_AttrMainEntry
#
# Description:
#   This structure is used to store the attribute information and the elements
#   that it is applicable to in the hash table. Note that the elements that
#   are applicable to the attribute (aam_ElmntList) are stored in hash table in
#   this structure. The entries in aam_ElmntList hash table are of type
#   ATM_ElmntEntry.
#
# Members:
#   aam_Name        Attribute name.
#
#   aam_Href        URL to the attribute description.
#
#   aam_ElmntList   Hash table of elements associated with the attribute.
#
$ATM_AttrMainEntry =
{
    TEXT    => $aam_Name,
    TEXT    => $aam_Href,
    LOOKUP  => {%aam_ElmntList},
};

#
# ATM_ElmntEntry
#
# Description:
#   This structure is used to store element information within the
#   ATM_AttrMainEntry data type.
#
# Members:
#   aee_Name    Element name.
#
#   aee_Href    URL to the element description.
#
$ATM_ElmntEntry =
{
    TEXT    => $aee_Name,
    TEXT    => $aee_Href,
};

#
# ATM_AttrCatEntry
#
# Description:
#   This structure is used to store the attribute names that belong to a
#   particular category .
#
# Members:
#   aac_Name        Attribute Category name.
#
#   aac_AttrList    List of attributes that belong to the category.
#
$ATM_AttrCatEntry =
{
    TEXT        => $aac_Name,
    SEQUENCE    => [@aac_AttrList],
};

#
# ATM_ElmntAttrEntry
#
# Description:
#   This structure is used to store the element names and associated attributes
#   that belong to it.
#
# Members:
#   aea_Name        Element name.
#
#   aea_AttrList    List of attributes that are assocated with the element.
#
$ATM_ElmntAttrEntry
{
    TEXT        => $aea_Name,
    SEQUENCE    => [@aea_AttrList],
};

#
# ATM_TempAttrEntry
#
$ATM_TempAttrEntry
{
    TEXT    => $ata_Name,
    TEXT    => $ata_Href,
};


#--- Function definitions ------------------------------------------------------

#
# ATM_populate_attribute_category_table
#
# Description:
#   This function parses the lines containing the attribute category
#   information gathered from the XML definitions file. The parsed data is used
#   to populate the attribute category table "attr_cat_table". A single line to
#   be parsed is of the format:
#
#   [category name]|[category list]|[attribute href]|[[attribute name]|[attribute href]|...|[attribute name]|[attribute href]]
#
#   [ segment 0   ]|[ segment 1   ]|[ segment 2    ]|[[ segment 3    ]|[ segment 4    ]|...|[ segment N     ]|[ segment N +1 ]]
#
# Parameters:
#   $line_ptr           - [Argument 1] Reference to the line to parse.
#
#   $attr_cat_table_ptr - [Argument 2] Reference to the hash table to populate.
#
sub ATM_populate_attribute_category_table
{
    #
    # Obtain references to arguments and split the line up into segments.
    #
    my ($line_ptr, $attr_cat_table_ptr) = @_;
    my @line_segments = split(/\|/, ${$line_ptr});

    #
    # Check if we have more than a single segment in the line. If there is only
    # one segment it means only a name is supplied which is pointless as there
    # are no attribute names or any list to associate with the category name.
    #
    if (@line_segments < 3)
    {
        return 0;
    }

    #
    # Assign attribute category name to hash entry.
    #
    $ATM_AttrCatEntry = {};
    $ATM_AttrCatEntry->{aac_Name} = $line_segments[0];

    print "Adding category: ".$line_segments[0]."\n";

    my $array_itr = 0;

    if ($line_segments[1] eq "")
    {
        goto add_remaining_attributes;
    }

    my @attr_list = split(/,/, $line_segments[1]);

    foreach $attribute (@attr_list)
    {
        #
        # Remove any inital spaces
        #
        $attribute =~ m/\s*([a-zA-Z0-9\:\-]*)/;

        print " adding attribute: \'".$1."\' to category\n";

        #
        # Because the list of attributes does not contain any href values to
        # the specification. Use the href supplied with the category.
        #
        $ATM_AttrCatEntry->{aac_AttrList}[$array_itr][0] = $1;
        $ATM_AttrCatEntry->{aac_AttrList}[$array_itr][1] = $line_segments[2];
        $array_itr++;
    }

add_remaining_attributes:

    my $seg_itr = 3;
    #
    # Populate array in hash entry. Add the attribute name (first line) and the
    # href containing the attribute reference in the specification (second
    # line). Only need to process n - 1 segments as every second segment is an
    # attribute name.
    #
    for (; $seg_itr < @line_segments - 1;)
    {
        print " adding attribute \'$line_segments[$seg_itr]\' to category.\n";

        $ATM_AttrCatEntry->{aac_AttrList}[$array_itr][0] = $line_segments[$seg_itr];
        $ATM_AttrCatEntry->{aac_AttrList}[$array_itr][1] = $line_segments[$seg_itr + 1];

        $seg_itr += 2;
        $array_itr++;
    }

    ${$attr_cat_table_ptr}{$ATM_AttrCatEntry->{aac_Name}} = $ATM_AttrCatEntry;
}


#
# ATM_parse_attribute_information
#
# Description:
#   This function parses a line (Argument 1) from the XML definitions file which
#   consists of an attribute name and its associated href to the specification.
#   If the parsed data contains the element names associated with the attribute
#   The data is placed in the element table (Argument 2). Else, if there are no
#   element names present the data is placed in the attribute table
#   (Argument 3). A single line to be parsed is of the format:
#
#   [attribute name]|[attribute href]|[element names]
#
#   [ segment 0    ]|[ segment 1    ]|[ segment 0   ]
#
#
# If only 2 segments are supplied it means that no element names were supplied
# with the attribute name and href. In that case the function will put the
# attribute information directly in the main table as the attribute will be
# common to most elements.
#
# If 3 segments are supplied then the third segment will contain the specific
# elements that the attribute is associated with. In this case the function add
# the information to the element table. When parsing the element information the
# element table will be used to gather attributes specifically associated with
# it.
#
# Parameters:
#   $line_ptr               - [Argument 1] Reference to the line to parse.
#
#   $elmnt_table_ptr        - [Argument 2] Reference to the element has table.
#
#   $attr_main_table_ptr    - [Argument 3] Reference to the attribute hash table.
#
sub ATM_parse_attribute_information
{
    #
    # Obtain references to arguments and split the line up into segments.
    #
    my ($line_ptr, $elmnt_table_ptr, $attr_main_table_ptr) = @_;
    my @line_segments = split(/\|/, ${$line_ptr});

    #
    # Check if we have more than a single segment in the line. If there is only
    # one segment it means only an attribute name was supplied which is
    # pointless as there is no other information to associate with the
    # attribute.
    #
    if (@line_segments < 2)
    {
        return 0;
    }

    #
    # Less than 3 segments means that no element was supplied with the attribute
    # name and href. In that case put the attribute information directly in
    # the main table as the attribute will be common to most elements. If a
    # third segment is supplied then it will contain the specific elements that
    # the attribute is associated with. In this case add the information to the
    # element table.
    #
    if (@line_segments < 3)
    {
        print "Adding attribute \'$line_segments[0]\' to main table...";
        #
        # If the attribute name is not already in the hash table add a new
        # entry. Else, update the attribute entry if it requires it.
        #
        if (!exists ${$attr_main_table_ptr}{$line_segments[0]})
        {
            $ATM_AttrMainEntry = {};
            $ATM_AttrMainEntry->{aam_Name} = $line_segments[0];
            $ATM_AttrMainEntry->{aam_Href} = $line_segments[1];
            $ATM_AttrMainEntry->{aam_ElmntList} = {};
            ${$attr_main_table_ptr}{$line_segments[0]} = $ATM_AttrMainEntry;

            print "new entry created.\n";
        }
        else
        {
            print   "WARNING: entry already exists; only one ",
                    "$line_segments[0] <attribute> definition should exist.",
                    " Merging entries.\n";

            #
            # Check if the href for the attribute has been defined. If not then
            # update it.
            #
            if (${$attr_main_table_ptr}{$line_segments[0]}->{aam_Href} eq "")
            {
                ${$attr_main_table_ptr}{$line_segments[0]}->{aam_Href} = $line_segments[1];
            }
        }

        return 0;
    }

    my @elmnt_list = split(/,/, $line_segments[2]);

    foreach $element (@elmnt_list)
    {
        #
        # Remove any inital spaces
        #
        $element =~ m/\s*([a-zA-Z\:\-]*)/;
        $element = $1;

        print   "Adding attribute \'$line_segments[0]\' to",
                " <$element> element table...";

        #
        # If the attribute name is not already in the hash table add a new
        # entry. Else, append to the current entry. Note, not expecting any
        # duplicates in the data supplied to element table.
        #
        if (!exists ${$elmnt_table_ptr}{$element})
        {
            $ATM_ElmntAttrEntry = {};
            $ATM_ElmntAttrEntry->{aea_Name} = $element;
            $ATM_ElmntAttrEntry->{aea_AttrList}[0][0] = $line_segments[0];
            $ATM_ElmntAttrEntry->{aea_AttrList}[0][1] = $line_segments[1];
            ${$elmnt_table_ptr}{$element} = $ATM_ElmntAttrEntry;

            print "new entry created.\n";
        }
        else
        {
            my $array_len = @{${$elmnt_table_ptr}{$element}->{aea_AttrList}};

            ${$elmnt_table_ptr}{$element}->{aea_AttrList}[$array_len][0] = $line_segments[0];
            ${$elmnt_table_ptr}{$element}->{aea_AttrList}[$array_len][1] = $line_segments[1];

            print "appending to current entry $array_len.\n";
        }
    }
}


#
# ATM_parse_element_information
#
# Description:
#   This function parses a line (Argument 1) from the XML definitions file which
#   consists of an element and its associated attribute names. The parsed data
#   is placed in the attribute table (Argument 2). A single line to be parsed is
#   of the format:
#
#   [element name]|[element href]|[attribute categories]|[attribute names]|[[additional attribute name]|[additional attribute href]|...|[additional attribute name]|[additional attribute href]]
#
#   [ segment 0  ]|[ segment 1  ]|[ segment 2          ]|[ segment 3     ]|[[ segment 4]| ... |[ segment N]]
#
#   Some elements in the XML definitions file contian child elements which
#   specify additional attributes that are applicable to the element. The child
#   elements additionally contain the href to the section of the specification
#   that contains the definition of the attribute.
#
#   Some elements in the XML definitions file belong to a category of
#   attributes. In this case an Attribute Category Lookup Table (Argument 3) is
#   used to determine the exact attribute names that are associated with the
#   element.
#
#   This function creates a list of attributes from the Attribute Category
#   Lookup Table (using the attribute categories) and the attribute names.
#   Additionally, a temporary lookup table is created containing any additional
#   additional attributes supplied in segment 4 onwards and attributes from
#   the fuction Element Attribute Lookup Table. The list of attributes is
#   traversed and checked against the temporary lookup table.
#
#   If there is a duplication of a particular attribute name, the attribute
#   information source added to the main table for the element is from the
#   temporary lookup table. In this case the other duplicate attribute
#   information in the list associated with the element is discarded.
#
#   Any non-duplicated attributes in the lookup table are added to the Main
#   Attribute Lookup Table.
#
#   The Attribute Category Lookup Table and Element Attribute Lookup Table the
#   must both be populated before this function can be called.
#
# Parameters:
#   $line_ptr               - [Argument 1] Reference to the line to parse.
#
#   $attr_main_table_ptr    - [Argument 2] Reference to the hash table to populate.
#
#   $attr_cat_table_ptr     - [Argument 3] Reference to the attribute category table.
#
#   $elmnt_table_ptr        - [Argument 4] Reference to the element attribute table.
#
sub ATM_parse_element_information
{
    #
    # Obtain references to arguments and split the line up into segments.
    #
    my ($line_ptr, $attr_main_table_ptr, $attr_cat_table_ptr, $elmnt_table_ptr) = @_;
    my @line_segments = split(/\|/, ${$line_ptr});
    my @temp_attr_list = ();
    my %temp_attr_table = ();

    #
    # Check if there is more than two segments in the line. If there are less
    # than three segments it means only a name and a href for the element has
    # been supplied which is pointless as there are no associated attributes to
    # link the element to.
    #
    if (@line_segments < 3)
    {
        return 0;
    }

    my $elmnt_name = $line_segments[0];
    my $elmnt_href = $line_segments[1];

    $ATM_ElmntEntry = {};

    #
    # Set up the element structure initially as this maybe stored in multiple
    # places during the function call.
    #
    $ATM_ElmntEntry->{aee_Name} = $elmnt_name;
    $ATM_ElmntEntry->{aee_Href} = $elmnt_href;

    print "\nProcessing <".$elmnt_name.">\n";

    print "Building attribute list.\n";

    #
    # Build list of attributes associated with the element. List is constructed
    # from the attribute categories (segment 2) and attribute names (segment 3).
    #
    if ($line_segments[2] eq "")
    {
        print "No attribute categories found, checking for attribute names.\n";
        goto process_attribute_names;
    }

    my @attr_cats = split(/,/, $line_segments[2]);

    foreach $category (@attr_cats)
    {
        #
        # Remove any inital spaces
        #   
        $category =~ m/\s*([a-zA-Z\-\s]*)/;

        if (@{${$attr_cat_table_ptr}{$1}->{aac_AttrList}} == 0)
        {
            next;
        }

        print " adding attributes from category $1\n";
        push(@temp_attr_list, @{${$attr_cat_table_ptr}{$1}->{aac_AttrList}});
    }


process_attribute_names:

    if ($line_segments[3] eq "")
    {
        print "No attribute names found.\n";
        goto construct_temp_lookup_table;
    }

    my @attr_names = split(/,/, $line_segments[3]);

    foreach $attribute (@attr_names)
    {
        #
        # Remove any initial spaces
        #
        $attribute =~ m/\s*([a-zA-Z0-9\:\-]*)/;

        print " adding \'$1\' to list.\n";
        $temp_attr_list[@temp_attr_list][0] = $1;
    }


    #
    # Build the temporary hash table.
    #
construct_temp_lookup_table:

    print "Building temporary lookup table\n";

    my @child_elmnt_attr_ = ();

    #
    # Process the various attributes associated with the element, if any and
    # add those to the hash table.
    #
    if (@line_segments < 5)
    {
        goto process_attributes_in_element_table;
    }

    @child_elmnt_attr = @line_segments[4..@line_segments - 1];

    my $child_elmnt_itr = 0;

    for (; $child_elmnt_itr < @child_elmnt_attr;)
    {
        $ATM_TempAttrEntry = {};
        $ATM_TempAttrEntry->{ata_Name} = $child_elmnt_attr[$child_elmnt_itr];
        $ATM_TempAttrEntry->{ata_Href} = $child_elmnt_attr[$child_elmnt_itr + 1];

        $temp_attr_table{$ATM_TempAttrEntry->{ata_Name}} = $ATM_TempAttrEntry;

        print " adding \'$ATM_TempAttrEntry->{ata_Name}\' to table\n";

        $child_elmnt_itr += 2;
    }

process_attributes_in_element_table:
    #
    # Check Element lookup table for any additional attributes that may belong
    # to the element. If there are any additional attributes, add them to the
    # temporary lookup table.
    #
    if (exists ${$elmnt_table_ptr}{$elmnt_name})
    {
        my $elmnt_attr_itr = 0;

        for (; $elmnt_attr_itr < @{${$elmnt_table_ptr}{$elmnt_name}->{aea_AttrList}};)
        {
            $ATM_TempAttrEntry = {};
            $ATM_TempAttrEntry->{ata_Name} = ${$elmnt_table_ptr}{$elmnt_name}->{aea_AttrList}[$elmnt_attr_itr][0];
            $ATM_TempAttrEntry->{ata_Href} = ${$elmnt_table_ptr}{$elmnt_name}->{aea_AttrList}[$elmnt_attr_itr][1];

            print " adding \'$ATM_TempAttrEntry->{ata_Name}\' to table\n";

            $temp_attr_table{$ATM_TempAttrEntry->{ata_Name}} = $ATM_TempAttrEntry;

            $elmnt_attr_itr++;
        }
    }


    #
    # Iterate through the attributes in the list and check them against
    # attributes in the temporary lookup table.
    #

    print "Processing attribute list.\n";

    my $temp_attr_itr = 0;

    for (; $temp_attr_itr < @temp_attr_list;)
    {
        print "\'$temp_attr_list[$temp_attr_itr][0]\'...";

        #
        # Check if the attribute in the temporary list exsists in the temporary
        # hash table.
        #
        if (exists $temp_attr_table{$temp_attr_list[$temp_attr_itr][0]})
        {
            #
            # Create a new key in the main attribute hash table using the
            # information out of the temporary hash table.
            #
            my $attr_name = $temp_attr_table{$temp_attr_list[$temp_attr_itr][0]}->{ata_Name};
            my $attr_href = $temp_attr_table{$temp_attr_list[$temp_attr_itr][0]}->{ata_Href};

            my @href_segs = split(/\#/, $attr_href);
            my $attr_main_entry_key = $attr_name."_".$href_segs[1];

            if (!exists ${$attr_main_table_ptr}{$attr_main_entry_key})
            {
                print "Creating new entry [$attr_main_entry_key] in Main Attribute Table from the temporary lookup table\n";

                $ATM_AttrMainEntry = {};
                $ATM_AttrMainEntry->{aam_Name} = $attr_name;
                $ATM_AttrMainEntry->{aam_Href} = $attr_href;
                $ATM_AttrMainEntry->{aam_ElmntList}{$ATM_ElmntEntry->{aee_Name}} = $ATM_ElmntEntry;

                ${$attr_main_table_ptr}{$attr_main_entry_key} = $ATM_AttrMainEntry;
            }
            else
            {
                print "Appending entry [$attr_main_entry_key] in Main Attribute Table from lookup table.\n";

                ${$attr_main_table_ptr}{$attr_main_entry_key}->{aam_ElmntList}{$elmnt_name} = $ATM_ElmntEntry;
            }

            #
            # Delete the entries as they are found in the hash table. Any
            # entries in the list that are not in the hash table will be added
            # after this loop has iterated through the list.
            #
            delete($temp_attr_table{$temp_attr_list[$temp_attr_itr][0]});
        }
        else
        {
            #
            # Create a new key in the main attribute hash table using the
            # information out of the temporary list.
            #
            my $attr_name = $temp_attr_list[$temp_attr_itr][0];
            my $attr_href = $temp_attr_list[$temp_attr_itr][1];
            my $attr_main_entry_key = $attr_name;

            if ($attr_href ne "")
            {
                my @href_segs = split(/\#/, $attr_href);
                $attr_main_entry_key = $attr_main_entry_key."_".$href_segs[1];
            }
            else
            {
                print " WARNING: No HREF defined for entry.\n";
            }

            if (!exists ${$attr_main_table_ptr}{$attr_main_entry_key})
            {
                print "Creating new entry in Main Attribute Table from the attribute list.\n";

                $ATM_AttrMainEntry = {};
                $ATM_AttrMainEntry->{aam_Name} = $attr_name;
                $ATM_AttrMainEntry->{aam_Href} = $attr_href;
                $ATM_AttrMainEntry->{aam_ElmntList}{$ATM_ElmntEntry->{aee_Name}} = $ATM_ElmntEntry;

                ${$attr_main_table_ptr}{$attr_main_entry_key} = $ATM_AttrMainEntry;
            }
            else
            {
                if (!exists ${$attr_main_table_ptr}{$attr_main_entry_key}->{aam_ElmntList}{$elmnt_name})
                {
                    ${$attr_main_table_ptr}{$attr_main_entry_key}->{aam_ElmntList}{$elmnt_name} = $ATM_ElmntEntry;
                }

                print "Appending entry in Main Attribute Table from the attribute list\n";
            }
        }

        $temp_attr_itr++;
    }

    #
    #
    #
    my @temp_remaining_attr = sort(keys(%temp_attr_table));

    if (@temp_remaining_attr < 1)
    {
        goto end_of_process_element_information;
    }

    print "Adding remaining attributes to Main Attribute Lookup Table.\n";

    foreach $remaining_attr (@temp_remaining_attr)
    {
        my $attr_name = $temp_attr_table{$remaining_attr}->{ata_Name};
        my $attr_href = $temp_attr_table{$remaining_attr}->{ata_Href};

        my @href_segs = split(/\#/, $attr_href);
        my $attr_main_entry_key = $attr_name."_".$href_segs[1];

        if (!exists ${$attr_main_table_ptr}{$attr_main_entry_key})
        {
            print "\'$attr_name\'...Creating new entry [$attr_main_entry_key] in Main Attribute Table from the temporary lookup table.\n";

            $ATM_AttrMainEntry = {};
            $ATM_AttrMainEntry->{aam_Name} = $attr_name;
            $ATM_AttrMainEntry->{aam_Href} = $attr_href;
            $ATM_AttrMainEntry->{aam_ElmntList}{$ATM_ElmntEntry->{aee_Name}} = $ATM_ElmntEntry;

            ${$attr_main_table_ptr}{$attr_main_entry_key} = $ATM_AttrMainEntry;
        }
        else
        {
            print "\'$attr_name\'...Appending entry [$attr_main_entry_key] in Main Attribute Table from lookup table.\n";

            ${$attr_main_table_ptr}{$attr_main_entry_key}->{aam_ElmntList}{$elmnt_name} = $ATM_ElmntEntry;
        }
    }

end_of_process_element_information:
    print "Element processing complete\n";
}


#
# ATM_write_html_doc_head
#
sub ATM_write_html_doc_head
{
    print HTML_FH   "<html xmlns=\"http\:\/\/www\.w3\.org\/1999\/xhtml\" >\n",
                    "  <head>\n",
                    "    <title>SVG Attribute Table</title>\n",
                    "    <link rel=\"stylesheet\" href=\"style\/svg-style\.css\" type=\"text\/css\" media=\"all\" />\n",
                    "  </head>\n",
                    "  <body>\n";
}

#
# ATM_write_html_doc_end
#
sub ATM_write_html_doc_end
{
    print HTML_FH   "  </body>\n",
                    "</html>";
}

#
# ATM_write_html_table_head
#
sub ATM_write_html_table_head
{
    print HTML_FH   "    <table border=\"1\" cellpadding=\"5\" cellspacing=\"0\" >\n",
                    "      <tbody>\n",
                    "        <tr>\n",
                    "          <th rowspan=\"2\">Attribute</th>\n",
                    "          <th rowspan=\"2\">Type</th>\n",
                    "          <th colspan=\"2\">Applicable Elements</th>\n",
                    "        </tr>\n",
                    "        <tr>\n",
                    "          <th>SVG 1.1</th>\n",
                    "          <th>SVG 1.2</th>\n",
                    "        </tr>\n";
}

#
# ATM_write_html_table_end
#
sub ATM_write_html_table_end
{
    print HTML_FH   "      </tbody>\n",
                    "    </table>\n";
}

#
# ATM_write_html_table_single_entry
#
sub ATM_write_html_table_single_entry
{
    my ($attr_key_ptr, $attr_main_table_ptr, $url_ptr) = @_;

    my $attr_name = ${$attr_main_table_ptr}{${$attr_key_ptr}}->{aam_Name};
    my $url_11f = "SVG11";
    my $url_12t = "SVGT12";
    my $link_style_11f = "svg11-no-link";
    my $link_style_12t = "svgt12-no-link";
    my $elmnt_links_11f = "<span class=\"svg11-na\">N/A</span>";
    my $elmnt_links_12t = "<span class=\"svg12-na\">N/A</span>";
    my $elmnt_links_list = "";
    my @elmnt_list_sorted = sort(keys(%{${$attr_main_table_ptr}{${$attr_key_ptr}}->{aam_ElmntList}}));

    my $list_itr = 0;

    for (; $list_itr < @elmnt_list_sorted;)
    {
        #
        # Generate a comma separated list of elements. However, only write a
        # comma out after the first line is processed.
        #
        if ($list_itr > 0)
        {
            $elmnt_links_list = $elmnt_links_list.", ";
        }

        $elmnt_links_list = $elmnt_links_list."<a href=\"${$url_ptr}${$attr_main_table_ptr}{${$attr_key_ptr}}->{aam_ElmntList}{$elmnt_list_sorted[$list_itr]}->{aee_Href}\">$elmnt_list_sorted[$list_itr]</a>";

        $list_itr++;
    }

    if (${$url_ptr} eq $SVG_11_FULL_URL)
    {
        $link_style_11f = "svg11-link";
        $elmnt_links_11f = $elmnt_links_list;

        if (${$attr_main_table_ptr}{${$attr_key_ptr}}->{aam_Href} eq "")
        {
            goto print_attribte_row_heading;
        }

        $url_11f = "<a href=\"${$url_ptr}${$attr_main_table_ptr}{${$attr_key_ptr}}->{aam_Href}\">SVG11</a>";
    }
    else
    {
        $link_style_12t = "svgt12-link";
        $elmnt_links_12t = $elmnt_links_list;

        if (${$attr_main_table_ptr}{${$attr_key_ptr}}->{aam_Href} eq "")
        {
            goto print_attribte_row_heading;
        }

        $url_12t = "<a href=\"${$url_ptr}${$attr_main_table_ptr}{${$attr_key_ptr}}->{aam_Href}\">SVGT12</a>";
    }

print_attribte_row_heading:
    print HTML_FH   "        <tr id=\"attribute_${$attr_key_ptr}\">\n",
                    "          <td>\n",
                    "            <span class=\"attr-name\">$attr_name</span>\n",
                    "            <br/>\n",
                    "            [<span class=\"$link_style_11f\">$url_11f</span>, <span class=\"$link_style_12t\">$url_12t</span>]\n",
                    "          </td>\n",
                    "          <td><span class=\"attribute\">attribute</span></td>\n",
                    "          <td>$elmnt_links_11f</td>\n",
                    "          <td>$elmnt_links_12t</td>\n",
                    "        </tr>\n";
}

#
# ATM_write_html_table_merge_entry
#
sub ATM_write_html_table_merge_entry
{
    my ($attr_key_ptr) = @_;

    my $attr_name = $attr_main_table_11f{${$attr_key_ptr}}->{aam_Name};
    my $url_11f = "SVG11";
    my $url_12t = "SVGT12";
    my $elmnt_links_11f = "";
    my $elmnt_links_12t = "";
    my @elmnt_list_sorted_11f = sort(keys(%{$attr_main_table_11f{${$attr_key_ptr}}->{aam_ElmntList}}));
    my @elmnt_list_sorted_12t = sort(keys(%{$attr_main_table_12t{${$attr_key_ptr}}->{aam_ElmntList}}));


    if ($attr_main_table_11f{${$attr_key_ptr}}->{aam_Href} ne "")
    {
        $url_11f = "<a href=\"$SVG_11_FULL_URL$attr_main_table_11f{${$attr_key_ptr}}->{aam_Href}\">SVG11</a>";
    }

    if ($attr_main_table_12t{${$attr_key_ptr}}->{aam_Href} ne "")
    {
        $url_12t = "<a href=\"$SVG_12_TINY_URL$attr_main_table_12t{${$attr_key_ptr}}->{aam_Href}\">SVGT12</a>";
    }

    my $list_itr = 0;

    for (; $list_itr < @elmnt_list_sorted_11f;)
    {
        #
        # Generate a comma separated list of elements. However, only write a
        # comma out after the first line is processed.
        #
        if ($list_itr > 0)
        {
            $elmnt_links_11f = $elmnt_links_11f.", ";
        }

        $elmnt_links_11f = $elmnt_links_11f."<a href=\"$SVG_11_FULL_URL$attr_main_table_11f{${$attr_key_ptr}}->{aam_ElmntList}{$elmnt_list_sorted_11f[$list_itr]}->{aee_Href}\">$elmnt_list_sorted_11f[$list_itr]</a>";

        $list_itr++;
    }

    $list_itr = 0;

    for (; $list_itr < @elmnt_list_sorted_12t;)
    {
        #
        # Generate a comma separated list of elements. However, only write a
        # comma out after the first line is processed.
        #
        if ($list_itr > 0)
        {
            $elmnt_links_12t = $elmnt_links_12t.", ";
        }

        $elmnt_links_12t = $elmnt_links_12t."<a href=\"$SVG_12_TINY_URL$attr_main_table_12t{${$attr_key_ptr}}->{aam_ElmntList}{$elmnt_list_sorted_12t[$list_itr]}->{aee_Href}\">$elmnt_list_sorted_12t[$list_itr]</a>";

        $list_itr++;
    }

    print HTML_FH   "        <tr id=\"attribute_${$attr_key_ptr}\">\n",
                    "          <td>\n",
                    "            <span class=\"attr-name\">$attr_name</span>\n",
                    "            <br/>\n",
                    "            [<span class=\"svg11-link\">$url_11f</span>, <span class=\"svgt12-link\">$url_12t</span>]\n",
                    "          </td>\n",
                    "          <td><span class=\"attribute\">attribute</span></td>\n",
                    "          <td>$elmnt_links_11f</td>\n",
                    "          <td>$elmnt_links_12t</td>\n",
                    "        </tr>\n";

}


#--- Main program operation ----------------------------------------------------

#
# The xslt output will be one long line with delimiters. An in the line '&'
# indicates a new line of data and a '|' indicates a segment of data. The
# initial XSLT output (single line) is gathered and stored in @xslt_output. The
# output is then split into single data lines according to the placement of the
# '&' and stored in @xslt_entries. Each entry is then processed. This procedure
# is performed each time an XSLT script is used to extract data from the
# definitions.xml files.
#

#
# Gather the <attributecategory> data to populate the attribute category
# lookup tables.
#
my @xslt_output = ();
my @xslt_entries = ();

print "\nPopulating attribute category lookup tables.\n";

open my $CMD_FH, "xsltproc --novalid $EXTRACT_ATTR_CAT_XSL $SVG_11_FULL_DEFINITIONS | " or die "cannot run command: $!";
@xslt_output = <$CMD_FH>;
close $CMD_FH;

@xslt_entries = split(/\&/, $xslt_output[0]);

foreach $line (@xslt_entries)
{
    ATM_populate_attribute_category_table(\$line, \%attr_cat_table_11f);
}


@xslt_output = ();
@xslt_entries = ();

open my $CMD_FH, "xsltproc --novalid $EXTRACT_ATTR_CAT_XSL $SVG_12_TINY_DEFINITIONS | " or die "cannot run command: $!";
@xslt_output = <$CMD_FH>;
close $CMD_FH;

@xslt_entries = split(/\&/, $xslt_output[0]);

foreach $line (@xslt_entries)
{
    ATM_populate_attribute_category_table(\$line, \%attr_cat_table_12t);
}


#
# Gather the <attribute> data that is defined and populate the attribute lookup
# tables.
#
print   "\nPopulating element lookup tables with element and attribute",
        " information.\n";

@xslt_output = ();
@xslt_entries = ();

open my $CMD_FH, "xsltproc --novalid $EXTRACT_ATTR_INFO_XSL $SVG_11_FULL_DEFINITIONS | " or die "cannot run command: $!";
@xslt_output = <$CMD_FH>;
close $CMD_FH;

@xslt_entries = split(/\&/, $xslt_output[0]);

foreach $line (@xslt_entries)
{
    ATM_parse_attribute_information(\$line, \%elmnt_attr_table_11f, \%attr_main_table_11f);
}


@xslt_output = ();
@xslt_entries = ();

open my $CMD_FH, "xsltproc --novalid $EXTRACT_ATTR_INFO_XSL $SVG_12_TINY_DEFINITIONS | " or die "cannot run command: $!";
@xslt_output = <$CMD_FH>;
close $CMD_FH;

@xslt_entries = split(/\&/, $xslt_output[0]);

foreach $line (@xslt_entries)
{
    ATM_parse_attribute_information(\$line, \%elmnt_attr_table_12t, \%attr_main_table_12t);
}


#
# Gather the <element> data that is defined and populate the attribute lookup
# tables.
#
print "\nPopulating attribute name lookup tables with element information.\n";

@xslt_output = ();
@xslt_entries = ();

open my $CMD_FH, "xsltproc --novalid $EXTRACT_ELMNT_INFO_XSL $SVG_11_FULL_DEFINITIONS | " or die "cannot run command: $!";
@xslt_output = <$CMD_FH>;
close $CMD_FH;

@xslt_entries = split(/\&/, $xslt_output[0]);

foreach $line (@xslt_entries)
{
    ATM_parse_element_information(\$line, \%attr_main_table_11f, \%attr_cat_table_11f, \%elmnt_attr_table_11f);
}


@xslt_output = ();
@xslt_entries = ();

open my $CMD_FH, "xsltproc --novalid $EXTRACT_ELMNT_INFO_XSL $SVG_12_TINY_DEFINITIONS | " or die "cannot run command: $!";
@xslt_output = <$CMD_FH>;
close $CMD_FH;

@xslt_entries = split(/\&/, $xslt_output[0]);

foreach $line (@xslt_entries)
{
    ATM_parse_element_information(\$line, \%attr_main_table_12t, \%attr_cat_table_12t, \%elmnt_attr_table_12t);
}



#
# Get an ordered list of the attributes in the hash table.
#
my @attr_list_11f_sorted = sort(keys(%attr_main_table_11f));
my @attr_list_12t_sorted = sort(keys(%attr_main_table_12t));

#print @attr_list_11f_sorted."\n";
#print @attr_list_12t_sorted."\n";

open HTML_FH, ">", "attribute_table.html" or die "Cannot open file for writing: $!\n";

ATM_write_html_doc_head();

ATM_write_html_table_head();

#
# Merge the two tables together.
#
my $list_12t_itr = 0;
my $list_11f_itr = 0;
my $cmp_result = 0;

while ($list_11f_itr < @attr_list_11f_sorted && $list_12t_itr < @attr_list_12t_sorted)
{
    #
    # $a cmp $b returns: 1 when $a > $b, -1 when $a < $b, and 0 when $a == $b.
    #
    $cmp_result = $attr_list_11f_sorted[$list_11f_itr] cmp $attr_list_12t_sorted[$list_12t_itr];
#    print "Comparing ".$attr_list_11f_sorted[$list_11f_itr]." and ".$attr_list_12t_sorted[$list_12t_itr]."... ";

    #
    # If this case is true the 11F table entry > 12T table entry. The 12T table
    # entry needs to be processed and the iterator incremented because it is
    # behind.
    #
    if ($cmp_result == 1)
    {
        ATM_write_html_table_single_entry(\$attr_list_12t_sorted[$list_12t_itr], \%attr_main_table_12t, \$SVG_12_TINY_URL);
#        print $attr_list_11f_sorted[$list_11f_itr]." > ".$attr_list_12t_sorted[$list_12t_itr]."\n";
        $list_12t_itr++;
    }

    #
    # If this case is true the 11F table entry < 12T table entry The 11F table
    # entry needs to be processed and the iterator incremented because it is
    # behind.
    #
    if ($cmp_result == -1)
    {
        ATM_write_html_table_single_entry(\$attr_list_11f_sorted[$list_11f_itr], \%attr_main_table_11f, \$SVG_11_FULL_URL);
#        print $attr_list_11f_sorted[$list_11f_itr]." < ".$attr_list_12t_sorted[$list_12t_itr]."\n";
        $list_11f_itr++;
    }

    #
    # If this case is true the 11F table entry == 12T table entry. The two
    # entries are merged and both the 11F and 12T iterators are incremented.
    #
    if ($cmp_result == 0)
    {
        ATM_write_html_table_merge_entry(\$attr_list_11f_sorted[$list_11f_itr]);
#        print $attr_list_11f_sorted[$list_11f_itr]." == ".$attr_list_12t_sorted[$list_12t_itr]."\n";
        $list_11f_itr++;
        $list_12t_itr++;
    }
}


ATM_write_html_table_end();

ATM_write_html_doc_end();

close HTML_FH;