# This source code file is part of the "mod_survey" package. # # Copyright (C) 2004 eveca GmbH, Regensburg (ilse@eveca.de) # # 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 (probably in a file named "LICENSE.txt" or the like); # if not, write to: # # Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #!/usr/bin/perl package Survey::Template; use strict; use warnings; use Data::Dumper; # using numbers inside hashes should be faster, but nobody can understand the code than. # so define the numbers as constans. # When printing structures for debugging for example with Data::Dumper, # use constants below which contains strings instead of numbers use constant TYPE => 10; use constant VARIABLE => 11; use constant LOOPBEGIN => 12; use constant LOOPEND => 13; use constant TEXT => 15; use constant IF => 16; use constant ELSE => 17; use constant ENDIF => 18; use constant LEVEL => 20; use constant VALUE => 30; use constant CONTEXT => 40; use constant NAME => 50; use constant TEMPLATE => 60; use constant CURRENT_LEVEL => 70; use constant CURRENT_CONTEXT => 80; use constant VARIABLES => 90; use constant SEARCH_OFFSET => 100; use constant LOOP_RUNS => 110; use constant RAN_LOOP => 120; use constant FIXED_LOOP_RUNS => 130; use constant FIXED => 140; ############### # use this constants for debugging to easier understand what is inside datastructures #use constant TYPE => 'type'; #use constant VARIABLE => 'variable'; #use constant LOOPBEGIN => 'loopBegin'; #use constant LOOPEND => 'loopEnd'; #use constant TEXT => 'text'; #use constant IF => 'if'; #use constant ELSE => 'else'; #use constant ENDIF => 'endif'; #use constant LEVEL => 'level'; #use constant VALUE => 'value'; #use constant CONTEXT => 'context'; #use constant NAME => 'name'; #use constant TEMPLATE => 'template'; #use constant CURRENT_LEVEL => 'currentLevel'; #use constant CURRENT_CONTEXT => 'currentContext'; #use constant VARIABLES => 'variables'; #use constant SEARCH_OFFSET => 'searchOffset'; #use constant LOOP_RUNS => 'loopRuns'; #use constant RAN_LOOP => 'ranLoops'; #use constant FIXED_LOOP_RUNS => 'fixedLoopRuns'; #use constant FIXED => 'fixed'; ##################################### use constant NUMBER_OF_RUNS => 0; use constant RANDOMIZED => 1; sub new() { my $Object = shift; my $File = shift; my $this = {}; bless($this, $Object); $this->{Survey::Template::TEMPLATE} = [{}]; # contains the template as a datastructure $this->{Survey::Template::CURRENT_LEVEL} = 0; # current nesting level (loops) $this->{Survey::Template::CURRENT_CONTEXT} = ["global"]; # current context (loops) $this->{Survey::Template::VARIABLES} = {}; # contains variables and its values $this->{Survey::Template::SEARCH_OFFSET} = 0; # required for sub _findNextByType() $this->{Survey::Template::LOOP_RUNS} = {}; # in here is saved, how often a loop is initialized with "enterLoop" and how often "nextLoop" is called $this->{Survey::Template::RAN_LOOP} = {}; # saves what initialization of a loop is already run $this->{Survey::Template::FIXED_LOOP_RUNS} = {}; if ($File) { $this->_readFile($File); } return $this; } ####################################################### sub _readFile() { my $this = shift; my $File = shift; open(FIN, "<$File") or die $! . ": $File "; # create character array out of the filecontent my @characterArray = split(//, join("", )); close(FIN); # parse the character array and create a datastructure of the template $this->{Survey::Template::TEMPLATE} = $this->_parseArray(\@characterArray); # initialize variables to avoid errors of "use strict" (can't use undefined value as arrayref etc.) $this->_initVariables(); } ####################################################### sub _getNumberOfLoopInits() { my $this = shift; my $LoopName = shift; my $numberOfInits = 0; if ($LoopName ne "global") { $numberOfInits = scalar(@{ $this->{Survey::Template::LOOP_RUNS}->{$LoopName} }) - 1; } return $numberOfInits; } ####################################################### # set a variable to a value sub setVar() { my $this = shift; my $VarName = shift; my $VarValue = shift; # check if the variable is valid my $VariableExits = $this->_variableExits($VarName); if ($VariableExits < 0) { die "variable '$VarName' does not exist"; } # for easier usage my $vars = $this->{Survey::Template::VARIABLES}; # get the name of current context (global, or name of current loop) my $currentContext = $this->{Survey::Template::CURRENT_CONTEXT}->[scalar(@{ $this->{Survey::Template::CURRENT_CONTEXT} }) - 1]; my $numberOfInits = $this->_getNumberOfLoopInits($currentContext); if (!defined $vars->{$VarName}->{$currentContext}) { $vars->{$VarName}->{$currentContext} = []; } my $loopRuns = $this->{Survey::Template::LOOP_RUNS}; my $valueIndex = $loopRuns->{$currentContext}->[$numberOfInits]->[Survey::Template::NUMBER_OF_RUNS]; if (not defined $valueIndex) { $valueIndex = 0; } $vars->{$VarName}->{$currentContext}->[$numberOfInits]->[$valueIndex] = $VarValue; } ####################################################### # get the template as a string sub get() { my $this = shift; my $tmpl = $this->{Survey::Template::TEMPLATE}; # clear: how often a loop was run to enable to call this method more than once $this->{Survey::Template::RAN_LOOP} = {}; #print Dumper $this->{Survey::Template::VARIABLES}; #exit; # insert variables into the template datastructure $this->_setValues($tmpl); # create a string out of the template datastructure my $str = ""; for (my $i = 0 ; $i < scalar(@$tmpl) ; $i++) { $str .= $this->{Survey::Template::TEMPLATE}->[$i]->{Survey::Template::VALUE}; } return $str; } ####################################################### sub enterLoop() { my $this = shift; my $LoopName = shift; my $Random = shift; if (!defined $Random) { $Random = 0; } elsif ($Random eq "random") { $Random = 1; } else { $Random = 0; } # check if loop is existant my $loopIndex = $this->_loopExistsAtLevel($LoopName, $this->{Survey::Template::CURRENT_LEVEL}); # if ($loopIndex < 0) # {die "loop '$LoopName' does not exist at level '$this->{Survey::Template::CURRENT_LEVEL}'";} # increase current nesting level $this->{Survey::Template::CURRENT_LEVEL}++; # add loopname as current context push(@{ $this->{Survey::Template::CURRENT_CONTEXT} }, $LoopName); if (!defined $this->{Survey::Template::LOOP_RUNS}->{$LoopName}) { $this->{Survey::Template::LOOP_RUNS}->{$LoopName} = []; } push(@{ $this->{Survey::Template::LOOP_RUNS}->{$LoopName} }, [0, $Random]); } ####################################################### sub nextLoop() { my $this = shift; my $loopName = $this->{Survey::Template::CURRENT_CONTEXT}->[scalar(@{ $this->{Survey::Template::CURRENT_CONTEXT} }) - 1]; my $top = scalar(@{ $this->{Survey::Template::LOOP_RUNS}->{$loopName} }) - 1; # increase number of loop executions $this->{Survey::Template::LOOP_RUNS}->{$loopName}->[$top]->[Survey::Template::NUMBER_OF_RUNS]++; } ####################################################### sub exitLoop() { my $this = shift; # decrement current nesting level $this->{Survey::Template::CURRENT_LEVEL}--; # delete loopname from context pop(@{ $this->{Survey::Template::CURRENT_CONTEXT} }); } ####################################################### # keep elements at their position in random loops sub fixLoopRun() { my $this = shift; my $loopName = $this->{Survey::Template::CURRENT_CONTEXT}->[scalar(@{ $this->{Survey::Template::CURRENT_CONTEXT} }) - 1]; my $numberOfInits = $this->_getNumberOfLoopInits($loopName); my $top = scalar(@{ $this->{Survey::Template::LOOP_RUNS}->{$loopName} }) - 1; my $numberOfRuns = $this->{Survey::Template::LOOP_RUNS}->{$loopName}->[$top]->[Survey::Template::NUMBER_OF_RUNS]; if (not defined $this->{Survey::Template::FIXED_LOOP_RUNS}->{$loopName}) { $this->{Survey::Template::FIXED_LOOP_RUNS}->{$loopName} = []; } if (not defined $this->{Survey::Template::FIXED_LOOP_RUNS}->{$loopName}->[$numberOfInits]) { $this->{Survey::Template::FIXED_LOOP_RUNS}->{$loopName}->[$numberOfInits] = {}; } $this->{Survey::Template::FIXED_LOOP_RUNS}->{$loopName}->[$numberOfInits]->{$numberOfRuns} = Survey::Template::FIXED; } ####################################################### # parses the template (must be a character array) # and creates the template datastructure sub _parseArray() { my $this = shift; my $Tmpl = shift; my @stack; my $tagFlag = 0; my $exclamationMarkFlag = 0; my $hyphenFlag1 = 0; my $hyphenFlag2 = 0; # everything belongs to context "global" my @context = ("global"); # nesting level is 0 at the beginning my $level = 0; my $text = ""; # iterate over the whole template (character by character) for (my $i = 0 ; $i < scalar(@$Tmpl) ; $i++) { my $c = $Tmpl->[$i]; my $asdfx = "test"; # a tag starts if ($c eq '<') { $tagFlag = 1; } # last character was an opening tag-bracket elsif ($c eq '!' && $tagFlag) { $exclamationMarkFlag = 1; } # "_parseVariable($Tmpl, $i, \@stack, \@context, \$level, \$text); $tagFlag = 0; $exclamationMarkFlag = 0; $hyphenFlag1 = 0; $hyphenFlag2 = 0; } # a loop-begin or loop-end was found ("