Index: /main/trunk/greenstone2/perllib/buildcolutils.pm
===================================================================
--- /main/trunk/greenstone2/perllib/buildcolutils.pm	(revision 27304)
+++ /main/trunk/greenstone2/perllib/buildcolutils.pm	(revision 27304)
@@ -0,0 +1,762 @@
+###############################################################################
+#
+# buildcolutils.pm -- index and build the collection. The buildtime counterpart
+#                    of inexport.pl
+#
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 1999 New Zealand Digital Library Project
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+###############################################################################
+
+package buildcolutils;
+
+use colcfg;
+use dbutil;
+use util;
+use FileUtils;
+use scriptutil;
+use gsprintf;
+use printusage;
+use parse2;
+
+## @method new()
+#
+#  Parses up and validates the arguments to the build process before creating
+#  the appropriate build process to do the actual work
+#
+#  @note Added true incremental support - John Thompson, DL Consulting Ltd.
+#  @note There were several bugs regarding using directories other than
+#        "import" or "archives" during import and build quashed. - John
+#        Thompson, DL Consulting Ltd.
+#
+#  @param  $incremental If true indicates this build should not regenerate all
+#                       the index and metadata files, and should instead just
+#                       append the information found in the archives directory
+#                       to the existing files. If this requires some complex
+#                       work so as to correctly insert into a classifier so be
+#                       it. Of course none of this is done here - instead the
+#                       incremental argument is passed to the document
+#                       processor.
+#
+sub new
+{
+  my $class = shift(@_);
+  my ($argv, $options, $opt_listall_options) = @_;
+
+  my $self = {'builddir' => undef,
+              'buildtype' => undef,
+              'close_faillog' => 0,
+              'close_out' => 0,
+              'mode' => '',
+              'orthogonalbuildtypes' => undef,
+              'realbuilddir' => undef,
+              'textindex' => '',
+              'xml' => 0
+             };
+
+  # general options available to all plugins
+  my $arguments = $options->{'args'};
+  my $intArgLeftinAfterParsing = &parse2::parse($argv, $arguments, $self, "allow_extra_options");
+  # If parse returns -1 then something has gone wrong
+  if ($intArgLeftinAfterParsing == -1)
+  {
+    &PrintUsage::print_txt_usage($options, "{buildcol.params}");
+    die "\n";
+  }
+
+  # If $language has been specified, load the appropriate resource bundle
+  # (Otherwise, the default resource bundle will be loaded automatically)
+  if ($self->{'language'} && $self->{'language'} =~ /\S/)
+  {
+    &gsprintf::load_language_specific_resource_bundle($self->{'language'});
+  }
+
+  # Do we need 'listall' support in buildcol? If so, copy code from inexport
+  # later [jmt12]
+
+  # <insert explanation here>
+  if ($self->{'xml'})
+  {
+    &PrintUsage::print_xml_usage($options);
+    print "\n";
+    return bless($self, $class);
+  }
+
+  # the gli wants strings to be in UTF-8
+  if ($gli)
+  {
+    &gsprintf::output_strings_in_UTF8;
+  }
+
+  # now check that we had exactly one leftover arg, which should be
+  # the collection name. We don't want to do this earlier, cos
+  # -xml arg doesn't need a collection name
+  # Or if the user specified -h, then we output the usage also
+  if ($intArgLeftinAfterParsing != 1 || (@ARGV && $ARGV[0] =~ /^\-+h/))
+  {
+    &PrintUsage::print_txt_usage($options, "{buildcol.params}");
+    die "\n";
+  }
+
+  my $out = $self->{'out'};
+  if ($out !~ /^(STDERR|STDOUT)$/i)
+  {
+    open (OUT, ">$out") || (&gsprintf::gsprintf(STDERR, "{common.cannot_open_output_file}\n", $out) && die);
+    $out = "buildcolutils::OUT";
+    $self->{'close_out'} = 1;
+  }
+  $out->autoflush(1);
+  $self->{'out'} = $out;
+
+  # @ARGV should be only one item, the name of the collection
+  $self->{'collection'} = shift(@{$argv});
+
+  return bless($self, $class);
+}
+# new()
+
+# newCGI()?
+
+# @function get_collection
+#
+sub get_collection
+{
+  my $self = shift @_;
+  return $self->{'collection'};
+}
+# get_collection()
+
+# @function read_collection_cfg
+#
+sub read_collection_cfg
+{
+  my $self = shift(@_);
+  my ($collection, $options) = @_;
+
+  my $collectdir = $self->{'collectdir'};
+  my $site       = $self->{'site'};
+  my $out        = $self->{'out'};
+
+  # get and check the collection
+  if (($collection = &colcfg::use_collection($site, $collection, $collectdir)) eq "")
+  {
+    &PrintUsage::print_txt_usage($options, "{buildcol.params}");
+    die "\n";
+  }
+
+  # set gs_version 2/3
+  $self->{'gs_version'} = "2";
+  if ((defined $site) && ($site ne ""))
+  {
+    # gs3
+    $self->{'gs_version'} = "3";
+  }
+
+  # add collection's perllib dir into include path in case we have collection
+  # specific modules
+  &util::augmentINC(&FileUtils::filenameConcatenate($ENV{'GSDLCOLLECTDIR'}, 'perllib'));
+  &util::augmentINC(&FileUtils::filenameConcatenate($ENV{'GSDLCOLLECTDIR'}, 'perllib', 'classify'));
+  &util::augmentINC(&FileUtils::filenameConcatenate($ENV{'GSDLCOLLECTDIR'}, 'perllib', 'plugins'));
+
+  # check that we can open the faillog
+  my $faillog = $self->{'faillog'};
+  if ($faillog eq "")
+  {
+    $faillog = &FileUtils::filenameConcatenate($ENV{'GSDLCOLLECTDIR'}, "etc", "fail.log");
+  }
+  # note that we're appending to the faillog here (import.pl clears it each time)
+  # this could potentially create a situation where the faillog keeps being added
+  # to over multiple builds (if the import process is being skipped)
+  open (FAILLOG, ">>$faillog") || (&gsprintf::gsprintf(STDERR, "{common.cannot_open_fail_log}\n", $faillog) && die);
+  $faillog = 'buildcolutils::FAILLOG';
+  $faillog->autoflush(1);
+  $self->{'faillog'} = $faillog;
+  $self->{'faillogname'} = $faillog;
+  $self->{'close_faillog'} = 1;
+
+  # Read in the collection configuration file.
+  my $gs_mode = "gs".$self->{'gs_version'}; #gs2 or gs3
+  my $config_filename = &colcfg::get_collect_cfg_name($out, $gs_mode);
+  my $collect_cfg = &colcfg::read_collection_cfg($config_filename, $gs_mode);
+
+  return ($config_filename, $collect_cfg);
+}
+# read_collection_cfg()
+
+# @function set_collection_options
+# This function copies across values for arguments from the collection
+# configuration file if they are not already provided by the user, then
+# sets reasonable defaults for any required arguments that remains without
+# a value.
+sub set_collection_options
+{
+  my $self = shift @_;
+  my ($collectcfg) = @_;
+  my ($buildtype, $orthogonalbuildtypes);
+
+  # If the infodbtype value wasn't defined in the collect.cfg file, use the default
+  if (!defined($collectcfg->{'infodbtype'}))
+  {
+    $collectcfg->{'infodbtype'} = &dbutil::get_default_infodb_type();
+  }
+  # - just so I don't have to pass collectcfg around as well
+  $self->{'infodbtype'} = $collectcfg->{'infodbtype'};
+
+  if ($self->{'verbosity'} !~ /\d+/)
+  {
+    if (defined $collectcfg->{'verbosity'} && $collectcfg->{'verbosity'} =~ /\d+/)
+    {
+      $self->{'verbosity'} = $collectcfg->{'verbosity'};
+    }
+    else
+    {
+      $self->{'verbosity'} = 2; # the default
+    }
+  }
+
+  # we use searchtype for determining buildtype, but for old versions, use buildtype
+  if (defined $collectcfg->{'buildtype'})
+  {
+    $self->{'buildtype'} = $collectcfg->{'buildtype'};
+  }
+  elsif (defined $collectcfg->{'searchtypes'} || defined $collectcfg->{'searchtype'})
+  {
+    $self->{'buildtype'} = "mgpp";
+  }
+  else
+  {
+    $self->{'buildtype'} = "mg"; #mg is the default
+  }
+
+  if ($self->{'buildtype'} eq "mgpp" && defined $collectcfg->{'textcompress'})
+  {
+    $self->{'textindex'} = $collectcfg->{'textcompress'};
+  }
+
+  # is it okay to always clobber or possible remain undefined? [jmt12]
+  if (defined $collectcfg->{'orthogonalbuildtypes'})
+  {
+    $self->{'orthogonalbuildtypes'} = $collectcfg->{'orthogonalbuildtypes'};
+  }
+
+  if (defined $collectcfg->{'archivedir'} && $self->{'archivedir'} eq "")
+  {
+    $self->{'archivedir'} = $collectcfg->{'archivedir'};
+  }
+
+  if (defined $collectcfg->{'cachedir'} && $self->{'cachedir'} eq "")
+  {
+    $self->{'cachedir'} = $collectcfg->{'cachedir'};
+  }
+
+  if (defined $collectcfg->{'builddir'} && $self->{'builddir'} eq "")
+  {
+    $self->{'builddir'} = $collectcfg->{'builddir'};
+  }
+
+  if ($self->{'maxdocs'} !~ /\-?\d+/)
+  {
+    if (defined $collectcfg->{'maxdocs'} && $collectcfg->{'maxdocs'} =~ /\-?\d+/)
+    {
+      $self->{'maxdocs'} = $collectcfg->{'maxdocs'};
+    }
+    else
+    {
+      $self->{'maxdocs'} = -1; # the default
+    }
+  }
+
+  # always clobbers? [jmt12]
+  if (defined $collectcfg->{'maxnumeric'} && $collectcfg->{'maxnumeric'} =~ /\d+/)
+  {
+    $self->{'maxnumeric'} = $collectcfg->{'maxnumeric'};
+  }
+  if ($self->{'maxnumeric'} < 4 || $self->{'maxnumeric'} > 512)
+  {
+    $self->{'maxnumeric'} = 4;
+  }
+
+  if (defined $collectcfg->{'debug'} && $collectcfg->{'debug'} =~ /^true$/i)
+  {
+    $self->{'debug'} = 1;
+  }
+
+  if ($self->{'mode'} !~ /^(all|compress_text|build_index|infodb)$/)
+  {
+    if (defined $collectcfg->{'mode'} && $collectcfg->{'mode'} =~ /^(all|compress_text|build_index|infodb)$/)
+    {
+      $self->{'mode'} = $collectcfg->{'mode'};
+    }
+    else
+    {
+      $self->{'mode'} = "all"; # the default
+    }
+  }
+
+  # Presumably 'index' from the collect.cfg still works [jmt12]
+  if (defined $collectcfg->{'index'} && $self->{'indexname'} eq "")
+  {
+    $self->{'indexname'} = $collectcfg->{'index'};
+  }
+  # - 'index' from the command line doesn't make it through parsing so I
+  # renamed this option 'indexname' [jmt12]
+  if (defined $collectcfg->{'indexname'} && $self->{'indexname'} eq "")
+  {
+    $self->{'indexname'} = $collectcfg->{'indexname'};
+  }
+  # - we may also define the index level to build now [jmt12]
+  if (defined $collectcfg->{'indexlevel'} && $self->{'indexlevel'} eq "")
+  {
+    $self->{'indexlevel'} = $collectcfg->{'indexlevel'};
+  }
+
+  if (defined $collectcfg->{'no_text'} && $self->{'no_text'} == 0)
+  {
+    if ($collectcfg->{'no_text'} =~ /^true$/i)
+    {
+      $self->{'no_text'} = 1;
+    }
+  }
+
+  if (defined $collectcfg->{'no_strip_html'} && $self->{'no_strip_html'} == 0)
+  {
+    if ($collectcfg->{'no_strip_html'} =~ /^true$/i)
+    {
+      $self->{'no_strip_html'} = 1;
+    }
+  }
+
+  if (defined $collectcfg->{'store_metadata_coverage'} && $self->{'store_metadata_coverage'} == 0)
+  {
+    if ($collectcfg->{'store_metadata_coverage'} =~ /^true$/i)
+    {
+      $self->{'store_metadata_coverage'} = 1;
+    }
+  }
+
+  if (defined $collectcfg->{'remove_empty_classifications'} && $self->{'remove_empty_classifications'} == 0)
+  {
+    if ($collectcfg->{'remove_empty_classifications'} =~ /^true$/i)
+    {
+      $self->{'remove_empty_classifications'} = 1;
+    }
+  }
+
+  if (defined $collectcfg->{'gli'} && $collectcfg->{'gli'} =~ /^true$/i)
+  {
+    $self->{'gli'} = 1;
+  }
+  if (!defined $self->{'gli'})
+  {
+    $self->{'gli'} = 0;
+  }
+
+  if ($self->{'sections_index_document_metadata'} !~ /\S/ && defined $collectcfg->{'sections_index_document_metadata'})
+  {
+    $self->{'sections_index_document_metadata'} = $collectcfg->{'sections_index_document_metadata'};
+  }
+
+  if ($self->{'sections_index_document_metadata'} !~ /^(never|always|unless_section_metadata_exists)$/) {
+    $self->{'sections_index_document_metadata'} = 'never';
+  }
+
+  my ($removeold, $keepold, $incremental, $incremental_mode)
+      = &scriptutil::check_removeold_and_keepold($self->{'removeold'}, $self->{'keepold'},
+                                                 $self->{'incremental'}, 'building',
+                                                 $collectcfg);
+  $self->{'removeold'}        = $removeold;
+  $self->{'keepold'}          = $keepold;
+  $self->{'incremental'}      = $incremental;
+  $self->{'incremental_mode'} = $incremental_mode;
+
+  # New argument to track whether build is incremental
+  if (!defined $self->{'incremental'})
+  {
+    $self->{'incremental'} = 0;
+  }
+
+  #set the text index
+  if (($self->{'buildtype'} eq 'mgpp') || ($self->{'buildtype'} eq 'lucene') || ($self->{'buildtype'} eq 'solr'))
+  {
+    if ($self->{'textindex'} eq '')
+    {
+      $self->{'textindex'} = 'text';
+    }
+  }
+  else
+  {
+    $self->{'textindex'} = 'section:text';
+  }
+}
+# set_collection_options()
+
+# @function prepare_builders
+#
+sub prepare_builders
+{
+  my $self = shift @_;
+  my ($config_filename,$collectcfg) = @_;
+
+  my $archivedir  = $self->{'archivedir'};
+  my $builddir    = $self->{'builddir'};
+  my $buildtype   = $self->{'buildtype'};
+  my $cachedir    = $self->{'cachedir'};
+  my $collectdir  = $self->{'collectdir'};
+  my $collection  = $self->{'collection'};
+  my $debug       = $self->{'debug'};
+  my $faillog     = $self->{'faillog'};
+  my $gli         = $self->{'gli'};
+  my $incremental = $self->{'incremental'};
+  my $incremental_mode = $self->{'incremental_mode'};
+  my $keepold     = $self->{'keepold'};
+  my $maxdocs     = $self->{'maxdocs'};
+  my $maxnumeric  = $self->{'maxnumeric'};
+  my $no_strip_html = $self->{'no_strip_html'};
+  my $no_text     = $self->{'no_text'};
+  my $orthogonalbuildtypes = $self->{'orthogonalbuildtypes'};
+  my $out         = $self->{'out'};
+  my $remove_empty_classifications = $self->{'remove_empty_classifications'};
+  my $sections_index_document_metadata = $self->{'sections_index_document_metadata'};
+  my $site        = $self->{'site'};
+  my $store_metadata_coverage = $self->{'store_metadata_coverage'};
+  my $verbosity   = $self->{'verbosity'};
+
+  if ($gli)
+  {
+    print STDERR "<Build>\n";
+  }
+
+  # fill in the default archives and building directories if none
+  # were supplied, turn all \ into / and remove trailing /
+
+  my ($realarchivedir, $realbuilddir);
+  # Modified so that the archivedir, if provided as an argument, is made
+  # absolute if it isn't already
+  if ($archivedir eq "")
+  {
+    $archivedir = &FileUtils::filenameConcatenate($ENV{'GSDLCOLLECTDIR'}, "archives");
+  }
+  else
+  {
+    $archivedir = &util::make_absolute($ENV{'GSDLCOLLECTDIR'}, $archivedir);
+  }
+  # End Mod
+  $archivedir =~ s/[\\\/]+/\//g;
+  $archivedir =~ s/\/$//;
+
+  if ($builddir eq "")
+  {
+    $builddir = &FileUtils::filenameConcatenate($ENV{'GSDLCOLLECTDIR'}, 'building');
+    if ($incremental)
+    {
+      &gsprintf::gsprintf($out, "{buildcol.incremental_default_builddir}\n");
+    }
+  }
+  $builddir =~ s/[\\\/]+/\//g;
+  $builddir =~ s/\/$//;
+  $self->{'builddir'} = $builddir;
+
+  # update the archive cache if needed
+  if ($cachedir)
+  {
+    if ($verbosity >= 1)
+    {
+      &gsprintf::gsprintf($out, "{buildcol.updating_archive_cache}\n")
+    }
+
+    $cachedir =~ s/[\\\/]+$//;
+    if ($cachedir !~ /collect[\/\\]$collection/)
+    {
+      $cachedir = &FileUtils::filenameConcatenate($cachedir, 'collect', $collection);
+    }
+
+    $realarchivedir = &FileUtils::filenameConcatenate($cachedir, 'archives');
+    $realbuilddir = &FileUtils::filenameConcatenate($cachedir, 'building');
+    &FileUtils::makeAllDirectories($realarchivedir);
+    &FileUtils::makeAllDirectories($realbuilddir);
+    &util::cachedir($archivedir, $realarchivedir, $verbosity);
+  }
+  else
+  {
+    $realarchivedir = $archivedir;
+    $realbuilddir = $builddir;
+  }
+  $self->{'realbuilddir'} = $realbuilddir;
+
+  # build it in realbuilddir
+  &FileUtils::makeAllDirectories($realbuilddir);
+
+  my ($buildertype, $builderdir,  $builder);
+  # if a builder class has been created for this collection, use it
+  # otherwise, use the mg or mgpp builder
+  if (-e "$ENV{'GSDLCOLLECTDIR'}/custom/${collection}/perllib/custombuilder.pm")
+  {
+    $builderdir = "$ENV{'GSDLCOLLECTDIR'}/custom/${collection}/perllib";
+    $buildertype = "custombuilder";
+  }
+  elsif (-e "$ENV{'GSDLCOLLECTDIR'}/perllib/custombuilder.pm")
+  {
+    $builderdir = "$ENV{'GSDLCOLLECTDIR'}/perllib";
+    $buildertype = "custombuilder";
+  }
+  elsif (-e "$ENV{'GSDLCOLLECTDIR'}/perllib/${collection}builder.pm")
+  {
+    $builderdir = "$ENV{'GSDLCOLLECTDIR'}/perllib";
+    $buildertype = $collection . 'builder';
+  }
+  else
+  {
+    $builderdir = undef;
+    if ($buildtype ne '')
+    {
+      # caters for extension-based build types, such as 'solr'
+      $buildertype = $buildtype . 'builder';
+    }
+    else
+    {
+      # Default to mgpp
+      $buildertype = 'mgppbuilder';
+    }
+  }
+  # check for extension specific builders
+  # (that will then be run after main builder.pm
+  my @builderdir_list = ($builderdir);
+  my @buildertype_list = ($buildertype);
+
+  if (defined $orthogonalbuildtypes)
+  {
+    foreach my $obt (@$orthogonalbuildtypes)
+    {
+      push(@builderdir_list,undef); # rely on @INC to find it
+      push(@buildertype_list,$obt."Builder");
+    }
+  }
+
+  # Set up array of the main builder.pm, followed by any ones
+  # from the extension folders
+
+  my $num_builders = scalar(@buildertype_list);
+  my @builders = ();
+
+  for (my $i=0; $i<$num_builders; $i++)
+  {
+    my $this_builder;
+    my $this_buildertype = $buildertype_list[$i];
+    my $this_builderdir  = $builderdir_list[$i];
+
+    if ((defined $this_builderdir) && ($this_builderdir ne ""))
+    {
+      require "$this_builderdir/$this_buildertype.pm";
+    }
+    else
+    {
+      require "$this_buildertype.pm";
+    }
+
+    eval("\$this_builder = new $this_buildertype(\$site, \$collection, " .
+         "\$realarchivedir, \$realbuilddir, \$verbosity, " .
+         "\$maxdocs, \$debug, \$keepold, \$incremental, \$incremental_mode, " .
+         "\$remove_empty_classifications, " .
+         "\$out, \$no_text, \$faillog, \$gli)");
+    die "$@" if $@;
+
+    push(@builders,$this_builder);
+  }
+
+  # Init phase for builders
+  for (my $i=0; $i<$num_builders; $i++)
+  {
+    my $this_buildertype = $buildertype_list[$i];
+    my $this_builderdir  = $builderdir_list[$i];
+    my $this_builder     = $builders[$i];
+
+    $this_builder->init();
+    $this_builder->set_maxnumeric($maxnumeric);
+
+    if (($this_buildertype eq "mgppbuilder") && $no_strip_html)
+    {
+      $this_builder->set_strip_html(0);
+    }
+
+    if ($sections_index_document_metadata ne "never")
+    {
+      $this_builder->set_sections_index_document_metadata($sections_index_document_metadata);
+    }
+
+    if ($store_metadata_coverage)
+    {
+      $this_builder->set_store_metadata_coverage(1);
+    }
+  }
+  return \@builders;
+}
+
+sub build_collection
+{
+  my $self = shift(@_);
+  my @builders = @{shift(@_)};
+
+  my $indexlevel  = $self->{'indexlevel'};
+  my $indexname   = $self->{'indexname'};
+  my $mode        = $self->{'mode'};
+  my $textindex   = $self->{'textindex'};
+
+  # Run the requested passes
+  if ($mode =~ /^all$/i)
+  {
+    # 'map' modifies the elements of the original array, so calling
+    # methods -- as done below -- will cause (by default) @builders
+    # to be changed to whatever these functions return (which is *not*
+    # what we want -- we want to leave the values unchanged)
+    # => Use 'local' (dynamic scoping) to give each 'map' call its
+    #    own local copy This could also be done with:
+    #      (my $new =$_)->method(); $new
+    #    but is a bit more cumbersome to write
+    map { local $_=$_; $_->compress_text($textindex); } @builders;
+    # - we pass the required indexname and indexlevel (if specified) to the
+    #   processor [jmt12]
+    map { local $_=$_; $_->build_indexes($indexname, $indexlevel); } @builders;
+    map { local $_=$_; $_->make_infodatabase(); }  @builders;
+    map { local $_=$_; $_->collect_specific(); } @builders;
+  }
+  elsif ($mode =~ /^compress_text$/i)
+  {
+    map { local $_=$_; $_->compress_text($textindex); } @builders;
+  }
+  elsif ($mode =~ /^build_index$/i)
+  {
+    map { local $_=$_; $_->build_indexes($indexname, $indexlevel); } @builders;
+  }
+  elsif ($mode =~ /^infodb$/i)
+  {
+    map { local $_=$_; $_->make_infodatabase(); } @builders;
+  }
+  else
+  {
+    (&gsprintf::gsprintf(STDERR, "{buildcol.unknown_mode}\n", $mode) && die);
+  }
+}
+# build_collection()
+
+# @function build_auxiliary_files
+#
+sub build_auxiliary_files
+{
+  my $self = shift(@_);
+  my @builders = @{shift(@_)};
+  if (!$self->{'debug'})
+  {
+    map {local $_=$_; $_->make_auxiliary_files(); } @builders;
+  }
+}
+# build_auxiliary_files()
+
+# @function complete_builders
+#
+sub complete_builders
+{
+  my $self = shift(@_);
+  my @builders = @{shift(@_)};
+
+  map {local $_=$_; $_->deinit(); } @builders;
+
+  if (($self->{'realbuilddir'} ne $self->{'builddir'}) && !$self->{'debug'})
+  {
+    if ($self->{'verbosity'} >= 1)
+    {
+      &gsprintf::gsprintf($out, "{buildcol.copying_back_cached_build}\n");
+    }
+    &util::rm_r($self->{'builddir'});
+    &util::cp_r($self->{'realbuilddir'}, $self->{'builddir'});
+  }
+
+  # for RSS support: Need rss-items.rdf file in index folder
+  #  check if a file called rss-items.rdf exists in archives, then copy it into the building folder
+  #  so that when building is moved to index, this file will then also be in index as desired
+  my $collection_dir = &util::resolve_collection_dir($self->{'collectdir'},
+                                                     $self->{'collection'},
+                                                     $self->{'site'});
+  my $rss_items_rdf_file = &FileUtils::filenameConcatenate($self->{'archivedir'}, 'rss-items.rdf');
+  # @todo FileUtils
+  if(defined $self->{'builddir'} && -d $self->{'builddir'} && -f $rss_items_rdf_file)
+  {
+    if ($verbosity >= 1)
+    {
+      &gsprintf::gsprintf($self->{'out'}, "{buildcol.copying_rss_items_rdf}\n");
+    }
+    &util::cp($rss_items_rdf_file, $self->{'builddir'});
+  }
+
+  if ($self->{'gli'})
+  {
+    print STDERR "</Build>\n";
+  }
+}
+# complete_builders()
+
+# @function activate_collection
+#
+sub activate_collection
+{
+  my $self = shift(@_);
+  # if buildcol.pl was run with -activate, need to run activate.pl
+  # now that building's complete
+  if ($self->{'activate'})
+  {
+    #my $quoted_argv = join(" ", map { "\"$_\"" } @ARGV);
+    my @activate_argv = ();
+    push(@activate_argv, '-collectdir', $self->{'collectdir'}) if ($self->{'collectdir'});
+    push(@activate_argv, '-builddir', $self->{'builddir'}) if ($self->{'builddir'});
+    push(@activate_argv, '-site', $self->{'site'}) if ($self->{'site'});
+    push(@activate_argv, '-verbosity', $self->{'verbosity'}) if ($self->{'verbosity'});
+    push(@activate_argv, '-removeold') if ($self->{'removeold'});
+    push(@activate_argv, '-keepold') if ($self->{'keepold'});
+    push(@activate_argv, '-incremental') if ($self->{'incremental'});
+    my $quoted_argv = join(' ', map { "\"$_\"" } @activate_argv);
+    my $activatecol_cmd = '"' . &util::get_perl_exec(). '" -S activate.pl ' . $quoted_argv . ' "' . $collection . '"';
+    my $activatecol_status = system($activatecol_cmd)/256;
+
+    if ($activatecol_status != 0)
+    {
+      print STDERR "Error: Failed to run: $activatecol_cmd\n";
+      print STDERR "       $!\n" if ($! ne '');
+      exit(-1);
+    }
+  }
+}
+
+# @function deinit()
+#
+sub deinit
+{
+  my $self = shift(@_);
+
+  if ($self->{'close_out'})
+  {
+    close OUT;
+  }
+  if ($self->{'close_faillog'})
+  {
+    close FAILLOG;
+  }
+}
+# deinit()
+
+1;
