--- /dev/null
+# Copyright (C) 2002 Stanislav Sinyagin
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+
+# $Id: ConfigBuilder.pm,v 1.1 2010-12-27 00:03:40 ivan Exp $
+# Stanislav Sinyagin <ssinyagin@yahoo.com>
+
+# XML configuration builder
+
+package Torrus::ConfigBuilder;
+
+use strict;
+use XML::LibXML;
+use IO::File;
+
+use Torrus::Log;
+
+sub new
+{
+ my $self = {};
+ my $class = shift;
+ bless $self, $class;
+
+ my $doc = XML::LibXML->createDocument( "1.0", "UTF-8" );
+ my $root = $doc->createElement('configuration');
+ $doc->setDocumentElement( $root );
+ $self->{'doc'} = $doc;
+ $self->{'docroot'} = $root;
+
+ $root->appendChild($doc->createComment('DO NOT EDIT THIS FILE'));
+
+ my $dsnode = $doc->createElement('datasources');
+ $self->{'docroot'}->appendChild( $dsnode );
+ $self->{'datasources'} = $dsnode;
+
+ $self->{'required_templates'} = {};
+
+ $self->{'statistics'} = {};
+
+ $self->{'registry_overlays'} = [];
+
+ return $self;
+}
+
+
+sub setRegistryOverlays
+{
+ my $self = shift;
+
+ $self->{'registry_overlays'} = [];
+ push( @{$self->{'registry_overlays'}}, @_ );
+}
+
+
+sub lookupRegistry
+{
+ my $self = shift;
+ my $template = shift;
+
+ my $ret = undef;
+
+ foreach my $regOverlay ( @{$self->{'registry_overlays'}} )
+ {
+ if( defined( $regOverlay->{$template} ) )
+ {
+ $ret = $regOverlay->{$template};
+ }
+ }
+
+ if( not defined( $ret ) and
+ defined( $Torrus::ConfigBuilder::templateRegistry{$template} ) )
+ {
+ $ret = $Torrus::ConfigBuilder::templateRegistry{$template};
+ }
+
+ if( not defined( $ret ) )
+ {
+ if( scalar( %Torrus::ConfigBuilder::templateRegistry ) > 0 )
+ {
+ Warn('Template ' . $template .
+ ' is not listed in ConfigBuilder template registry');
+ }
+ }
+
+ return $ret;
+}
+
+
+
+
+sub addCreatorInfo
+{
+ my $self = shift;
+ my $creatorInfo = shift;
+
+ my $creatorNode = $self->{'doc'}->createElement('creator-info');
+ $creatorNode->appendText( $creatorInfo );
+ $self->{'docroot'}->insertBefore( $creatorNode, $self->{'datasources'} );
+}
+
+
+sub addRequiredFiles
+{
+ my $self = shift;
+
+ foreach my $file ( $self->requiredFiles() )
+ {
+ $self->addFileInclusion( $file );
+ }
+}
+
+
+sub addFileInclusion
+{
+ my $self = shift;
+ my $file = shift;
+
+ my $node = $self->{'doc'}->createElement('include');
+ $node->setAttribute( 'filename', $file );
+ $self->{'docroot'}->insertBefore( $node, $self->{'datasources'} );
+}
+
+
+sub startDefinitions
+{
+ my $self = shift;
+
+ my $node = $self->{'doc'}->createElement('definitions');
+ $self->{'docroot'}->insertBefore( $node, $self->{'datasources'} );
+ return $node;
+}
+
+
+sub addDefinition
+{
+ my $self = shift;
+ my $definitionsNode = shift;;
+ my $name = shift;
+ my $value = shift;
+
+ my $node = $self->{'doc'}->createElement('def');
+ $node->setAttribute( 'name', $name );
+ $node->setAttribute( 'value', $value );
+ $definitionsNode->appendChild( $node );
+}
+
+
+sub startParamProps
+{
+ my $self = shift;
+
+ my $node = $self->{'doc'}->createElement('param-properties');
+ $self->{'docroot'}->insertBefore( $node, $self->{'datasources'} );
+ return $node;
+}
+
+
+sub addParamProp
+{
+ my $self = shift;
+ my $propsNode = shift;;
+ my $param = shift;
+ my $prop = shift;
+ my $value = shift;
+
+ my $node = $self->{'doc'}->createElement('prop');
+ $node->setAttribute( 'param', $param );
+ $node->setAttribute( 'prop', $prop );
+ $node->setAttribute( 'value', $value );
+ $propsNode->appendChild( $node );
+}
+
+
+
+sub addSubtree
+{
+ my $self = shift;
+ my $parentNode = shift;
+ my $subtreeName = shift;
+ my $params = shift; # hash reference with param name-value pairs
+ my $templates = shift; # array reference with template names
+
+ return $self->addChildElement( 0, $parentNode, $subtreeName,
+ $params, $templates );
+}
+
+
+sub addLeaf
+{
+ my $self = shift;
+ my $parentNode = shift;
+ my $leafName = shift;
+ my $params = shift; # hash reference with param name-value pairs
+ my $templates = shift; # array reference with template names
+
+ return $self->addChildElement( 1, $parentNode, $leafName,
+ $params, $templates );
+}
+
+
+sub addChildElement
+{
+ my $self = shift;
+ my $isLeaf = shift;
+ my $parentNode = shift;
+ my $childName = shift;
+ my $params = shift;
+ my $templates = shift;
+
+ my $doc = $self->{'doc'};
+
+ if( not ref( $parentNode ) )
+ {
+ $parentNode = $self->{'datasources'};
+ }
+
+ my $childNode = $doc->createElement( $isLeaf ? 'leaf' : 'subtree' );
+ $childNode->setAttribute( 'name', $childName );
+ $childNode = $parentNode->appendChild( $childNode );
+
+ if( ref( $templates ) )
+ {
+ foreach my $tmpl ( sort @{$templates} )
+ {
+ $self->addTemplateApplication( $childNode, $tmpl );
+ }
+ }
+
+ $self->addParams( $childNode, $params );
+
+ return $childNode;
+}
+
+
+sub getChildSubtree
+{
+ my $self = shift;
+ my $parentNode = shift;
+ my $childName = shift;
+
+ if( not ref( $parentNode ) )
+ {
+ $parentNode = $self->{'datasources'};
+ }
+
+ my @subtrees =
+ $parentNode->findnodes( 'subtree[@name="' . $childName . '"]' );
+ if( not @subtrees )
+ {
+ Error('Cannot find subtree named ' . $childName);
+ return undef;
+ }
+ return $subtrees[0];
+}
+
+
+# Reconstruct the path to the given subtree or leaf
+sub getElementPath
+{
+ my $self = shift;
+ my $node = shift;
+
+ my $path = '';
+ if( $node->nodeName() eq 'subtree' )
+ {
+ $path = '/';
+ }
+
+ while( not $node->isSameNode( $self->{'datasources'} ) )
+ {
+ $path = '/' . $node->getAttribute( 'name' ) . $path;
+ $node = $node->parentNode();
+ }
+
+ return $path;
+}
+
+
+sub getTopSubtree
+{
+ my $self = shift;
+ return $self->{'datasources'};
+}
+
+
+sub addTemplateApplication
+{
+ my $self = shift;
+ my $parentNode = shift;
+ my $template = shift;
+
+ if( not ref( $parentNode ) )
+ {
+ $parentNode = $self->{'datasources'};
+ }
+
+ my $found = 0;
+
+ my $reg = $self->lookupRegistry( $template );
+ if( defined( $reg ) )
+ {
+ $self->{'required_templates'}{$template} = 1;
+ my $name = $reg->{'name'};
+ if( defined( $name ) )
+ {
+ $template = $name;
+ }
+ }
+
+ my $tmplNode = $self->{'doc'}->createElement( 'apply-template' );
+ $tmplNode->setAttribute( 'name', $template );
+ $parentNode->appendChild( $tmplNode );
+}
+
+
+sub addParams
+{
+ my $self = shift;
+ my $parentNode = shift;
+ my $params = shift;
+
+ if( ref( $params ) )
+ {
+ foreach my $paramName ( sort keys %{$params} )
+ {
+ $self->addParam( $parentNode, $paramName, $params->{$paramName} );
+ }
+ }
+}
+
+
+sub addParam
+{
+ my $self = shift;
+ my $parentNode = shift;
+ my $param = shift;
+ my $value = shift;
+
+ if( not ref( $parentNode ) )
+ {
+ $parentNode = $self->{'datasources'};
+ }
+
+ my $paramNode = $self->{'doc'}->createElement( 'param' );
+ $paramNode->setAttribute( 'name', $param );
+ $paramNode->setAttribute( 'value', $value );
+ $parentNode->appendChild( $paramNode );
+}
+
+
+sub addAlias
+{
+ my $self = shift;
+ my $parentNode = shift;
+ my $aliasPath = shift;
+
+ if( not ref( $parentNode ) ) # I hope nobody would need this
+ {
+ $parentNode = $self->{'datasources'};
+ }
+
+ my $aliasNode = $self->{'doc'}->createElement( 'alias' );
+ $aliasNode->appendText( $aliasPath );
+ $parentNode->appendChild( $aliasNode );
+}
+
+
+sub setVar
+{
+ my $self = shift;
+ my $parentNode = shift;
+ my $name = shift;
+ my $value = shift;
+
+ my $setvarNode = $self->{'doc'}->createElement( 'setvar' );
+ $setvarNode->setAttribute( 'name', $name );
+ $setvarNode->setAttribute( 'value', $value );
+ $parentNode->appendChild( $setvarNode );
+}
+
+
+
+sub startMonitors
+{
+ my $self = shift;
+
+ my $node = $self->{'doc'}->createElement('monitors');
+ $self->{'docroot'}->appendChild( $node );
+ return $node;
+}
+
+
+sub addMonitorAction
+{
+ my $self = shift;
+ my $monitorsNode = shift;;
+ my $name = shift;
+ my $params = shift;
+
+ my $node = $self->{'doc'}->createElement('action');
+ $node->setAttribute( 'name', $name );
+ $monitorsNode->appendChild( $node );
+
+ $self->addParams( $node, $params );
+}
+
+
+sub addMonitor
+{
+ my $self = shift;
+ my $monitorsNode = shift;;
+ my $name = shift;
+ my $params = shift;
+
+ my $node = $self->{'doc'}->createElement('monitor');
+ $node->setAttribute( 'name', $name );
+ $monitorsNode->appendChild( $node );
+
+ $self->addParams( $node, $params );
+}
+
+
+sub startTokensets
+{
+ my $self = shift;
+
+ my $node = $self->{'doc'}->createElement('token-sets');
+ $self->{'docroot'}->appendChild( $node );
+ return $node;
+}
+
+
+sub addTokenset
+{
+ my $self = shift;
+ my $tsetsNode = shift;;
+ my $name = shift;
+ my $params = shift;
+
+ my $node = $self->{'doc'}->createElement('token-set');
+ $node->setAttribute( 'name', $name );
+ $tsetsNode->appendChild( $node );
+
+ $self->addParams( $node, $params );
+}
+
+
+sub addStatistics
+{
+ my $self = shift;
+
+ foreach my $stats ( sort keys %{$self->{'statistics'}} )
+ {
+ my $node = $self->{'doc'}->createElement('configbuilder-statistics');
+ $node->setAttribute( 'category', $stats );
+ $node->setAttribute( 'value', $self->{'statistics'}{$stats} );
+ $self->{'docroot'}->appendChild( $node );
+ }
+}
+
+
+
+sub requiredFiles
+{
+ my $self = shift;
+
+ my %files;
+ foreach my $template ( keys %{$self->{'required_templates'}} )
+ {
+ my $file;
+ my $reg = $self->lookupRegistry( $template );
+ if( defined( $reg ) )
+ {
+ $file = $reg->{'source'};
+ }
+
+ if( defined( $file ) )
+ {
+ $files{$file} = 1;
+ }
+ else
+ {
+ Error('Source file is not defined for template ' . $template .
+ ' in ConfigBuilder template registry');
+ }
+ }
+ return( sort keys %files );
+}
+
+
+
+sub toFile
+{
+ my $self = shift;
+ my $filename = shift;
+
+ my $fh = new IO::File('> ' . $filename);
+ if( defined( $fh ) )
+ {
+ my $ok = $self->{'doc'}->toFH( $fh, 2 );
+ $fh->close();
+ return $ok;
+ }
+ else
+ {
+ return undef;
+ }
+}
+
+1;
+
+
+# Local Variables:
+# mode: perl
+# indent-tabs-mode: nil
+# perl-indent-level: 4
+# End: