#
# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of the License "Eclipse Public License v1.0"
# which accompanies this distribution, and is available
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
#
# Initial Contributors:
# Nokia Corporation - initial contribution.
#
# Contributors:
#
# Description:
#
#****h* lib/xml2tree.pm
# NAME
# xml2tree.pm
# DESCRIPTION
# This module is used to parse the data from a xml file and convert it to tree like data structure for further processing
# Each node is connected two way and looks like as follows
# +---------+------+------------+------------+--------------+----------------------+
# |prev |el_name |attr |value |next |
# |(Reference to |(Name of |(Hash of the|(String value)|Reference of array to |
# |the parent node)|the element)|atrributes) | |next child elements) |
# +----------------+------------+------------+--------------+----------------------+
# RETURN VALUE
# 0 if successfull, 1 on error
# EXAMPLE
# use xml2tree;
# .
# .
# my $pr = xml2tree->new();
# $pr->parseFile(dct5_release_note.xml);
# .
# .
# my $trees = $getNode('Document');
# SEE ALSO
# sbt/data//subcon/dct5_release_note.xml
# sbt/make_dct5_upload.pl
# sbt/lib/sbt_common.pm
# HISTORY
# Version : 0.1
# Date :
# Author : rownak
# TODO
# Case insensitive search
# Support non recursive search in case of attachment
#******
package xml2tree;
use strict;
use XML::Parser;
my %entities = ();
my %root;
my $parser;
my $curr_elem;
my $xml_file;
my $count=0;
#my @found_nodes;
#==========================================================
# Intialization Part
#
# Constructor. and others
#==========================================================
#usage();
#exit 0;
sub new{
$parser = new XML::Parser(ParseParamEnt => 1,Handlers =>
{Entity => \&entityHandler,
Char => \&charHandler,
Start => \&startHandler,
End => \&endHandler});
}
# Parse a given XML file.
sub parseFile{
my $file = shift;
$parser->parsefile($file);
}
#==========================================================
# Data Processing Part
#
#
#==========================================================
# XML::Parser uses this function when it finds an xml entity.
sub entityHandler{
my ($p, $name, $val) = @_;
eval {$entities{$name} = $val;};
die ("Error in handling xml characters " .
"at line $p->current_line") if $@;
}
# XML::Parser uses this function when it finds an xml element start entry.
sub startHandler{
my ($p, $element, %attr) = @_;
#Root of the tree
if($element eq ''){
return;
}
$p->{cdata_buffer}='';
#making a two way linked list of tree value
if(not defined($curr_elem)){
$root{'el_name'} = $element;
$root{'attr'} = \%attr;
$root{'prev'} = 0;
$root{'next'} = 0;
$root{'value'}= 0;
$curr_elem = \%root;
}else{
my %element;
$element{'el_name'} = $element;
$element{'attr'} = \%attr;
$element{'prev'} = $curr_elem;
$element{'next'} = 0;
$element{'value'}= 0;
if($curr_elem->{'next'} == 0){
my @child_element;
push(@child_element,\%element);
$curr_elem->{'next'}=\@child_element;
}
else{
my $child_element = $curr_elem->{'next'};
push(@$child_element,\%element);
$curr_elem->{'next'}=$child_element;
}
$curr_elem = \%element;
}
}
# XML::Parser uses this function when it finds an xml element end entry.
sub endHandler{
my($p) = @_;
$curr_elem->{'value'} = $p->{cdata_buffer} ;
$curr_elem = $curr_elem->{'prev'};
}
# XML::Parser uses this function when it finds an xml character entry.
sub charHandler{
my ($p, $str) = @_;
my $element;
my @context = $p->context;
$str =~ s/^\s+//;
$str =~ s/\s+$//;
if($str eq ""){
return;
}
$p->{cdata_buffer} .= $str;
}
##==========================================================
## External interface part
##
## This methods will be used by its client
##==========================================================
#
sub usage
{
print"\n\n";
print "Functionality: \n";
print"======================\n";
print "it parse any xml and convert the data into a two way trre like linked list \n\n";
print "use xml2tree\n";
print "\.\n";
print "\.\n";
print 'my $pr = xml2tree->new()'."\n";
print '$pr->parseFile(dct5_release_note.xml)'."\n";
print "\.\n";
print "\.\n";
print 'my $trees = $getNode(\'Document\')'."\n";
}
#Depending on the search criteria it returns array of nodes.
# Input is
# el_name => element name of searching node(compulsory)
# attributes => Hash input of attributes to be searched. It will retun true even if it is a subset.(optional)
# value => value of the element(optional)
#
#Output is
# Array of nodes that has been found . Each node has following type of structure
# +---------+------+------------+------------+--------------+----------------------+
# |prev |el_name |attr |value |next |
# |(Reference to |(Name of |(Hash of the|(String value)|Reference of array to |
# |the parent node)|the element)|atrributes) | |next child elements) |
# +----------------+------------+------------+--------------+----------------------+
sub getNode{
my $tmp_el_name = shift;
my $tmp_attr = shift;
my $tmp_value = shift;
my @found_nodes=();
if ($tmp_el_name eq '0' ){
return;
}
if(not defined $tmp_attr){$tmp_attr=0;}
if(not defined $tmp_value){$tmp_value=0;}
searchTree($root{'next'},$tmp_el_name,$tmp_attr, $tmp_value,\@found_nodes);
return \@found_nodes;
}
sub getPrevSibling{
my $curr_element = shift;
my $parent = $curr_element->{'prev'};
my $child_array = $parent->{'next'};
my $prevSibling=undef;
foreach my $tmp_sibling (@$child_array){
if ($tmp_sibling == $curr_element){
last;
}
}
return $prevSibling
}
sub getEntities{
return \%entities;
}
sub getRoot{
return \%root;
}
##==========================================================
## Private functions part
##
## This functions will be used by external interface part
##==========================================================
#
sub searchTree{
my $xml_sub_tree = shift;
my $tmp_el_name = shift;
my $tmp_attr = shift;
my $tmp_value = shift;
my $found_nodes_ref = shift;
my $is_attr_exist = 0;
my $is_value_exists=0;
my $is_el_name_exists=0;
if($xml_sub_tree eq '0'){return;}
foreach my $node (@$xml_sub_tree){
my $prev_node = $node->{'prev'};
my $tmp_attr_node= $node->{'attr'};
if($tmp_attr ne '0'){
$is_attr_exist = is_LsubsetR($tmp_attr,$tmp_attr_node);
}
if((($tmp_el_name eq '0')||(($tmp_el_name ne '0') && ($node->{'el_name'} eq $tmp_el_name))) &&
(($tmp_value eq '0')||(($tmp_value ne '0') && ($node->{'value'} eq $tmp_value)))&&
(($tmp_attr eq '0')||(($tmp_attr ne '0') && $is_attr_exist))){
push (@$found_nodes_ref, $node)
}
searchTree ($node->{'next'}, $tmp_el_name, $tmp_attr, $tmp_value, $found_nodes_ref);
}
}
sub is_LsubsetR(){
my $left_hash = shift;
my $right_hash = shift;
if ((keys(%$left_hash) == 0) || (keys(%$right_hash) ==0)){
return 0;
}
my $is_match=1;
my $is_match_single;
while(my($key, $value) = each(%$left_hash)){
$is_match_single=0;
while(my ($r_key,$r_value) = each(%$right_hash)){
if(($key eq $r_key ) && ($value eq $r_value)){
$is_match_single=1;
}
}
$is_match = $is_match & $is_match_single;
}
return $is_match;
}
1;