#!/usr/bin/perl
package XmlExtractor;

use strict;
use XML::LibXML;
use XML::LibXML::XPathContext;

# includes
#require_once 'util/InputExtractor.php';


our $EC_BEG = 1;  # begin of subtree (current node descends) - in
our $EC_CTN = 2;  # continue with next sibling element - in / out: data of found element 
our $EC_END = 3;  # subtree completion (current node ascends); no data - out

sub new {
	my $self = { 
		doc => undef,            # whole document
		docNode => 0,            # root node
		currentNode => undef };  # current node
	
	bless $self, shift;
	return $self;
}

sub openInput {
	my $self = shift;
	my $input = shift;
	
	$self->{doc} = XML::LibXML->load_xml(location => $input);
	$self->{docNode} = $self->{doc}->documentElement();
	$self->{currentNode} = $self->{docNode};
}
	
sub toTop {
	my $self = shift;
	$self->{currentNode} = $self->{docNode};
}
	
sub toParent{
	my $self = shift;
	$self->{currentNode} = $self->{currentNode}->parentNode;
}
	
sub	extractElement {
	my $self = shift;
	my $extr_ctl = shift;
	
	# return parameters
	my $elem  = '';
	my $value = undef;
	my %attrs = ();
	
	# locals
	my $srh_node;
		
	if ($extr_ctl == $EC_BEG) {
		$srh_node = $self->{currentNode}->firstChild;
	} elsif ($extr_ctl == $EC_CTN) {
		$srh_node = $self->{currentNode}->nextSibling;
	} else {
		return ($EC_END, $elem, $value, %attrs);
	}

	# search for next element node
	while (defined $srh_node) {
		if ($srh_node->nodeType != XML::LibXML::XML_ELEMENT_NODE) {
			$srh_node = $srh_node->nextSibling;
		} else {
			last;
		}
	}
	
	if (defined $srh_node) {
		# element node found: extract tag name, value and attributes
		$self->{currentNode} = $srh_node;
		($elem, $value, %attrs) = $self->getCurrentNodeData;
		return ($EC_CTN, $elem, $value, %attrs)
	} else {
		# terminate / ascend subtree after child extraction
		if ($extr_ctl == $EC_CTN) {
			$self->{currentNode} = $self->{currentNode}->parentNode;
		}
		return ($EC_END, $elem, $value, %attrs);
	}
	
}
sub searchNode {
	my $self = shift;
	my $ns = shift;
	my $xpath = shift;
	
	my $xpc = 'XML::LibXML::XPathContext'->new($self->{doc});
	$xpc->registerNs(o => $ns);
	
	my $res = $xpc->findnodes("//o:$xpath");	
	
	return $res;
}
#sub setCurrentNode {
#	my $self = shift;
#	$self->{currentNode} = shift;
#}
sub gotoChild {
	my $self = shift;
	my $name = shift;
	# return 0/1, if child node could be found
	
	my ($ctl, $elem, $value, %attrs) = $self->extractElement($EC_BEG);
	while ($ctl ne $EC_END) {
		return 1 if ($elem eq $name);
		($ctl, $elem, $value, %attrs) = $self->extractElement($ctl);
	}
	return 0;
}
sub requireChild {
	my $self = shift;
	my $name = shift;
	# throws exception, if child not present
	
	die "Could not find node '$name'" unless $self->gotoChild($name);
}
sub getCurrentNodeData {
	my $self = shift;

	# return parameters
	my $elem  = '';
	my $value = undef;
	my %attrs = ();

	$elem = $self->{currentNode}->tagName;
	if (defined($self->{currentNode}->{nodeValue})) {
		$value = $self->{currentNode}->{nodeValue};
	} else {
		# search for CDATA or TEXT_NODE section
		
		############# TODO: Das Iterieren stimmt nicht!
		for my $childNode ($self->{currentNode}->childNodes()) {
			if ($childNode->nodeType == XML::LibXML::XML_CDATA_SECTION_NODE) {
				$value = $childNode->data;
			} elsif ($childNode->nodeType == XML::LibXML::XML_TEXT_NODE) {
				my $node_text = $childNode->data;
				if (length(chomp($node_text)) > 0 ) {
					$value .= $childNode->data;
				}
			}
		}
	}
	if (defined($self->{currentNode}->nodeValue)) { 
		$value = $self->{currentNode}->nodeValue;
	}
	for (my $att_idx = 0; $att_idx < $self->{currentNode}->attributes->length; $att_idx++) {
		my $att_node = $self->{currentNode}->attributes->item($att_idx);	
		$attrs{$att_node->name} = $att_node->value;
	}
	return ($elem, $value, %attrs);
	
}
1;

