#!/usr/bin/perl

#
# Module: vyatta-cfg
#
# **** License ****
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
# 
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# A copy of the GNU General Public License is available as
# `/usr/share/common-licenses/GPL' in the Debian GNU/Linux distribution
# or on the World Wide Web at `http://www.gnu.org/copyleft/gpl.html'.
# You can also obtain it by writing to the Free Software Foundation,
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
# This code was originally developed by Vyatta, Inc.
# Portions created by Vyatta are Copyright (C) 2005, 2006, 2007 Vyatta, Inc.
# All Rights Reserved.
#
# Author: Oleg Moskalenko
# Date: 2007
# Description:
#
# **** End License ****
#
#

package Vyatta::ConfigDOMTree;

use strict;

my %fields = (
	      _dir              => undef,
	      _name             => undef,
	      _value            => undef,
	      _subnodes         => undef
	      );

sub new {

  my $that = shift;
  my $dir = shift;
  my $name = shift;

  my $class = ref ($that) || $that;

  my $self = {
    %fields
  };

  bless $self, $class;

  $self->{_dir}  = $dir;
  $self->{_name} = $name;

  return $self->_construct_dom_tree();
}

#Simple DOM Tree iteration and screen output
#$1 - left screen offset (optional)
sub print {

    my $self = shift;
    my $level = shift;

    my $tree = $self;

    if(!(defined $level)) {
	$level="";
    }
    
    if(defined $tree) {

	print("$level name=",$tree->getNodeName(),"\n");

	my $value = $tree->getNodeValue();

	if(defined $value) {

	    print("$level value=$value\n");

	}
	
	my @subnodes = $tree->getSubNodes();
	
	while(@subnodes) {
	    
	    my $subnode = shift @subnodes;
	    $subnode->print($level . "  ");
	}
    }
}

#Return value of the tree node
sub getNodeValue {

    my $self = shift;
    my $tree = $self;
    
    my $ret = undef;
    
    if(defined $tree) {
	
	$ret = $tree->{_value};
    }
    
    return $ret;
}

#Return value of the tree node.
#If the value is nor defined, return empty string.
sub getNodeValueAsString {

    my $self = shift;
    my $tree = $self;
    
    my $ret = undef;
    
    if(defined $tree) {
	
	$ret = $tree->getNodeValue();
    }

    if(!defined $ret) {
	$ret = "";
    }
    
    return $ret;
}

#Return name of the tree node
sub getNodeName {

    my $self = shift;
    my $tree = $self;
    
    my $ret = undef;
    
    if(defined $tree) {
	
	$ret = $tree->{_name};
    }
    
    return $ret;
}

#Return array of subnodes of the tree node
sub getSubNodes {
    
    my $self = shift;
    my $tree = $self;
    
    my @ret = ();
    
    if(defined $tree) {
	
	my $subnodes = $tree->{_subnodes};
	
	if(defined $subnodes) {
	    
	    @ret = values %{$subnodes};
	    
	}
    }
    
    return @ret;
}

sub isLeafNode {
    
    my $self = shift;
    my $tree = $self;

    my $ret=undef;

    if(defined $tree) {

	if(defined $tree->{_value}) {

	    if(! defined $tree->{_subnodes}) {

		$ret="true";
	    }
	}
    }

    return $ret;
}

#Return subtree of the tree according to the path list
#$1, $2, ... - path to the subtree
sub getSubNode {
    
    my $self = shift;
    my $tree = $self;
    
    my $ret = undef;

    while(@_ && $tree) {
	
	my $subnode = shift (@_);
	
	my $subnodes = $tree->{_subnodes};
	
	if(defined $subnodes) {
	    
	    $tree = $subnodes->{$subnode};
	    
	} else {
	    
	    $tree = undef;
	    
	}
    }
    
    $ret=$tree;
    
    return $ret;
}

#Return value of the subnode of the tree according to the path list
#$1, $2, ... - path to the subtree
sub getSubNodeValue {
    
    my $self = shift;
    my $tree = $self;
    
    my $ret = undef;
    
    if(defined $tree) {
	
	my $node = $tree->getSubNode(@_);
	
	if(defined $node) {

	    $ret=$node->getNodeValue();
	}
    }
    
    return $ret;
}

#Return value of the subnode of the tree according to the path list.
#If the value is not defined, return empty string.
#$1, $2, ... - path to the subtree
sub getSubNodeValueAsString {
    
    my $self = shift;
    my $tree = $self;
    
    my $ret = undef;
    
    if(defined $tree) {
	
	my $node = $tree->getSubNode(@_);
	
	if(defined $node) {

	    $ret=$node->getNodeValue();
	}
    }

    if(! defined $ret) {
	$ret = "";
    }
    
    return $ret;
}

#Check if there is a subnode with the specified path.
#$1, $2, ... - path to the subtree
sub subNodeExist {
    
    my $self = shift;
    my $tree = $self;
    
    my $ret = undef;
    
    if(defined $tree) {
	
	my $node = $tree->getSubNode(@_);
	
	if(defined $node) {

	    $ret="true";
	}
    }
    
    return $ret;
}

#Return of the children of the node
#$1, $2, ... - path to the subtree
sub getSubNodesNumber {
    
    my $self = shift;
    my $tree = $self;
    
    my $ret = 0;
    
    if(defined $tree) {

	my $node = $tree->getSubNode(@_);
	
	if(defined $node) {

	    my @subs = $node->getSubNodes();

	    if(@subs) {
		$ret = $#subs + 1;
	    }
	}
    }
    
    return $ret;
}

#private method: costruct DOM Tree according to the absolute path provided
sub _construct_dom_tree {

    my $self = shift;

    my $subnodesNum=0;
    my $valuePresent=0;

    return unless defined $self;

    opendir my $dir, $self->{_dir} or return;
    my @entries = grep !/^\./, readdir $dir;
    closedir $dir;

    while(@entries) {

	my $entry = shift @entries;

	if($entry) {
	    my $fn = $self->{_dir} . "/" . $entry;
	    if( -f $fn) {
		if($entry eq "node.val") {
		    my $value=`cat $fn`;
		    while(chomp $value) {};
		    $self->{_value} = $value;
		    $valuePresent++;
		}
	    } elsif (-d $fn) {
		my $subnode = new Vyatta::ConfigDOMTree($fn,$entry);
		if(defined $subnode) {
		    if(! defined $self->{_subnodes} ) {
			$self->{_subnodes} = {};
		    }
		    $self->{_subnodes}->{$entry} = $subnode;
		    $subnodesNum++;
		}
	    }
	}
    }

    return if ( $valuePresent < 1 && $subnodesNum < 1 );
    
    return $self;
}

1;