Index: /gsdl/trunk/perllib/buildConfigxml.pm
===================================================================
--- /gsdl/trunk/perllib/buildConfigxml.pm	(revision 20094)
+++ /gsdl/trunk/perllib/buildConfigxml.pm	(revision 20094)
@@ -0,0 +1,801 @@
+###########################################################################
+#
+# cfgread4gs3.pm --
+# 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.
+#
+###########################################################################
+
+# reads in configuration files of xml form
+
+package cfgread4gs3;
+use strict;
+no strict 'refs';
+no strict 'subs';
+
+# Wrapper that ensures the right version of XML::Parser is loaded given
+# the version of Perl being used.  Need to distinguish between Perl 5.6 and
+# Perl 5.8
+sub BEGIN {
+    my $perl_dir;
+
+    # Note: $] encodes the version number of perl
+    if ($]>5.008) { 
+	# perl 5.8.1 or above
+	$perl_dir = "perl-5.8";
+    }
+    elsif ($]<5.008) {
+	# assume perl 5.6
+	$perl_dir = "perl-5.6";
+    }
+    else {
+	print STDERR "Warning: Perl 5.8.0 is not a maintained release.\n";
+	print STDERR "         Please upgrade to a newer version of Perl.\n";
+	$perl_dir = "perl-5.8";
+    }
+
+    if ($ENV{'GSDLOS'} !~ /^windows$/i) {
+	# Use push to put this on the end, so an existing XML::Parser will be used by default
+	push (@INC, "$ENV{'GSDLHOME'}/perllib/cpan/$perl_dir");
+    }
+}
+
+use XML::Parser;
+
+# A mapping hash to resolve name discrepancy between gs2 and gs3.
+my $nameMap = {"key" => "value",
+	       "creator" => "creator",
+	       "maintainer" => "maintainer",
+	       "public" => "public",
+	       "infodb" => "infodbtype",
+	       "defaultIndex" => "defaultindex",
+	       "defaultLevel" => "defaultlevel",
+	       "name" => "collectionname",
+	       "description" => "collectionextra",
+	       "smallicon" => "iconcollectionsmall",
+	       "icon" => "iconcollection",
+	       "level" => "levels",
+	       "classifier" => "classify",
+	       "indexSubcollection" => "indexsubcollections",
+	       "indexLanguage" => "languages",
+	       "defaultIndexLanguage" => "defaultlanguage",
+	       "index" => "indexes",
+	       "plugin" => "plugin",
+	       "plugout" => "plugout",
+	       "indexOption" => "indexoptions",
+	       "searchType" => "searchtype",
+	       "languageMetadata" => "languagemetadata",
+	       "buildType" => "buildtype"
+	       };
+# A hash structure which is returned by sub read_cfg_file.
+my $data = {};
+
+my $repeatedBlock = q/^(browse|pluginList)$/;
+
+# use those unique attribute values to locate the text within the elements
+# creator, public, maintainer and within a displayItem.
+my $currentLocation = "";
+my $stringexp = q/^(creator|maintainer|public|buildType)$/;
+my $displayItemNames = q/^(name|description)$/;
+  
+# For storing the attributes during the StartTag subroutine, so that 
+# we can use it later in Text (or EndTag) subroutines
+my $currentAttrRef = undef; 
+
+my $currentLevel = "";
+
+# Count the elements with same name within the same block
+# ("plugin", "option")
+my $currentIndex = 0;
+my $arrayexp = q/^(index|level|indexSubcollection|indexLanguage)$/;
+my $arrayarrayexp= q/^(plugin|classifier)$/;
+my $hashexp = q/^(subcollection)$/; # add other element names that should be represented by hash expressions here
+my $hashhashexp = q/^(displayItem)$/; # add other (collectionmeta) element names that should be represented by hashes of hashes here.
+
+my $defaults = q/^(defaultIndex|defaultLevel|defaultIndexLanguage|languageMetadata)$/;
+
+sub StartTag {
+# Those marked with #@ will not be executed at the same time when this sub is being called
+# so that if/elsif is used to avoid unnecessary tests
+    my ($expat, $element) = @_;
+    
+    # See http://search.cpan.org/~msergeant/XML-Parser-2.36/Parser.pm#Stream
+    # %_ is a hash of all the attributes of this element, we want to store them so we can use the attributes
+    # when the textnode contents of the element are parsed in the subroutine Text (that's the handler for Text). 
+    $currentAttrRef = \%_; 
+
+    my $name = $_{'name'};
+    my $value = $_{'value'};
+    my $type = $_{'type'};
+
+    # for subcollections
+    my $filter = $_{'filter'};
+
+	# for flax activities
+	my $desid = $_{'desid'};
+	my $assigned = $_{'assigned'};
+	my $lang = $_{'lang'};
+	
+    #@ Marking repeated block
+    if ($element =~ /$repeatedBlock/) {
+	$currentIndex = 0;
+    }
+
+    #@ handling block metadataList
+    elsif (defined $name and $name =~ /$stringexp/){
+      $currentLocation = $name;
+    }
+    #@ handling default search index/level/indexLanguage and languageMetadata
+    elsif ($element =~ /$defaults/) {
+      if (defined $name and $name =~ /\w/) {
+	$data->{$nameMap->{$element}} = $name;
+      }
+    }
+
+    #@ handling the displayItems name and description (known as collectionname and collectionextra in GS2)
+    elsif($element eq "displayItemList") {
+	$currentLevel = "displayItemList"; # storing the parent if it is displayItemList
+    } 
+    elsif($element =~ /$hashhashexp/) { # can expand on this to check for other collectionmeta elements
+	if((!defined $assigned) || (defined $assigned and $assigned =~ /\w/ and $assigned eq "true")) {
+	    # either when there is no "assigned" attribute, or when assigned=true (for displayItems):
+	    $currentLocation = $name;
+	}
+    }
+
+    #@ Handling database type: gdbm or gdbm-txtgz, later jdbm.
+    elsif ($element eq "infodb") {
+      $data->{'infodbtype'} = $type;
+    }
+    
+    #@ Handling indexer: mgpp/mg/lucene; stringexp
+    elsif ($element eq "search") {
+      $data->{'buildtype'} = $type;
+    }
+
+    #@ Handling searchtype: plain,form; arrayexp
+    #elsif ($element eq "format" and defined $name and $name =~ /searchType/) {
+	#@ Handling searchtype: plain, form
+	#$currentLocation = $name;	
+    #}
+ 
+    #@ Handle index|level|indexSubcollection|indexLanguage
+    elsif ($element =~ /$arrayexp/) {
+      my $key = $nameMap->{$element};	
+      if (!defined $data->{$key}) {
+	$data->{$key} = [];
+      }
+
+      push (@{$data->{$key}},$name);
+    }
+
+    #@ indexoptions: accentfold/casefold/stem; arrayexp
+    elsif ($element eq "indexOption") {
+      $currentLevel = "indexOption";
+    }
+    if ($currentLevel eq "indexOption" and $element eq "option") {
+      my $key = $nameMap->{$currentLevel};	
+      if (!defined $data->{$key}) {
+	$data->{$key} = [];
+      }
+      push (@{$data->{$key}},$name);
+    }
+    #@ plugout options
+    elsif ($element eq "plugout") {
+	$currentLevel = "plugout";
+	my $key = $nameMap->{$currentLevel};	
+	if (!defined $data->{$key}) {
+	    $data->{$key} = [];
+	}
+	if(defined $name and $name ne ""){
+	    push (@{$data->{$key}},$name);
+	}
+	else{
+	   push (@{$data->{$key}},"GreenstoneXMLPlugout"); 
+	}
+    }
+    if ($currentLevel eq "plugout" and $element eq "option") {     
+	my $key = $nameMap->{$currentLevel};
+	if (defined $name and $name ne ""){
+	    push (@{$data->{$key}},$name);
+	}
+	if (defined $value and $value ne  ""){
+	    push (@{$data->{$key}},$value);
+	}
+    }
+
+    #@ use hash of hash of strings: hashexp
+    elsif ($element =~ /$hashexp/) {
+      if (!defined $data->{$element}) {
+	$data->{$element} = {};
+      }
+      if (defined $name and $name =~ /\w/) {
+	if (defined $filter and $filter =~ /\w/) {
+	  $data->{$element}->{$name} = $filter;
+
+	}
+      }
+    }
+
+    #@ Handling each classifier/plugin element
+    elsif ($element =~ /$arrayarrayexp/) {
+	# find the gs2 mapping name
+        $currentLevel = $element;
+        my $key = $nameMap->{$element};	
+
+	# define an array of array of strings	foreach $k (@{$data->{$key}}) {
+	if (!defined $data->{$key}) {
+	  $data->{$key} = [];
+	}
+	# Push classifier/plugin name (e.g. AZList) into $data as the first string
+ 	push (@{$data->{$key}->[$currentIndex]},$name);
+	#print $currentIndex."indexup\n";
+      }
+    
+    #@ Handling the option elements in each classifier/plugin element (as the following strings)
+    elsif ($currentLevel =~ /$arrayarrayexp/ and $element eq "option") {
+	# find the gs2 mapping name for classifier and plugin
+        my $key = $nameMap->{$currentLevel};	
+
+	if (defined $name and $name =~ /\w/) {
+	    push (@{$data->{$key}->[$currentIndex]}, $name);
+	}
+	if (defined $value and $value =~ /\w/) {
+            push (@{$data->{$key}->[$currentIndex]}, $value);
+	}
+
+    }
+    #@ Handling each flaxActivity element (arrayarrayexp)
+    elsif ($element eq "flaxActivity") {
+		if (!defined $data->{'flaxActivity'}) {
+			$data->{'flaxActivity'} = [];
+		}
+		if(defined $assigned and $assigned =~ /\w/ and $assigned eq "true") {
+		if (defined $name and $name =~ /\w/) {
+	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, 'name');
+	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, $name);
+		}
+	
+		if (defined $desid and $desid =~ /\w/) {
+	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, 'desid');
+	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, $desid);
+		}		
+	
+		if (defined $lang and $lang =~ /\w/) {
+	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, 'lang');
+	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, $lang);
+		}		
+		}
+    }
+}
+
+sub EndTag {
+    my ($expat, $element) = @_;
+    my $endTags = q/^(browse|pluginList|displayItemList)$/;   
+    if ($element =~ /$endTags/) {
+		$currentIndex = 0;
+		$currentLevel = "";
+    }
+    # $arrayarrayexp contains classifier|plugin
+    elsif($element =~ /$arrayarrayexp/ || $element eq "flaxActivity"){
+     	$currentIndex = $currentIndex + 1;
+    }
+}
+
+sub Text {
+    if (defined $currentLocation) { 
+	#@ Handling block metadataList(creator, maintainer, public)
+	if($currentLocation =~ /$stringexp/){
+	    #print $currentLocation;
+	    my $key = $nameMap->{$currentLocation};	
+	    $data->{$key} = $_;
+	    undef $currentLocation;
+	}
+	
+	#@ Handling displayItem metadata that are children of displayItemList
+	# that means we will be getting the collection's name and possibly description ('collectionextra' in GS2).
+	elsif($currentLevel eq "displayItemList" && $currentLocation =~ /$displayItemNames/) {
+	    my $lang = $currentAttrRef->{'lang'};
+	    my $name = $currentAttrRef->{'name'};
+	    
+	    # this is how data->collectionmeta's language is set in Greenstone 2. 
+	    # Need to be consistent, since export.pl accesses these values all in the same way
+	    if(!defined $lang) {
+		$lang = 'default';
+	    } else {
+		$lang = "[l=$lang]"; 
+	    }
+	    
+	    if(defined $name and $name =~ /$displayItemNames/) { # attribute name = 'name' || 'description'
+		# using $nameMap->$name resolves to 'collectionname' if $name='name' and 'collectionextra' if $name='description'
+		$data->{'collectionmeta'}->{$nameMap->{$name}}->{$lang} = $_; # the value is the Text parsed
+		#print STDERR "***Found: $nameMap->{$name} collectionmeta, lang is $lang. Value: $data->{'collectionmeta'}->{$nameMap->{$name}}->{$lang}\n";
+	    }
+	    undef $currentLocation;
+	}
+  
+	#@ Handling searchtype: plain,form; arrayexp
+	elsif (defined $currentLocation and $currentLocation =~ /searchType/) {
+	    # map 'searchType' into 'searchtype'
+	    my $key = $nameMap->{$currentLocation};
+	    # split it by ','
+	    my ($plain, $form) = split (",", $_);
+	    
+	    if (!defined $data->{$key}) {
+		$data->{$key} = [];
+	    }
+	    if (defined $plain and $plain =~ /\w/) {
+		push @{ $data->{$key} }, $plain;
+	    }
+	    if (defined $form and $form =~ /\w/) {
+		push @{ $data->{$key} }, $form;
+	    }
+	}
+    }	
+}
+
+# This sub is for debugging purposes
+sub Display {
+    # metadataList
+    foreach my $k (keys %{$data}) {
+	print STDERR "*** metadatalist key $k\n"; 
+    }
+  
+    print $data->{'creator'}."\n" if (defined $data->{'creator'});
+    print $data->{"maintainer"}."\n" if (defined $data->{"maintainer"});
+    print $data->{"public"}."\n" if (defined $data->{"public"});
+    print $data->{"defaultindex"}."\n" if (defined $data->{"defaultindex"});
+    print $data->{"defaultlevel"}."\n" if (defined $data->{"defaultlevel"});
+    print $data->{"buildtype"}."\n" if (defined $data->{"buildtype"});
+    print  join(",",@{$data->{"searchtype"}})."\n" if (defined $data->{"searchtype"});
+    print  join(",",@{$data->{'levels'}})."\n" if (defined $data->{'levels'});
+    print  join(",",@{$data->{'indexsubcollections'}})."\n" if (defined $data->{'indexsubcollections'});
+    print  join(",",@{$data->{'indexes'}})."\n" if (defined $data->{'indexes'});
+    print  join(",",@{$data->{'indexoptions'}})."\n" if (defined $data->{'indexoptions'});
+    print  join(",",@{$data->{'languages'}})."\n" if (defined $data->{'languages'});
+    print  join(",",@{$data->{'languagemetadata'}})."\n" if (defined $data->{'languagemetadata'});
+ 
+    if (defined $data->{'plugin'}) {
+	foreach $a (@{$data->{'plugin'}}) {
+	    print join(",",@$a);
+	    print "\n";
+	}
+    }
+    if (defined $data->{'classify'}) {
+	print "Classifiers: \n";
+	map { print join(",",@$_)."\n"; } @{$data->{'classify'}};
+    }
+    
+    if (defined $data->{'subcollection'}) {
+	foreach my $key (keys %{$data->{'subcollection'}}) {
+	    print "subcollection ".$key." ".$data->{'subcollection'}->{$key}."\n";
+	}
+    }
+}
+sub Doctype {
+    my ($expat, $name, $sysid, $pubid, $internal) = @_;
+
+    # allow the short-lived and badly named "GreenstoneDirectoryMetadata" files 
+    # to be processed as well as the "DirectoryMetadata" files which should now
+    # be created by import.pl
+    die if ($name !~ /^(Greenstone)?DirectoryMetadata$/);
+}
+
+# This Char function overrides the one in XML::Parser::Stream to overcome a
+# problem where $expat->{Text} is treated as the return value, slowing
+# things down significantly in some cases.
+sub Char {
+    if ($]<5.008) {
+	use bytes;  # Necessary to prevent encoding issues with XML::Parser 2.31+ and Perl 5.6
+    }
+    $_[0]->{'Text'} .= $_[1];
+    return undef;
+}
+
+# Reads in the model collection configuration file, collectionConfig.xml,
+# into a structure which complies with the one used by gs2 (i.e. one read
+# in by &cfgread::read_cfg_file).
+sub read_cfg_file {
+    my ($filename) = @_;
+    $data = {};
+    if (($filename !~ /collectionConfig\.xml$/ && $filename !~ /buildConfig\.xml$/) || !-f $filename) {
+        return undef;
+    }
+
+    # create XML::Parser object for parsing metadata.xml files
+    my $parser;
+    if ($]<5.008) {
+	# Perl 5.6
+	$parser = new XML::Parser('Style' => 'Stream',
+				  'Handlers' => {'Char' => \&Char,
+						 'Doctype' => \&Doctype
+						 });
+    }
+    else {
+	# Perl 5.8
+	$parser = new XML::Parser('Style' => 'Stream',
+				  'ProtocolEncoding' => 'ISO-8859-1',
+				  'Handlers' => {'Char' => \&Char,
+						 'Doctype' => \&Doctype
+						 });
+    }
+
+    if (!open (COLCFG, $filename)) {
+	print STDERR "cfgread::read_cfg_file couldn't read the cfg file $filename\n";
+    } else {
+
+      $parser->parsefile ($filename);# (COLCFG);
+      close (COLCFG);
+    }
+
+    #&Display; 
+    return $data;
+}
+
+
+sub write_line {
+    my ($filehandle, $line) = @_;
+    print $filehandle join ("", @$line), "\n";
+}
+
+# Create the buildConfig.xml file for a specific collection
+sub write_cfg_file {
+    # this sub is called in make_auxiliary_files() in basebuilder.pm
+    # the received args: $buildoutfile - destination file: buildConfig.xml
+    #                    $buildcfg - all build options, eg, disable_OAI
+    #                    $collectcfg - contents of collectionConfig.xml read in by read_cfg_file sub in cfgread4gs3.pm.
+    my ($buildoutfile, $buildcfg, $collectcfg, $disable_OAI) = @_;
+    my $line = [];
+
+    if (!open (COLCFG, ">$buildoutfile")) {
+	print STDERR "cfgread4gs3::write_cfg_file couldn't write the build config file $buildoutfile\n";
+	die;
+    }
+
+    &write_line('COLCFG', ["<buildConfig xmlns:gsf=\"http://www.greenstone.org/greenstone3/schema/ConfigFormat\">"]);  
+
+    # output building metadata to build config file 
+    my $buildtype;
+    if (defined $buildcfg->{"buildtype"}) {
+	$buildtype = $buildcfg->{"buildtype"};
+    } else {
+	$buildtype = "mgpp";
+    }
+    my $numdocs;
+    if (defined $buildcfg->{"numdocs"}) {
+      $numdocs = $buildcfg->{"numdocs"};
+    }
+    &write_line('COLCFG', ["<metadataList>"]);
+    &write_line('COLCFG', ["<metadata name=\"numDocs\">", $numdocs, "</metadata>"]);
+    &write_line('COLCFG', ["<metadata name=\"buildType\">", $buildtype, "</metadata>"]);
+    &write_line('COLCFG', ["</metadataList>"]);
+
+    my $service_type = "MGPP";
+    if ($buildtype eq "mg") {
+	$service_type = "MG";
+    } elsif ($buildtype eq "lucene") {
+	$service_type = "Lucene";
+    }
+
+    # output serviceRackList
+    &write_line('COLCFG', ["<serviceRackList>"]);
+
+    # This serviceRack enables the collection to provide the oai metadata retrieve service, which is served by the OAIPMH.java class
+    # For each collection, we write the following serviceRack in the collection's buildConfig.xml file if the 'disable_OAI' argument is not checked in the GLI (or equivalently, a 'disable_OAI' flag is not specified on the command line). There are also other configurations in the OAIConfig.xml.
+    if ($disable_OAI == 0) {
+      &write_line('COLCFG', ["<serviceRack name=\"OAIPMH\">"]);
+      if (defined $buildcfg->{'indexstem'}) {
+        my $indexstem = $buildcfg->{'indexstem'};
+        &write_line('COLCFG', ["<indexStem name=\"", $indexstem, "\" />"]);     
+      }
+      &write_line('COLCFG', ["</serviceRack>"]);
+    }    
+    # do the search service 
+    &write_line('COLCFG', ["<serviceRack name=\"GS2", $service_type, "Search\">"]);
+    if (defined $buildcfg->{'indexstem'}) {
+      my $indexstem = $buildcfg->{'indexstem'};
+      &write_line('COLCFG', ["<indexStem name=\"", $indexstem, "\" />"]);     
+    }
+
+    #indexes
+    # maps index name to shortname
+    my $indexmap = {};
+    # keeps the order for indexes
+    my @indexlist = ();
+    
+    my $defaultindex = "";
+    my $first = 1;
+    my $maptype = "indexfieldmap";
+    if ($buildtype eq "mg") {
+	$maptype = "indexmap";
+    }
+
+    #map {print $_."\n"} keys %$buildcfg;
+
+    if (defined $buildcfg->{$maptype}) {
+	my $indexmap_t = $buildcfg->{$maptype};
+       foreach my $i (@$indexmap_t) {
+    my ($k, $v) = $i =~ /^(.*)\-\>(.*)$/;
+	    $indexmap->{$k} = $v;
+	    push @indexlist, $k;
+	    if ($first) {
+		$defaultindex = $v;
+		$first = 0;
+	    }	
+	}
+	# now if the user has assigned a default index, we use it 
+	if (defined $collectcfg->{"defaultindex"}) {
+	  $defaultindex = $indexmap->{$collectcfg->{"defaultindex"}};
+	}
+	
+    } else {
+	print STDERR "$maptype not defined";
+    }  
+    #for each index in indexList, write them out
+    &write_line('COLCFG', ["<indexList>"]);
+    foreach my $i (@indexlist) {
+	my $index = $indexmap->{$i};
+	&write_line('COLCFG', ["<index name=\"", $i, "\" ", "shortname=\"", $index, "\" />"]);
+    }	
+    &write_line('COLCFG', ["</indexList>"]);
+
+    
+    &write_line('COLCFG', ["<defaultIndex shortname=\"", $defaultindex, "\" />"]);
+
+
+    # do indexOptionList
+    if ($buildtype eq "mg" || $buildtype eq "mgpp") {
+        &write_line('COLCFG', ["<indexOptionList>"]);
+	my $stemindexes = 3; # default is stem and casefold
+	if (defined $buildcfg->{'stemindexes'} && $buildcfg->{'stemindexes'} =~ /^\d+$/ ) {
+	    $stemindexes = $buildcfg->{'stemindexes'};
+	}
+	&write_line('COLCFG', ["<indexOption name=\"stemIndexes\" value=\"", $stemindexes, "\" />"]);
+	
+	my $maxnumeric = 4; # default
+	if (defined $buildcfg->{'maxnumeric'} && $buildcfg->{'maxnumeric'} =~ /^\d+$/) {
+	    $maxnumeric = $buildcfg->{'maxnumeric'};
+	}
+	&write_line('COLCFG', ["<indexOption name=\"maxnumeric\" value=\"", $maxnumeric, "\" />"]);
+        &write_line('COLCFG', ["</indexOptionList>"]);
+    }
+
+    #  levelList
+    my $levelmap = {};
+    my @levellist = ();
+    my $default_search_level = "Doc";
+    my $default_retrieve_level = "Doc";
+    my $default_db_level = "Doc";
+    $first = 1;
+    if ($buildtype eq "mgpp" || $buildtype eq "lucene") {
+	if (defined $buildcfg->{'levelmap'}) {
+	    my $levelmap_t = $buildcfg->{'levelmap'};
+	    foreach my $l (@$levelmap_t) {
+		my ($key, $val) = $l =~ /^(.*)\-\>(.*)$/;
+		$levelmap->{$key} = $val;
+		push @levellist, $key;
+		if ($first) {
+		    # let default search level follow the first level in the level list
+		    $default_search_level = $val;
+		    # retrieve/database levels may get modified later if text level is defined
+		    $default_retrieve_level = $val;
+		    $default_db_level = $val;
+		    $first = 0;
+		}
+	    }
+	}
+	# the default level assigned by the user is no longer ignored [Shaoqun], but the retrievel level stays the same. 
+       	#if (defined $collectcfg->{"defaultlevel"}) {
+	$default_search_level = $levelmap->{$collectcfg->{"defaultlevel"}};
+        #  $default_retrieve_level = $default_search_level;
+	#}
+	
+	if (defined $buildcfg->{'textlevel'}) {
+	   # let the retrieve/database levels always follow the textlevel
+           $default_retrieve_level = $buildcfg->{'textlevel'};
+	   $default_db_level = $buildcfg->{'textlevel'};
+		 
+	}
+    }
+    #for each level in levelList, write them out
+	if ($buildtype ne "mg") {
+    &write_line('COLCFG', ["<levelList>"]);
+    foreach my $lv (@levellist) {
+	my $level = $levelmap->{$lv};
+        &write_line('COLCFG', ["<level name=\"", $lv, "\" shortname=\"", $level, "\" />"]);
+    }	
+    &write_line('COLCFG', ["</levelList>"]);
+    }
+    # add in defaultLevel as the same level as indexLevelList, making the reading job easier
+    if ($buildtype eq "lucene" || $buildtype eq "mgpp") {
+	&write_line('COLCFG', ["<defaultLevel shortname=\"", $default_search_level, "\" />"]);
+    }
+    if ($buildtype eq "lucene" || $buildtype eq "mgpp") {
+        &write_line('COLCFG', ["<defaultDBLevel shortname=\"", $default_db_level, "\" />"]);
+    }
+    # do searchTypeList
+    if ($buildtype eq "mgpp" || $buildtype eq "lucene") {
+	  &write_line('COLCFG', ["<searchTypeList>"]);
+      
+      if (defined $buildcfg->{"searchtype"}) {
+	  my $searchtype_t = $buildcfg->{"searchtype"};
+	  foreach my $s (@$searchtype_t) {
+	  &write_line('COLCFG', ["<searchType name=\"", $s, "\" />"]);
+	}
+      } else {
+	  &write_line('COLCFG', ["<searchType name=\"plain\" />"]);
+	  &write_line('COLCFG', ["<searchType name=\"form\" />"]);
+      }
+	  &write_line('COLCFG', ["</searchTypeList>"]);
+    }
+
+    # do indexLanguageList [in collect.cfg: languages; in build.cfg: languagemap]
+    $first = 1;
+    my $default_lang = "";
+    my $default_lang_short = "";
+    if (defined $buildcfg->{"languagemap"}) {
+      &write_line('COLCFG', ["<indexLanguageList>"]);
+
+      my $langmap_t = $buildcfg->{"languagemap"};
+      foreach my $l (@$langmap_t) {
+	my ($k, $v) = $l =~ /^(.*)\-\>(.*)$/; 
+
+	&write_line('COLCFG', ["<indexLanguage name=\"", $k, "\" shortname=\"", $v, "\" />"]);
+	if ($first) {
+	  $default_lang = $k; #name
+	  $default_lang_short = $v; #shortname
+	  $first = 0;
+	}
+      }
+
+      &write_line('COLCFG', ["</indexLanguageList>"]);
+      # now if the user has assigned a default language (as "en", "ru" etc.)
+      if (defined $collectcfg->{"defaultlanguage"}) {
+	$default_lang = $collectcfg->{"defaultlanguage"};
+      }
+      &write_line('COLCFG', ["<defaultIndexLanguage name=\"", $default_lang, "\" shortname=\"", $default_lang_short, "\" />"]);
+    }
+
+
+    # do indexSubcollectionList
+    my $default_subcol = "";# make it in sub scope to be used in the concatenation
+    if (defined $buildcfg->{'subcollectionmap'}) {
+      &write_line('COLCFG', ["<indexSubcollectionList>"]);
+      my $subcolmap = {};
+      my @subcollist = ();
+      $first = 1;
+      my $subcolmap_t = $buildcfg->{'subcollectionmap'};
+      foreach my $l (@$subcolmap_t) {
+	my ($k, $v) = $l =~ /^(.*)\-\>(.*)$/;
+	$subcolmap->{$k} = $v;
+	push @subcollist, $k;
+	if ($first) {
+	  $default_subcol = $v;
+	  $first = 0;
+	}
+      }
+      foreach my $sl (@subcollist) {
+	my $subcol = $subcolmap->{$sl};
+	&write_line('COLCFG', ["<indexSubcollection name=\"", $sl, "\" shortname=\"", $subcol, "\" />"]);
+      }	
+
+      &write_line('COLCFG', ["</indexSubcollectionList>"]);
+      &write_line('COLCFG', ["<defaultIndexSubcollection shortname=\"", $default_subcol, "\" />"]);
+    }
+      
+    # close off search service 
+    &write_line('COLCFG', ["</serviceRack>"]);
+
+    # do the retrieve service
+    &write_line('COLCFG', ["<serviceRack name=\"GS2", $service_type, "Retrieve\">"]);
+
+    # do default index 
+    if (defined $buildcfg->{"languagemap"}) {
+	&write_line('COLCFG', ["<defaultIndexLanguage shortname=\"", $default_lang, "\" />"]);
+    }
+    if (defined $buildcfg->{'subcollectionmap'}) {
+	&write_line('COLCFG', ["<defaultIndexSubcollection shortname=\"", $default_subcol, "\" />"]);
+    }
+    if ($buildtype eq "mg") {
+      &write_line('COLCFG', ["<defaultIndex shortname=\"", $defaultindex, "\" />"]);
+    }
+
+    if (defined $buildcfg->{'indexstem'}) {
+      my $indexstem = $buildcfg->{'indexstem'};
+      &write_line('COLCFG', ["<indexStem name=\"", $indexstem, "\" />"]);     
+    }
+    if ($buildtype eq "mgpp" || $buildtype eq "lucene") {
+      &write_line('COLCFG', ["<defaultLevel shortname=\"", $default_retrieve_level, "\" />"]);
+    }
+    &write_line('COLCFG', ["</serviceRack>"]);
+
+    # do the browse service
+    my $count = 1;
+    my $phind = 0;
+    my $started_classifiers = 0;
+
+    my $classifiers = $collectcfg->{"classify"};
+    foreach my $cl (@$classifiers) {
+      my $name = "CL$count";
+      $count++;
+      my ($classname) = @$cl[0];
+      if ($classname =~ /^phind$/i) {
+	$phind=1;
+	#should add it into coll config classifiers
+	next;
+      }
+      
+      if (not $started_classifiers) {
+	&write_line('COLCFG', ["<serviceRack name=\"GS2Browse\">"]);
+	if (defined $buildcfg->{'indexstem'}) {
+	  my $indexstem = $buildcfg->{'indexstem'};
+	  &write_line('COLCFG', ["<indexStem name=\"", $indexstem, "\" />"]);     
+	}
+	&write_line('COLCFG', ["<classifierList>"]);		
+	$started_classifiers = 1;
+      }
+      my $content = ''; #use buttonname first, then metadata
+      if ($classname eq "DateList") {
+	$content = "Date";
+      } else {
+	for (my $j=0; $j<scalar(@$cl); $j++) {
+	  my $arg = @$cl[$j];
+	  if ($arg eq "-buttonname"){
+	    $content = @$cl[$j+1];
+	    last;
+	  } elsif ($arg eq "-metadata") {
+	    $content = @$cl[$j+1];
+	  }
+	  
+	}
+      }
+      &write_line('COLCFG', ["<classifier name=\"", $name, "\" content=\"", $content, "\" />"]);
+    }     
+    if ($started_classifiers) {
+      # end the classifiers
+      &write_line('COLCFG', ["</classifierList>"]);
+      # close off the Browse service
+      &write_line('COLCFG', ["</serviceRack>"]);
+    }
+    
+    # the phind classifier is a separate service
+    if ($phind) {
+	# if phind classifier
+	&write_line('COLCFG', ["<serviceRack name=\"PhindPhraseBrowse\" />"]);
+    }
+
+	my $flaxActivities = $collectcfg->{"flaxActivity"};
+    foreach my $fa (@$flaxActivities) {
+    	# Six elements of the array for three attribute name/value pairs: name, desid, and lang.
+    	if(defined $fa and @$fa[0] =~ /\w/ and @$fa[1] =~ /\w/ and @$fa[2] =~ /\w/ and @$fa[3] =~ /\w/  and @$fa[4] =~ /\w/ and @$fa[5] =~ /\w/) {
+	    	&write_line('COLCFG', ["<serviceRack type=\"flaxActivity\" ", @$fa[0], "=\"", @$fa[1], "\" ", @$fa[2], "=\"", @$fa[3], "\" ", @$fa[4], "=\"", @$fa[5], "\" />"]);
+	    }
+    }  
+    
+    &write_line('COLCFG', ["</serviceRackList>"]);
+    &write_line('COLCFG', ["</buildConfig>"]);
+
+    close (COLCFG);
+  }
+
+
+#########################################################
+
+1;
Index: dl/trunk/perllib/cfgread4gs3.pm
===================================================================
--- /gsdl/trunk/perllib/cfgread4gs3.pm	(revision 20093)
+++ 	(revision )
@@ -1,801 +1,0 @@
-###########################################################################
-#
-# cfgread4gs3.pm --
-# 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.
-#
-###########################################################################
-
-# reads in configuration files of xml form
-
-package cfgread4gs3;
-use strict;
-no strict 'refs';
-no strict 'subs';
-
-# Wrapper that ensures the right version of XML::Parser is loaded given
-# the version of Perl being used.  Need to distinguish between Perl 5.6 and
-# Perl 5.8
-sub BEGIN {
-    my $perl_dir;
-
-    # Note: $] encodes the version number of perl
-    if ($]>5.008) { 
-	# perl 5.8.1 or above
-	$perl_dir = "perl-5.8";
-    }
-    elsif ($]<5.008) {
-	# assume perl 5.6
-	$perl_dir = "perl-5.6";
-    }
-    else {
-	print STDERR "Warning: Perl 5.8.0 is not a maintained release.\n";
-	print STDERR "         Please upgrade to a newer version of Perl.\n";
-	$perl_dir = "perl-5.8";
-    }
-
-    if ($ENV{'GSDLOS'} !~ /^windows$/i) {
-	# Use push to put this on the end, so an existing XML::Parser will be used by default
-	push (@INC, "$ENV{'GSDLHOME'}/perllib/cpan/$perl_dir");
-    }
-}
-
-use XML::Parser;
-
-# A mapping hash to resolve name discrepancy between gs2 and gs3.
-my $nameMap = {"key" => "value",
-	       "creator" => "creator",
-	       "maintainer" => "maintainer",
-	       "public" => "public",
-	       "infodb" => "infodbtype",
-	       "defaultIndex" => "defaultindex",
-	       "defaultLevel" => "defaultlevel",
-	       "name" => "collectionname",
-	       "description" => "collectionextra",
-	       "smallicon" => "iconcollectionsmall",
-	       "icon" => "iconcollection",
-	       "level" => "levels",
-	       "classifier" => "classify",
-	       "indexSubcollection" => "indexsubcollections",
-	       "indexLanguage" => "languages",
-	       "defaultIndexLanguage" => "defaultlanguage",
-	       "index" => "indexes",
-	       "plugin" => "plugin",
-	       "plugout" => "plugout",
-	       "indexOption" => "indexoptions",
-	       "searchType" => "searchtype",
-	       "languageMetadata" => "languagemetadata",
-	       "buildType" => "buildtype"
-	       };
-# A hash structure which is returned by sub read_cfg_file.
-my $data = {};
-
-my $repeatedBlock = q/^(browse|pluginList)$/;
-
-# use those unique attribute values to locate the text within the elements
-# creator, public, maintainer and within a displayItem.
-my $currentLocation = "";
-my $stringexp = q/^(creator|maintainer|public|buildType)$/;
-my $displayItemNames = q/^(name|description)$/;
-  
-# For storing the attributes during the StartTag subroutine, so that 
-# we can use it later in Text (or EndTag) subroutines
-my $currentAttrRef = undef; 
-
-my $currentLevel = "";
-
-# Count the elements with same name within the same block
-# ("plugin", "option")
-my $currentIndex = 0;
-my $arrayexp = q/^(index|level|indexSubcollection|indexLanguage)$/;
-my $arrayarrayexp= q/^(plugin|classifier)$/;
-my $hashexp = q/^(subcollection)$/; # add other element names that should be represented by hash expressions here
-my $hashhashexp = q/^(displayItem)$/; # add other (collectionmeta) element names that should be represented by hashes of hashes here.
-
-my $defaults = q/^(defaultIndex|defaultLevel|defaultIndexLanguage|languageMetadata)$/;
-
-sub StartTag {
-# Those marked with #@ will not be executed at the same time when this sub is being called
-# so that if/elsif is used to avoid unnecessary tests
-    my ($expat, $element) = @_;
-    
-    # See http://search.cpan.org/~msergeant/XML-Parser-2.36/Parser.pm#Stream
-    # %_ is a hash of all the attributes of this element, we want to store them so we can use the attributes
-    # when the textnode contents of the element are parsed in the subroutine Text (that's the handler for Text). 
-    $currentAttrRef = \%_; 
-
-    my $name = $_{'name'};
-    my $value = $_{'value'};
-    my $type = $_{'type'};
-
-    # for subcollections
-    my $filter = $_{'filter'};
-
-	# for flax activities
-	my $desid = $_{'desid'};
-	my $assigned = $_{'assigned'};
-	my $lang = $_{'lang'};
-	
-    #@ Marking repeated block
-    if ($element =~ /$repeatedBlock/) {
-	$currentIndex = 0;
-    }
-
-    #@ handling block metadataList
-    elsif (defined $name and $name =~ /$stringexp/){
-      $currentLocation = $name;
-    }
-    #@ handling default search index/level/indexLanguage and languageMetadata
-    elsif ($element =~ /$defaults/) {
-      if (defined $name and $name =~ /\w/) {
-	$data->{$nameMap->{$element}} = $name;
-      }
-    }
-
-    #@ handling the displayItems name and description (known as collectionname and collectionextra in GS2)
-    elsif($element eq "displayItemList") {
-	$currentLevel = "displayItemList"; # storing the parent if it is displayItemList
-    } 
-    elsif($element =~ /$hashhashexp/) { # can expand on this to check for other collectionmeta elements
-	if((!defined $assigned) || (defined $assigned and $assigned =~ /\w/ and $assigned eq "true")) {
-	    # either when there is no "assigned" attribute, or when assigned=true (for displayItems):
-	    $currentLocation = $name;
-	}
-    }
-
-    #@ Handling database type: gdbm or gdbm-txtgz, later jdbm.
-    elsif ($element eq "infodb") {
-      $data->{'infodbtype'} = $type;
-    }
-    
-    #@ Handling indexer: mgpp/mg/lucene; stringexp
-    elsif ($element eq "search") {
-      $data->{'buildtype'} = $type;
-    }
-
-    #@ Handling searchtype: plain,form; arrayexp
-    #elsif ($element eq "format" and defined $name and $name =~ /searchType/) {
-	#@ Handling searchtype: plain, form
-	#$currentLocation = $name;	
-    #}
- 
-    #@ Handle index|level|indexSubcollection|indexLanguage
-    elsif ($element =~ /$arrayexp/) {
-      my $key = $nameMap->{$element};	
-      if (!defined $data->{$key}) {
-	$data->{$key} = [];
-      }
-
-      push (@{$data->{$key}},$name);
-    }
-
-    #@ indexoptions: accentfold/casefold/stem; arrayexp
-    elsif ($element eq "indexOption") {
-      $currentLevel = "indexOption";
-    }
-    if ($currentLevel eq "indexOption" and $element eq "option") {
-      my $key = $nameMap->{$currentLevel};	
-      if (!defined $data->{$key}) {
-	$data->{$key} = [];
-      }
-      push (@{$data->{$key}},$name);
-    }
-    #@ plugout options
-    elsif ($element eq "plugout") {
-	$currentLevel = "plugout";
-	my $key = $nameMap->{$currentLevel};	
-	if (!defined $data->{$key}) {
-	    $data->{$key} = [];
-	}
-	if(defined $name and $name ne ""){
-	    push (@{$data->{$key}},$name);
-	}
-	else{
-	   push (@{$data->{$key}},"GreenstoneXMLPlugout"); 
-	}
-    }
-    if ($currentLevel eq "plugout" and $element eq "option") {     
-	my $key = $nameMap->{$currentLevel};
-	if (defined $name and $name ne ""){
-	    push (@{$data->{$key}},$name);
-	}
-	if (defined $value and $value ne  ""){
-	    push (@{$data->{$key}},$value);
-	}
-    }
-
-    #@ use hash of hash of strings: hashexp
-    elsif ($element =~ /$hashexp/) {
-      if (!defined $data->{$element}) {
-	$data->{$element} = {};
-      }
-      if (defined $name and $name =~ /\w/) {
-	if (defined $filter and $filter =~ /\w/) {
-	  $data->{$element}->{$name} = $filter;
-
-	}
-      }
-    }
-
-    #@ Handling each classifier/plugin element
-    elsif ($element =~ /$arrayarrayexp/) {
-	# find the gs2 mapping name
-        $currentLevel = $element;
-        my $key = $nameMap->{$element};	
-
-	# define an array of array of strings	foreach $k (@{$data->{$key}}) {
-	if (!defined $data->{$key}) {
-	  $data->{$key} = [];
-	}
-	# Push classifier/plugin name (e.g. AZList) into $data as the first string
- 	push (@{$data->{$key}->[$currentIndex]},$name);
-	#print $currentIndex."indexup\n";
-      }
-    
-    #@ Handling the option elements in each classifier/plugin element (as the following strings)
-    elsif ($currentLevel =~ /$arrayarrayexp/ and $element eq "option") {
-	# find the gs2 mapping name for classifier and plugin
-        my $key = $nameMap->{$currentLevel};	
-
-	if (defined $name and $name =~ /\w/) {
-	    push (@{$data->{$key}->[$currentIndex]}, $name);
-	}
-	if (defined $value and $value =~ /\w/) {
-            push (@{$data->{$key}->[$currentIndex]}, $value);
-	}
-
-    }
-    #@ Handling each flaxActivity element (arrayarrayexp)
-    elsif ($element eq "flaxActivity") {
-		if (!defined $data->{'flaxActivity'}) {
-			$data->{'flaxActivity'} = [];
-		}
-		if(defined $assigned and $assigned =~ /\w/ and $assigned eq "true") {
-		if (defined $name and $name =~ /\w/) {
-	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, 'name');
-	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, $name);
-		}
-	
-		if (defined $desid and $desid =~ /\w/) {
-	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, 'desid');
-	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, $desid);
-		}		
-	
-		if (defined $lang and $lang =~ /\w/) {
-	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, 'lang');
-	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, $lang);
-		}		
-		}
-    }
-}
-
-sub EndTag {
-    my ($expat, $element) = @_;
-    my $endTags = q/^(browse|pluginList|displayItemList)$/;   
-    if ($element =~ /$endTags/) {
-		$currentIndex = 0;
-		$currentLevel = "";
-    }
-    # $arrayarrayexp contains classifier|plugin
-    elsif($element =~ /$arrayarrayexp/ || $element eq "flaxActivity"){
-     	$currentIndex = $currentIndex + 1;
-    }
-}
-
-sub Text {
-    if (defined $currentLocation) { 
-	#@ Handling block metadataList(creator, maintainer, public)
-	if($currentLocation =~ /$stringexp/){
-	    #print $currentLocation;
-	    my $key = $nameMap->{$currentLocation};	
-	    $data->{$key} = $_;
-	    undef $currentLocation;
-	}
-	
-	#@ Handling displayItem metadata that are children of displayItemList
-	# that means we will be getting the collection's name and possibly description ('collectionextra' in GS2).
-	elsif($currentLevel eq "displayItemList" && $currentLocation =~ /$displayItemNames/) {
-	    my $lang = $currentAttrRef->{'lang'};
-	    my $name = $currentAttrRef->{'name'};
-	    
-	    # this is how data->collectionmeta's language is set in Greenstone 2. 
-	    # Need to be consistent, since export.pl accesses these values all in the same way
-	    if(!defined $lang) {
-		$lang = 'default';
-	    } else {
-		$lang = "[l=$lang]"; 
-	    }
-	    
-	    if(defined $name and $name =~ /$displayItemNames/) { # attribute name = 'name' || 'description'
-		# using $nameMap->$name resolves to 'collectionname' if $name='name' and 'collectionextra' if $name='description'
-		$data->{'collectionmeta'}->{$nameMap->{$name}}->{$lang} = $_; # the value is the Text parsed
-		#print STDERR "***Found: $nameMap->{$name} collectionmeta, lang is $lang. Value: $data->{'collectionmeta'}->{$nameMap->{$name}}->{$lang}\n";
-	    }
-	    undef $currentLocation;
-	}
-  
-	#@ Handling searchtype: plain,form; arrayexp
-	elsif (defined $currentLocation and $currentLocation =~ /searchType/) {
-	    # map 'searchType' into 'searchtype'
-	    my $key = $nameMap->{$currentLocation};
-	    # split it by ','
-	    my ($plain, $form) = split (",", $_);
-	    
-	    if (!defined $data->{$key}) {
-		$data->{$key} = [];
-	    }
-	    if (defined $plain and $plain =~ /\w/) {
-		push @{ $data->{$key} }, $plain;
-	    }
-	    if (defined $form and $form =~ /\w/) {
-		push @{ $data->{$key} }, $form;
-	    }
-	}
-    }	
-}
-
-# This sub is for debugging purposes
-sub Display {
-    # metadataList
-    foreach my $k (keys %{$data}) {
-	print STDERR "*** metadatalist key $k\n"; 
-    }
-  
-    print $data->{'creator'}."\n" if (defined $data->{'creator'});
-    print $data->{"maintainer"}."\n" if (defined $data->{"maintainer"});
-    print $data->{"public"}."\n" if (defined $data->{"public"});
-    print $data->{"defaultindex"}."\n" if (defined $data->{"defaultindex"});
-    print $data->{"defaultlevel"}."\n" if (defined $data->{"defaultlevel"});
-    print $data->{"buildtype"}."\n" if (defined $data->{"buildtype"});
-    print  join(",",@{$data->{"searchtype"}})."\n" if (defined $data->{"searchtype"});
-    print  join(",",@{$data->{'levels'}})."\n" if (defined $data->{'levels'});
-    print  join(",",@{$data->{'indexsubcollections'}})."\n" if (defined $data->{'indexsubcollections'});
-    print  join(",",@{$data->{'indexes'}})."\n" if (defined $data->{'indexes'});
-    print  join(",",@{$data->{'indexoptions'}})."\n" if (defined $data->{'indexoptions'});
-    print  join(",",@{$data->{'languages'}})."\n" if (defined $data->{'languages'});
-    print  join(",",@{$data->{'languagemetadata'}})."\n" if (defined $data->{'languagemetadata'});
- 
-    if (defined $data->{'plugin'}) {
-	foreach $a (@{$data->{'plugin'}}) {
-	    print join(",",@$a);
-	    print "\n";
-	}
-    }
-    if (defined $data->{'classify'}) {
-	print "Classifiers: \n";
-	map { print join(",",@$_)."\n"; } @{$data->{'classify'}};
-    }
-    
-    if (defined $data->{'subcollection'}) {
-	foreach my $key (keys %{$data->{'subcollection'}}) {
-	    print "subcollection ".$key." ".$data->{'subcollection'}->{$key}."\n";
-	}
-    }
-}
-sub Doctype {
-    my ($expat, $name, $sysid, $pubid, $internal) = @_;
-
-    # allow the short-lived and badly named "GreenstoneDirectoryMetadata" files 
-    # to be processed as well as the "DirectoryMetadata" files which should now
-    # be created by import.pl
-    die if ($name !~ /^(Greenstone)?DirectoryMetadata$/);
-}
-
-# This Char function overrides the one in XML::Parser::Stream to overcome a
-# problem where $expat->{Text} is treated as the return value, slowing
-# things down significantly in some cases.
-sub Char {
-    if ($]<5.008) {
-	use bytes;  # Necessary to prevent encoding issues with XML::Parser 2.31+ and Perl 5.6
-    }
-    $_[0]->{'Text'} .= $_[1];
-    return undef;
-}
-
-# Reads in the model collection configuration file, collectionConfig.xml,
-# into a structure which complies with the one used by gs2 (i.e. one read
-# in by &cfgread::read_cfg_file).
-sub read_cfg_file {
-    my ($filename) = @_;
-    $data = {};
-    if (($filename !~ /collectionConfig\.xml$/ && $filename !~ /buildConfig\.xml$/) || !-f $filename) {
-        return undef;
-    }
-
-    # create XML::Parser object for parsing metadata.xml files
-    my $parser;
-    if ($]<5.008) {
-	# Perl 5.6
-	$parser = new XML::Parser('Style' => 'Stream',
-				  'Handlers' => {'Char' => \&Char,
-						 'Doctype' => \&Doctype
-						 });
-    }
-    else {
-	# Perl 5.8
-	$parser = new XML::Parser('Style' => 'Stream',
-				  'ProtocolEncoding' => 'ISO-8859-1',
-				  'Handlers' => {'Char' => \&Char,
-						 'Doctype' => \&Doctype
-						 });
-    }
-
-    if (!open (COLCFG, $filename)) {
-	print STDERR "cfgread::read_cfg_file couldn't read the cfg file $filename\n";
-    } else {
-
-      $parser->parsefile ($filename);# (COLCFG);
-      close (COLCFG);
-    }
-
-    #&Display; 
-    return $data;
-}
-
-
-sub write_line {
-    my ($filehandle, $line) = @_;
-    print $filehandle join ("", @$line), "\n";
-}
-
-# Create the buildConfig.xml file for a specific collection
-sub write_cfg_file {
-    # this sub is called in make_auxiliary_files() in basebuilder.pm
-    # the received args: $buildoutfile - destination file: buildConfig.xml
-    #                    $buildcfg - all build options, eg, disable_OAI
-    #                    $collectcfg - contents of collectionConfig.xml read in by read_cfg_file sub in cfgread4gs3.pm.
-    my ($buildoutfile, $buildcfg, $collectcfg, $disable_OAI) = @_;
-    my $line = [];
-
-    if (!open (COLCFG, ">$buildoutfile")) {
-	print STDERR "cfgread4gs3::write_cfg_file couldn't write the build config file $buildoutfile\n";
-	die;
-    }
-
-    &write_line('COLCFG', ["<buildConfig xmlns:gsf=\"http://www.greenstone.org/greenstone3/schema/ConfigFormat\">"]);  
-
-    # output building metadata to build config file 
-    my $buildtype;
-    if (defined $buildcfg->{"buildtype"}) {
-	$buildtype = $buildcfg->{"buildtype"};
-    } else {
-	$buildtype = "mgpp";
-    }
-    my $numdocs;
-    if (defined $buildcfg->{"numdocs"}) {
-      $numdocs = $buildcfg->{"numdocs"};
-    }
-    &write_line('COLCFG', ["<metadataList>"]);
-    &write_line('COLCFG', ["<metadata name=\"numDocs\">", $numdocs, "</metadata>"]);
-    &write_line('COLCFG', ["<metadata name=\"buildType\">", $buildtype, "</metadata>"]);
-    &write_line('COLCFG', ["</metadataList>"]);
-
-    my $service_type = "MGPP";
-    if ($buildtype eq "mg") {
-	$service_type = "MG";
-    } elsif ($buildtype eq "lucene") {
-	$service_type = "Lucene";
-    }
-
-    # output serviceRackList
-    &write_line('COLCFG', ["<serviceRackList>"]);
-
-    # This serviceRack enables the collection to provide the oai metadata retrieve service, which is served by the OAIPMH.java class
-    # For each collection, we write the following serviceRack in the collection's buildConfig.xml file if the 'disable_OAI' argument is not checked in the GLI (or equivalently, a 'disable_OAI' flag is not specified on the command line). There are also other configurations in the OAIConfig.xml.
-    if ($disable_OAI == 0) {
-      &write_line('COLCFG', ["<serviceRack name=\"OAIPMH\">"]);
-      if (defined $buildcfg->{'indexstem'}) {
-        my $indexstem = $buildcfg->{'indexstem'};
-        &write_line('COLCFG', ["<indexStem name=\"", $indexstem, "\" />"]);     
-      }
-      &write_line('COLCFG', ["</serviceRack>"]);
-    }    
-    # do the search service 
-    &write_line('COLCFG', ["<serviceRack name=\"GS2", $service_type, "Search\">"]);
-    if (defined $buildcfg->{'indexstem'}) {
-      my $indexstem = $buildcfg->{'indexstem'};
-      &write_line('COLCFG', ["<indexStem name=\"", $indexstem, "\" />"]);     
-    }
-
-    #indexes
-    # maps index name to shortname
-    my $indexmap = {};
-    # keeps the order for indexes
-    my @indexlist = ();
-    
-    my $defaultindex = "";
-    my $first = 1;
-    my $maptype = "indexfieldmap";
-    if ($buildtype eq "mg") {
-	$maptype = "indexmap";
-    }
-
-    #map {print $_."\n"} keys %$buildcfg;
-
-    if (defined $buildcfg->{$maptype}) {
-	my $indexmap_t = $buildcfg->{$maptype};
-       foreach my $i (@$indexmap_t) {
-    my ($k, $v) = $i =~ /^(.*)\-\>(.*)$/;
-	    $indexmap->{$k} = $v;
-	    push @indexlist, $k;
-	    if ($first) {
-		$defaultindex = $v;
-		$first = 0;
-	    }	
-	}
-	# now if the user has assigned a default index, we use it 
-	if (defined $collectcfg->{"defaultindex"}) {
-	  $defaultindex = $indexmap->{$collectcfg->{"defaultindex"}};
-	}
-	
-    } else {
-	print STDERR "$maptype not defined";
-    }  
-    #for each index in indexList, write them out
-    &write_line('COLCFG', ["<indexList>"]);
-    foreach my $i (@indexlist) {
-	my $index = $indexmap->{$i};
-	&write_line('COLCFG', ["<index name=\"", $i, "\" ", "shortname=\"", $index, "\" />"]);
-    }	
-    &write_line('COLCFG', ["</indexList>"]);
-
-    
-    &write_line('COLCFG', ["<defaultIndex shortname=\"", $defaultindex, "\" />"]);
-
-
-    # do indexOptionList
-    if ($buildtype eq "mg" || $buildtype eq "mgpp") {
-        &write_line('COLCFG', ["<indexOptionList>"]);
-	my $stemindexes = 3; # default is stem and casefold
-	if (defined $buildcfg->{'stemindexes'} && $buildcfg->{'stemindexes'} =~ /^\d+$/ ) {
-	    $stemindexes = $buildcfg->{'stemindexes'};
-	}
-	&write_line('COLCFG', ["<indexOption name=\"stemIndexes\" value=\"", $stemindexes, "\" />"]);
-	
-	my $maxnumeric = 4; # default
-	if (defined $buildcfg->{'maxnumeric'} && $buildcfg->{'maxnumeric'} =~ /^\d+$/) {
-	    $maxnumeric = $buildcfg->{'maxnumeric'};
-	}
-	&write_line('COLCFG', ["<indexOption name=\"maxnumeric\" value=\"", $maxnumeric, "\" />"]);
-        &write_line('COLCFG', ["</indexOptionList>"]);
-    }
-
-    #  levelList
-    my $levelmap = {};
-    my @levellist = ();
-    my $default_search_level = "Doc";
-    my $default_retrieve_level = "Doc";
-    my $default_db_level = "Doc";
-    $first = 1;
-    if ($buildtype eq "mgpp" || $buildtype eq "lucene") {
-	if (defined $buildcfg->{'levelmap'}) {
-	    my $levelmap_t = $buildcfg->{'levelmap'};
-	    foreach my $l (@$levelmap_t) {
-		my ($key, $val) = $l =~ /^(.*)\-\>(.*)$/;
-		$levelmap->{$key} = $val;
-		push @levellist, $key;
-		if ($first) {
-		    # let default search level follow the first level in the level list
-		    $default_search_level = $val;
-		    # retrieve/database levels may get modified later if text level is defined
-		    $default_retrieve_level = $val;
-		    $default_db_level = $val;
-		    $first = 0;
-		}
-	    }
-	}
-	# the default level assigned by the user is no longer ignored [Shaoqun], but the retrievel level stays the same. 
-       	#if (defined $collectcfg->{"defaultlevel"}) {
-	$default_search_level = $levelmap->{$collectcfg->{"defaultlevel"}};
-        #  $default_retrieve_level = $default_search_level;
-	#}
-	
-	if (defined $buildcfg->{'textlevel'}) {
-	   # let the retrieve/database levels always follow the textlevel
-           $default_retrieve_level = $buildcfg->{'textlevel'};
-	   $default_db_level = $buildcfg->{'textlevel'};
-		 
-	}
-    }
-    #for each level in levelList, write them out
-	if ($buildtype ne "mg") {
-    &write_line('COLCFG', ["<levelList>"]);
-    foreach my $lv (@levellist) {
-	my $level = $levelmap->{$lv};
-        &write_line('COLCFG', ["<level name=\"", $lv, "\" shortname=\"", $level, "\" />"]);
-    }	
-    &write_line('COLCFG', ["</levelList>"]);
-    }
-    # add in defaultLevel as the same level as indexLevelList, making the reading job easier
-    if ($buildtype eq "lucene" || $buildtype eq "mgpp") {
-	&write_line('COLCFG', ["<defaultLevel shortname=\"", $default_search_level, "\" />"]);
-    }
-    if ($buildtype eq "lucene" || $buildtype eq "mgpp") {
-        &write_line('COLCFG', ["<defaultDBLevel shortname=\"", $default_db_level, "\" />"]);
-    }
-    # do searchTypeList
-    if ($buildtype eq "mgpp" || $buildtype eq "lucene") {
-	  &write_line('COLCFG', ["<searchTypeList>"]);
-      
-      if (defined $buildcfg->{"searchtype"}) {
-	  my $searchtype_t = $buildcfg->{"searchtype"};
-	  foreach my $s (@$searchtype_t) {
-	  &write_line('COLCFG', ["<searchType name=\"", $s, "\" />"]);
-	}
-      } else {
-	  &write_line('COLCFG', ["<searchType name=\"plain\" />"]);
-	  &write_line('COLCFG', ["<searchType name=\"form\" />"]);
-      }
-	  &write_line('COLCFG', ["</searchTypeList>"]);
-    }
-
-    # do indexLanguageList [in collect.cfg: languages; in build.cfg: languagemap]
-    $first = 1;
-    my $default_lang = "";
-    my $default_lang_short = "";
-    if (defined $buildcfg->{"languagemap"}) {
-      &write_line('COLCFG', ["<indexLanguageList>"]);
-
-      my $langmap_t = $buildcfg->{"languagemap"};
-      foreach my $l (@$langmap_t) {
-	my ($k, $v) = $l =~ /^(.*)\-\>(.*)$/; 
-
-	&write_line('COLCFG', ["<indexLanguage name=\"", $k, "\" shortname=\"", $v, "\" />"]);
-	if ($first) {
-	  $default_lang = $k; #name
-	  $default_lang_short = $v; #shortname
-	  $first = 0;
-	}
-      }
-
-      &write_line('COLCFG', ["</indexLanguageList>"]);
-      # now if the user has assigned a default language (as "en", "ru" etc.)
-      if (defined $collectcfg->{"defaultlanguage"}) {
-	$default_lang = $collectcfg->{"defaultlanguage"};
-      }
-      &write_line('COLCFG', ["<defaultIndexLanguage name=\"", $default_lang, "\" shortname=\"", $default_lang_short, "\" />"]);
-    }
-
-
-    # do indexSubcollectionList
-    my $default_subcol = "";# make it in sub scope to be used in the concatenation
-    if (defined $buildcfg->{'subcollectionmap'}) {
-      &write_line('COLCFG', ["<indexSubcollectionList>"]);
-      my $subcolmap = {};
-      my @subcollist = ();
-      $first = 1;
-      my $subcolmap_t = $buildcfg->{'subcollectionmap'};
-      foreach my $l (@$subcolmap_t) {
-	my ($k, $v) = $l =~ /^(.*)\-\>(.*)$/;
-	$subcolmap->{$k} = $v;
-	push @subcollist, $k;
-	if ($first) {
-	  $default_subcol = $v;
-	  $first = 0;
-	}
-      }
-      foreach my $sl (@subcollist) {
-	my $subcol = $subcolmap->{$sl};
-	&write_line('COLCFG', ["<indexSubcollection name=\"", $sl, "\" shortname=\"", $subcol, "\" />"]);
-      }	
-
-      &write_line('COLCFG', ["</indexSubcollectionList>"]);
-      &write_line('COLCFG', ["<defaultIndexSubcollection shortname=\"", $default_subcol, "\" />"]);
-    }
-      
-    # close off search service 
-    &write_line('COLCFG', ["</serviceRack>"]);
-
-    # do the retrieve service
-    &write_line('COLCFG', ["<serviceRack name=\"GS2", $service_type, "Retrieve\">"]);
-
-    # do default index 
-    if (defined $buildcfg->{"languagemap"}) {
-	&write_line('COLCFG', ["<defaultIndexLanguage shortname=\"", $default_lang, "\" />"]);
-    }
-    if (defined $buildcfg->{'subcollectionmap'}) {
-	&write_line('COLCFG', ["<defaultIndexSubcollection shortname=\"", $default_subcol, "\" />"]);
-    }
-    if ($buildtype eq "mg") {
-      &write_line('COLCFG', ["<defaultIndex shortname=\"", $defaultindex, "\" />"]);
-    }
-
-    if (defined $buildcfg->{'indexstem'}) {
-      my $indexstem = $buildcfg->{'indexstem'};
-      &write_line('COLCFG', ["<indexStem name=\"", $indexstem, "\" />"]);     
-    }
-    if ($buildtype eq "mgpp" || $buildtype eq "lucene") {
-      &write_line('COLCFG', ["<defaultLevel shortname=\"", $default_retrieve_level, "\" />"]);
-    }
-    &write_line('COLCFG', ["</serviceRack>"]);
-
-    # do the browse service
-    my $count = 1;
-    my $phind = 0;
-    my $started_classifiers = 0;
-
-    my $classifiers = $collectcfg->{"classify"};
-    foreach my $cl (@$classifiers) {
-      my $name = "CL$count";
-      $count++;
-      my ($classname) = @$cl[0];
-      if ($classname =~ /^phind$/i) {
-	$phind=1;
-	#should add it into coll config classifiers
-	next;
-      }
-      
-      if (not $started_classifiers) {
-	&write_line('COLCFG', ["<serviceRack name=\"GS2Browse\">"]);
-	if (defined $buildcfg->{'indexstem'}) {
-	  my $indexstem = $buildcfg->{'indexstem'};
-	  &write_line('COLCFG', ["<indexStem name=\"", $indexstem, "\" />"]);     
-	}
-	&write_line('COLCFG', ["<classifierList>"]);		
-	$started_classifiers = 1;
-      }
-      my $content = ''; #use buttonname first, then metadata
-      if ($classname eq "DateList") {
-	$content = "Date";
-      } else {
-	for (my $j=0; $j<scalar(@$cl); $j++) {
-	  my $arg = @$cl[$j];
-	  if ($arg eq "-buttonname"){
-	    $content = @$cl[$j+1];
-	    last;
-	  } elsif ($arg eq "-metadata") {
-	    $content = @$cl[$j+1];
-	  }
-	  
-	}
-      }
-      &write_line('COLCFG', ["<classifier name=\"", $name, "\" content=\"", $content, "\" />"]);
-    }     
-    if ($started_classifiers) {
-      # end the classifiers
-      &write_line('COLCFG', ["</classifierList>"]);
-      # close off the Browse service
-      &write_line('COLCFG', ["</serviceRack>"]);
-    }
-    
-    # the phind classifier is a separate service
-    if ($phind) {
-	# if phind classifier
-	&write_line('COLCFG', ["<serviceRack name=\"PhindPhraseBrowse\" />"]);
-    }
-
-	my $flaxActivities = $collectcfg->{"flaxActivity"};
-    foreach my $fa (@$flaxActivities) {
-    	# Six elements of the array for three attribute name/value pairs: name, desid, and lang.
-    	if(defined $fa and @$fa[0] =~ /\w/ and @$fa[1] =~ /\w/ and @$fa[2] =~ /\w/ and @$fa[3] =~ /\w/  and @$fa[4] =~ /\w/ and @$fa[5] =~ /\w/) {
-	    	&write_line('COLCFG', ["<serviceRack type=\"flaxActivity\" ", @$fa[0], "=\"", @$fa[1], "\" ", @$fa[2], "=\"", @$fa[3], "\" ", @$fa[4], "=\"", @$fa[5], "\" />"]);
-	    }
-    }  
-    
-    &write_line('COLCFG', ["</serviceRackList>"]);
-    &write_line('COLCFG', ["</buildConfig>"]);
-
-    close (COLCFG);
-  }
-
-
-#########################################################
-
-1;
Index: /gsdl/trunk/perllib/collConfigxml.pm
===================================================================
--- /gsdl/trunk/perllib/collConfigxml.pm	(revision 20094)
+++ /gsdl/trunk/perllib/collConfigxml.pm	(revision 20094)
@@ -0,0 +1,801 @@
+###########################################################################
+#
+# cfgread4gs3.pm --
+# 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.
+#
+###########################################################################
+
+# reads in configuration files of xml form
+
+package cfgread4gs3;
+use strict;
+no strict 'refs';
+no strict 'subs';
+
+# Wrapper that ensures the right version of XML::Parser is loaded given
+# the version of Perl being used.  Need to distinguish between Perl 5.6 and
+# Perl 5.8
+sub BEGIN {
+    my $perl_dir;
+
+    # Note: $] encodes the version number of perl
+    if ($]>5.008) { 
+	# perl 5.8.1 or above
+	$perl_dir = "perl-5.8";
+    }
+    elsif ($]<5.008) {
+	# assume perl 5.6
+	$perl_dir = "perl-5.6";
+    }
+    else {
+	print STDERR "Warning: Perl 5.8.0 is not a maintained release.\n";
+	print STDERR "         Please upgrade to a newer version of Perl.\n";
+	$perl_dir = "perl-5.8";
+    }
+
+    if ($ENV{'GSDLOS'} !~ /^windows$/i) {
+	# Use push to put this on the end, so an existing XML::Parser will be used by default
+	push (@INC, "$ENV{'GSDLHOME'}/perllib/cpan/$perl_dir");
+    }
+}
+
+use XML::Parser;
+
+# A mapping hash to resolve name discrepancy between gs2 and gs3.
+my $nameMap = {"key" => "value",
+	       "creator" => "creator",
+	       "maintainer" => "maintainer",
+	       "public" => "public",
+	       "infodb" => "infodbtype",
+	       "defaultIndex" => "defaultindex",
+	       "defaultLevel" => "defaultlevel",
+	       "name" => "collectionname",
+	       "description" => "collectionextra",
+	       "smallicon" => "iconcollectionsmall",
+	       "icon" => "iconcollection",
+	       "level" => "levels",
+	       "classifier" => "classify",
+	       "indexSubcollection" => "indexsubcollections",
+	       "indexLanguage" => "languages",
+	       "defaultIndexLanguage" => "defaultlanguage",
+	       "index" => "indexes",
+	       "plugin" => "plugin",
+	       "plugout" => "plugout",
+	       "indexOption" => "indexoptions",
+	       "searchType" => "searchtype",
+	       "languageMetadata" => "languagemetadata",
+	       "buildType" => "buildtype"
+	       };
+# A hash structure which is returned by sub read_cfg_file.
+my $data = {};
+
+my $repeatedBlock = q/^(browse|pluginList)$/;
+
+# use those unique attribute values to locate the text within the elements
+# creator, public, maintainer and within a displayItem.
+my $currentLocation = "";
+my $stringexp = q/^(creator|maintainer|public|buildType)$/;
+my $displayItemNames = q/^(name|description)$/;
+  
+# For storing the attributes during the StartTag subroutine, so that 
+# we can use it later in Text (or EndTag) subroutines
+my $currentAttrRef = undef; 
+
+my $currentLevel = "";
+
+# Count the elements with same name within the same block
+# ("plugin", "option")
+my $currentIndex = 0;
+my $arrayexp = q/^(index|level|indexSubcollection|indexLanguage)$/;
+my $arrayarrayexp= q/^(plugin|classifier)$/;
+my $hashexp = q/^(subcollection)$/; # add other element names that should be represented by hash expressions here
+my $hashhashexp = q/^(displayItem)$/; # add other (collectionmeta) element names that should be represented by hashes of hashes here.
+
+my $defaults = q/^(defaultIndex|defaultLevel|defaultIndexLanguage|languageMetadata)$/;
+
+sub StartTag {
+# Those marked with #@ will not be executed at the same time when this sub is being called
+# so that if/elsif is used to avoid unnecessary tests
+    my ($expat, $element) = @_;
+    
+    # See http://search.cpan.org/~msergeant/XML-Parser-2.36/Parser.pm#Stream
+    # %_ is a hash of all the attributes of this element, we want to store them so we can use the attributes
+    # when the textnode contents of the element are parsed in the subroutine Text (that's the handler for Text). 
+    $currentAttrRef = \%_; 
+
+    my $name = $_{'name'};
+    my $value = $_{'value'};
+    my $type = $_{'type'};
+
+    # for subcollections
+    my $filter = $_{'filter'};
+
+	# for flax activities
+	my $desid = $_{'desid'};
+	my $assigned = $_{'assigned'};
+	my $lang = $_{'lang'};
+	
+    #@ Marking repeated block
+    if ($element =~ /$repeatedBlock/) {
+	$currentIndex = 0;
+    }
+
+    #@ handling block metadataList
+    elsif (defined $name and $name =~ /$stringexp/){
+      $currentLocation = $name;
+    }
+    #@ handling default search index/level/indexLanguage and languageMetadata
+    elsif ($element =~ /$defaults/) {
+      if (defined $name and $name =~ /\w/) {
+	$data->{$nameMap->{$element}} = $name;
+      }
+    }
+
+    #@ handling the displayItems name and description (known as collectionname and collectionextra in GS2)
+    elsif($element eq "displayItemList") {
+	$currentLevel = "displayItemList"; # storing the parent if it is displayItemList
+    } 
+    elsif($element =~ /$hashhashexp/) { # can expand on this to check for other collectionmeta elements
+	if((!defined $assigned) || (defined $assigned and $assigned =~ /\w/ and $assigned eq "true")) {
+	    # either when there is no "assigned" attribute, or when assigned=true (for displayItems):
+	    $currentLocation = $name;
+	}
+    }
+
+    #@ Handling database type: gdbm or gdbm-txtgz, later jdbm.
+    elsif ($element eq "infodb") {
+      $data->{'infodbtype'} = $type;
+    }
+    
+    #@ Handling indexer: mgpp/mg/lucene; stringexp
+    elsif ($element eq "search") {
+      $data->{'buildtype'} = $type;
+    }
+
+    #@ Handling searchtype: plain,form; arrayexp
+    #elsif ($element eq "format" and defined $name and $name =~ /searchType/) {
+	#@ Handling searchtype: plain, form
+	#$currentLocation = $name;	
+    #}
+ 
+    #@ Handle index|level|indexSubcollection|indexLanguage
+    elsif ($element =~ /$arrayexp/) {
+      my $key = $nameMap->{$element};	
+      if (!defined $data->{$key}) {
+	$data->{$key} = [];
+      }
+
+      push (@{$data->{$key}},$name);
+    }
+
+    #@ indexoptions: accentfold/casefold/stem; arrayexp
+    elsif ($element eq "indexOption") {
+      $currentLevel = "indexOption";
+    }
+    if ($currentLevel eq "indexOption" and $element eq "option") {
+      my $key = $nameMap->{$currentLevel};	
+      if (!defined $data->{$key}) {
+	$data->{$key} = [];
+      }
+      push (@{$data->{$key}},$name);
+    }
+    #@ plugout options
+    elsif ($element eq "plugout") {
+	$currentLevel = "plugout";
+	my $key = $nameMap->{$currentLevel};	
+	if (!defined $data->{$key}) {
+	    $data->{$key} = [];
+	}
+	if(defined $name and $name ne ""){
+	    push (@{$data->{$key}},$name);
+	}
+	else{
+	   push (@{$data->{$key}},"GreenstoneXMLPlugout"); 
+	}
+    }
+    if ($currentLevel eq "plugout" and $element eq "option") {     
+	my $key = $nameMap->{$currentLevel};
+	if (defined $name and $name ne ""){
+	    push (@{$data->{$key}},$name);
+	}
+	if (defined $value and $value ne  ""){
+	    push (@{$data->{$key}},$value);
+	}
+    }
+
+    #@ use hash of hash of strings: hashexp
+    elsif ($element =~ /$hashexp/) {
+      if (!defined $data->{$element}) {
+	$data->{$element} = {};
+      }
+      if (defined $name and $name =~ /\w/) {
+	if (defined $filter and $filter =~ /\w/) {
+	  $data->{$element}->{$name} = $filter;
+
+	}
+      }
+    }
+
+    #@ Handling each classifier/plugin element
+    elsif ($element =~ /$arrayarrayexp/) {
+	# find the gs2 mapping name
+        $currentLevel = $element;
+        my $key = $nameMap->{$element};	
+
+	# define an array of array of strings	foreach $k (@{$data->{$key}}) {
+	if (!defined $data->{$key}) {
+	  $data->{$key} = [];
+	}
+	# Push classifier/plugin name (e.g. AZList) into $data as the first string
+ 	push (@{$data->{$key}->[$currentIndex]},$name);
+	#print $currentIndex."indexup\n";
+      }
+    
+    #@ Handling the option elements in each classifier/plugin element (as the following strings)
+    elsif ($currentLevel =~ /$arrayarrayexp/ and $element eq "option") {
+	# find the gs2 mapping name for classifier and plugin
+        my $key = $nameMap->{$currentLevel};	
+
+	if (defined $name and $name =~ /\w/) {
+	    push (@{$data->{$key}->[$currentIndex]}, $name);
+	}
+	if (defined $value and $value =~ /\w/) {
+            push (@{$data->{$key}->[$currentIndex]}, $value);
+	}
+
+    }
+    #@ Handling each flaxActivity element (arrayarrayexp)
+    elsif ($element eq "flaxActivity") {
+		if (!defined $data->{'flaxActivity'}) {
+			$data->{'flaxActivity'} = [];
+		}
+		if(defined $assigned and $assigned =~ /\w/ and $assigned eq "true") {
+		if (defined $name and $name =~ /\w/) {
+	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, 'name');
+	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, $name);
+		}
+	
+		if (defined $desid and $desid =~ /\w/) {
+	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, 'desid');
+	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, $desid);
+		}		
+	
+		if (defined $lang and $lang =~ /\w/) {
+	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, 'lang');
+	    	push (@{$data->{'flaxActivity'}->[$currentIndex]}, $lang);
+		}		
+		}
+    }
+}
+
+sub EndTag {
+    my ($expat, $element) = @_;
+    my $endTags = q/^(browse|pluginList|displayItemList)$/;   
+    if ($element =~ /$endTags/) {
+		$currentIndex = 0;
+		$currentLevel = "";
+    }
+    # $arrayarrayexp contains classifier|plugin
+    elsif($element =~ /$arrayarrayexp/ || $element eq "flaxActivity"){
+     	$currentIndex = $currentIndex + 1;
+    }
+}
+
+sub Text {
+    if (defined $currentLocation) { 
+	#@ Handling block metadataList(creator, maintainer, public)
+	if($currentLocation =~ /$stringexp/){
+	    #print $currentLocation;
+	    my $key = $nameMap->{$currentLocation};	
+	    $data->{$key} = $_;
+	    undef $currentLocation;
+	}
+	
+	#@ Handling displayItem metadata that are children of displayItemList
+	# that means we will be getting the collection's name and possibly description ('collectionextra' in GS2).
+	elsif($currentLevel eq "displayItemList" && $currentLocation =~ /$displayItemNames/) {
+	    my $lang = $currentAttrRef->{'lang'};
+	    my $name = $currentAttrRef->{'name'};
+	    
+	    # this is how data->collectionmeta's language is set in Greenstone 2. 
+	    # Need to be consistent, since export.pl accesses these values all in the same way
+	    if(!defined $lang) {
+		$lang = 'default';
+	    } else {
+		$lang = "[l=$lang]"; 
+	    }
+	    
+	    if(defined $name and $name =~ /$displayItemNames/) { # attribute name = 'name' || 'description'
+		# using $nameMap->$name resolves to 'collectionname' if $name='name' and 'collectionextra' if $name='description'
+		$data->{'collectionmeta'}->{$nameMap->{$name}}->{$lang} = $_; # the value is the Text parsed
+		#print STDERR "***Found: $nameMap->{$name} collectionmeta, lang is $lang. Value: $data->{'collectionmeta'}->{$nameMap->{$name}}->{$lang}\n";
+	    }
+	    undef $currentLocation;
+	}
+  
+	#@ Handling searchtype: plain,form; arrayexp
+	elsif (defined $currentLocation and $currentLocation =~ /searchType/) {
+	    # map 'searchType' into 'searchtype'
+	    my $key = $nameMap->{$currentLocation};
+	    # split it by ','
+	    my ($plain, $form) = split (",", $_);
+	    
+	    if (!defined $data->{$key}) {
+		$data->{$key} = [];
+	    }
+	    if (defined $plain and $plain =~ /\w/) {
+		push @{ $data->{$key} }, $plain;
+	    }
+	    if (defined $form and $form =~ /\w/) {
+		push @{ $data->{$key} }, $form;
+	    }
+	}
+    }	
+}
+
+# This sub is for debugging purposes
+sub Display {
+    # metadataList
+    foreach my $k (keys %{$data}) {
+	print STDERR "*** metadatalist key $k\n"; 
+    }
+  
+    print $data->{'creator'}."\n" if (defined $data->{'creator'});
+    print $data->{"maintainer"}."\n" if (defined $data->{"maintainer"});
+    print $data->{"public"}."\n" if (defined $data->{"public"});
+    print $data->{"defaultindex"}."\n" if (defined $data->{"defaultindex"});
+    print $data->{"defaultlevel"}."\n" if (defined $data->{"defaultlevel"});
+    print $data->{"buildtype"}."\n" if (defined $data->{"buildtype"});
+    print  join(",",@{$data->{"searchtype"}})."\n" if (defined $data->{"searchtype"});
+    print  join(",",@{$data->{'levels'}})."\n" if (defined $data->{'levels'});
+    print  join(",",@{$data->{'indexsubcollections'}})."\n" if (defined $data->{'indexsubcollections'});
+    print  join(",",@{$data->{'indexes'}})."\n" if (defined $data->{'indexes'});
+    print  join(",",@{$data->{'indexoptions'}})."\n" if (defined $data->{'indexoptions'});
+    print  join(",",@{$data->{'languages'}})."\n" if (defined $data->{'languages'});
+    print  join(",",@{$data->{'languagemetadata'}})."\n" if (defined $data->{'languagemetadata'});
+ 
+    if (defined $data->{'plugin'}) {
+	foreach $a (@{$data->{'plugin'}}) {
+	    print join(",",@$a);
+	    print "\n";
+	}
+    }
+    if (defined $data->{'classify'}) {
+	print "Classifiers: \n";
+	map { print join(",",@$_)."\n"; } @{$data->{'classify'}};
+    }
+    
+    if (defined $data->{'subcollection'}) {
+	foreach my $key (keys %{$data->{'subcollection'}}) {
+	    print "subcollection ".$key." ".$data->{'subcollection'}->{$key}."\n";
+	}
+    }
+}
+sub Doctype {
+    my ($expat, $name, $sysid, $pubid, $internal) = @_;
+
+    # allow the short-lived and badly named "GreenstoneDirectoryMetadata" files 
+    # to be processed as well as the "DirectoryMetadata" files which should now
+    # be created by import.pl
+    die if ($name !~ /^(Greenstone)?DirectoryMetadata$/);
+}
+
+# This Char function overrides the one in XML::Parser::Stream to overcome a
+# problem where $expat->{Text} is treated as the return value, slowing
+# things down significantly in some cases.
+sub Char {
+    if ($]<5.008) {
+	use bytes;  # Necessary to prevent encoding issues with XML::Parser 2.31+ and Perl 5.6
+    }
+    $_[0]->{'Text'} .= $_[1];
+    return undef;
+}
+
+# Reads in the model collection configuration file, collectionConfig.xml,
+# into a structure which complies with the one used by gs2 (i.e. one read
+# in by &cfgread::read_cfg_file).
+sub read_cfg_file {
+    my ($filename) = @_;
+    $data = {};
+    if (($filename !~ /collectionConfig\.xml$/ && $filename !~ /buildConfig\.xml$/) || !-f $filename) {
+        return undef;
+    }
+
+    # create XML::Parser object for parsing metadata.xml files
+    my $parser;
+    if ($]<5.008) {
+	# Perl 5.6
+	$parser = new XML::Parser('Style' => 'Stream',
+				  'Handlers' => {'Char' => \&Char,
+						 'Doctype' => \&Doctype
+						 });
+    }
+    else {
+	# Perl 5.8
+	$parser = new XML::Parser('Style' => 'Stream',
+				  'ProtocolEncoding' => 'ISO-8859-1',
+				  'Handlers' => {'Char' => \&Char,
+						 'Doctype' => \&Doctype
+						 });
+    }
+
+    if (!open (COLCFG, $filename)) {
+	print STDERR "cfgread::read_cfg_file couldn't read the cfg file $filename\n";
+    } else {
+
+      $parser->parsefile ($filename);# (COLCFG);
+      close (COLCFG);
+    }
+
+    #&Display; 
+    return $data;
+}
+
+
+sub write_line {
+    my ($filehandle, $line) = @_;
+    print $filehandle join ("", @$line), "\n";
+}
+
+# Create the buildConfig.xml file for a specific collection
+sub write_cfg_file {
+    # this sub is called in make_auxiliary_files() in basebuilder.pm
+    # the received args: $buildoutfile - destination file: buildConfig.xml
+    #                    $buildcfg - all build options, eg, disable_OAI
+    #                    $collectcfg - contents of collectionConfig.xml read in by read_cfg_file sub in cfgread4gs3.pm.
+    my ($buildoutfile, $buildcfg, $collectcfg, $disable_OAI) = @_;
+    my $line = [];
+
+    if (!open (COLCFG, ">$buildoutfile")) {
+	print STDERR "cfgread4gs3::write_cfg_file couldn't write the build config file $buildoutfile\n";
+	die;
+    }
+
+    &write_line('COLCFG', ["<buildConfig xmlns:gsf=\"http://www.greenstone.org/greenstone3/schema/ConfigFormat\">"]);  
+
+    # output building metadata to build config file 
+    my $buildtype;
+    if (defined $buildcfg->{"buildtype"}) {
+	$buildtype = $buildcfg->{"buildtype"};
+    } else {
+	$buildtype = "mgpp";
+    }
+    my $numdocs;
+    if (defined $buildcfg->{"numdocs"}) {
+      $numdocs = $buildcfg->{"numdocs"};
+    }
+    &write_line('COLCFG', ["<metadataList>"]);
+    &write_line('COLCFG', ["<metadata name=\"numDocs\">", $numdocs, "</metadata>"]);
+    &write_line('COLCFG', ["<metadata name=\"buildType\">", $buildtype, "</metadata>"]);
+    &write_line('COLCFG', ["</metadataList>"]);
+
+    my $service_type = "MGPP";
+    if ($buildtype eq "mg") {
+	$service_type = "MG";
+    } elsif ($buildtype eq "lucene") {
+	$service_type = "Lucene";
+    }
+
+    # output serviceRackList
+    &write_line('COLCFG', ["<serviceRackList>"]);
+
+    # This serviceRack enables the collection to provide the oai metadata retrieve service, which is served by the OAIPMH.java class
+    # For each collection, we write the following serviceRack in the collection's buildConfig.xml file if the 'disable_OAI' argument is not checked in the GLI (or equivalently, a 'disable_OAI' flag is not specified on the command line). There are also other configurations in the OAIConfig.xml.
+    if ($disable_OAI == 0) {
+      &write_line('COLCFG', ["<serviceRack name=\"OAIPMH\">"]);
+      if (defined $buildcfg->{'indexstem'}) {
+        my $indexstem = $buildcfg->{'indexstem'};
+        &write_line('COLCFG', ["<indexStem name=\"", $indexstem, "\" />"]);     
+      }
+      &write_line('COLCFG', ["</serviceRack>"]);
+    }    
+    # do the search service 
+    &write_line('COLCFG', ["<serviceRack name=\"GS2", $service_type, "Search\">"]);
+    if (defined $buildcfg->{'indexstem'}) {
+      my $indexstem = $buildcfg->{'indexstem'};
+      &write_line('COLCFG', ["<indexStem name=\"", $indexstem, "\" />"]);     
+    }
+
+    #indexes
+    # maps index name to shortname
+    my $indexmap = {};
+    # keeps the order for indexes
+    my @indexlist = ();
+    
+    my $defaultindex = "";
+    my $first = 1;
+    my $maptype = "indexfieldmap";
+    if ($buildtype eq "mg") {
+	$maptype = "indexmap";
+    }
+
+    #map {print $_."\n"} keys %$buildcfg;
+
+    if (defined $buildcfg->{$maptype}) {
+	my $indexmap_t = $buildcfg->{$maptype};
+       foreach my $i (@$indexmap_t) {
+    my ($k, $v) = $i =~ /^(.*)\-\>(.*)$/;
+	    $indexmap->{$k} = $v;
+	    push @indexlist, $k;
+	    if ($first) {
+		$defaultindex = $v;
+		$first = 0;
+	    }	
+	}
+	# now if the user has assigned a default index, we use it 
+	if (defined $collectcfg->{"defaultindex"}) {
+	  $defaultindex = $indexmap->{$collectcfg->{"defaultindex"}};
+	}
+	
+    } else {
+	print STDERR "$maptype not defined";
+    }  
+    #for each index in indexList, write them out
+    &write_line('COLCFG', ["<indexList>"]);
+    foreach my $i (@indexlist) {
+	my $index = $indexmap->{$i};
+	&write_line('COLCFG', ["<index name=\"", $i, "\" ", "shortname=\"", $index, "\" />"]);
+    }	
+    &write_line('COLCFG', ["</indexList>"]);
+
+    
+    &write_line('COLCFG', ["<defaultIndex shortname=\"", $defaultindex, "\" />"]);
+
+
+    # do indexOptionList
+    if ($buildtype eq "mg" || $buildtype eq "mgpp") {
+        &write_line('COLCFG', ["<indexOptionList>"]);
+	my $stemindexes = 3; # default is stem and casefold
+	if (defined $buildcfg->{'stemindexes'} && $buildcfg->{'stemindexes'} =~ /^\d+$/ ) {
+	    $stemindexes = $buildcfg->{'stemindexes'};
+	}
+	&write_line('COLCFG', ["<indexOption name=\"stemIndexes\" value=\"", $stemindexes, "\" />"]);
+	
+	my $maxnumeric = 4; # default
+	if (defined $buildcfg->{'maxnumeric'} && $buildcfg->{'maxnumeric'} =~ /^\d+$/) {
+	    $maxnumeric = $buildcfg->{'maxnumeric'};
+	}
+	&write_line('COLCFG', ["<indexOption name=\"maxnumeric\" value=\"", $maxnumeric, "\" />"]);
+        &write_line('COLCFG', ["</indexOptionList>"]);
+    }
+
+    #  levelList
+    my $levelmap = {};
+    my @levellist = ();
+    my $default_search_level = "Doc";
+    my $default_retrieve_level = "Doc";
+    my $default_db_level = "Doc";
+    $first = 1;
+    if ($buildtype eq "mgpp" || $buildtype eq "lucene") {
+	if (defined $buildcfg->{'levelmap'}) {
+	    my $levelmap_t = $buildcfg->{'levelmap'};
+	    foreach my $l (@$levelmap_t) {
+		my ($key, $val) = $l =~ /^(.*)\-\>(.*)$/;
+		$levelmap->{$key} = $val;
+		push @levellist, $key;
+		if ($first) {
+		    # let default search level follow the first level in the level list
+		    $default_search_level = $val;
+		    # retrieve/database levels may get modified later if text level is defined
+		    $default_retrieve_level = $val;
+		    $default_db_level = $val;
+		    $first = 0;
+		}
+	    }
+	}
+	# the default level assigned by the user is no longer ignored [Shaoqun], but the retrievel level stays the same. 
+       	#if (defined $collectcfg->{"defaultlevel"}) {
+	$default_search_level = $levelmap->{$collectcfg->{"defaultlevel"}};
+        #  $default_retrieve_level = $default_search_level;
+	#}
+	
+	if (defined $buildcfg->{'textlevel'}) {
+	   # let the retrieve/database levels always follow the textlevel
+           $default_retrieve_level = $buildcfg->{'textlevel'};
+	   $default_db_level = $buildcfg->{'textlevel'};
+		 
+	}
+    }
+    #for each level in levelList, write them out
+	if ($buildtype ne "mg") {
+    &write_line('COLCFG', ["<levelList>"]);
+    foreach my $lv (@levellist) {
+	my $level = $levelmap->{$lv};
+        &write_line('COLCFG', ["<level name=\"", $lv, "\" shortname=\"", $level, "\" />"]);
+    }	
+    &write_line('COLCFG', ["</levelList>"]);
+    }
+    # add in defaultLevel as the same level as indexLevelList, making the reading job easier
+    if ($buildtype eq "lucene" || $buildtype eq "mgpp") {
+	&write_line('COLCFG', ["<defaultLevel shortname=\"", $default_search_level, "\" />"]);
+    }
+    if ($buildtype eq "lucene" || $buildtype eq "mgpp") {
+        &write_line('COLCFG', ["<defaultDBLevel shortname=\"", $default_db_level, "\" />"]);
+    }
+    # do searchTypeList
+    if ($buildtype eq "mgpp" || $buildtype eq "lucene") {
+	  &write_line('COLCFG', ["<searchTypeList>"]);
+      
+      if (defined $buildcfg->{"searchtype"}) {
+	  my $searchtype_t = $buildcfg->{"searchtype"};
+	  foreach my $s (@$searchtype_t) {
+	  &write_line('COLCFG', ["<searchType name=\"", $s, "\" />"]);
+	}
+      } else {
+	  &write_line('COLCFG', ["<searchType name=\"plain\" />"]);
+	  &write_line('COLCFG', ["<searchType name=\"form\" />"]);
+      }
+	  &write_line('COLCFG', ["</searchTypeList>"]);
+    }
+
+    # do indexLanguageList [in collect.cfg: languages; in build.cfg: languagemap]
+    $first = 1;
+    my $default_lang = "";
+    my $default_lang_short = "";
+    if (defined $buildcfg->{"languagemap"}) {
+      &write_line('COLCFG', ["<indexLanguageList>"]);
+
+      my $langmap_t = $buildcfg->{"languagemap"};
+      foreach my $l (@$langmap_t) {
+	my ($k, $v) = $l =~ /^(.*)\-\>(.*)$/; 
+
+	&write_line('COLCFG', ["<indexLanguage name=\"", $k, "\" shortname=\"", $v, "\" />"]);
+	if ($first) {
+	  $default_lang = $k; #name
+	  $default_lang_short = $v; #shortname
+	  $first = 0;
+	}
+      }
+
+      &write_line('COLCFG', ["</indexLanguageList>"]);
+      # now if the user has assigned a default language (as "en", "ru" etc.)
+      if (defined $collectcfg->{"defaultlanguage"}) {
+	$default_lang = $collectcfg->{"defaultlanguage"};
+      }
+      &write_line('COLCFG', ["<defaultIndexLanguage name=\"", $default_lang, "\" shortname=\"", $default_lang_short, "\" />"]);
+    }
+
+
+    # do indexSubcollectionList
+    my $default_subcol = "";# make it in sub scope to be used in the concatenation
+    if (defined $buildcfg->{'subcollectionmap'}) {
+      &write_line('COLCFG', ["<indexSubcollectionList>"]);
+      my $subcolmap = {};
+      my @subcollist = ();
+      $first = 1;
+      my $subcolmap_t = $buildcfg->{'subcollectionmap'};
+      foreach my $l (@$subcolmap_t) {
+	my ($k, $v) = $l =~ /^(.*)\-\>(.*)$/;
+	$subcolmap->{$k} = $v;
+	push @subcollist, $k;
+	if ($first) {
+	  $default_subcol = $v;
+	  $first = 0;
+	}
+      }
+      foreach my $sl (@subcollist) {
+	my $subcol = $subcolmap->{$sl};
+	&write_line('COLCFG', ["<indexSubcollection name=\"", $sl, "\" shortname=\"", $subcol, "\" />"]);
+      }	
+
+      &write_line('COLCFG', ["</indexSubcollectionList>"]);
+      &write_line('COLCFG', ["<defaultIndexSubcollection shortname=\"", $default_subcol, "\" />"]);
+    }
+      
+    # close off search service 
+    &write_line('COLCFG', ["</serviceRack>"]);
+
+    # do the retrieve service
+    &write_line('COLCFG', ["<serviceRack name=\"GS2", $service_type, "Retrieve\">"]);
+
+    # do default index 
+    if (defined $buildcfg->{"languagemap"}) {
+	&write_line('COLCFG', ["<defaultIndexLanguage shortname=\"", $default_lang, "\" />"]);
+    }
+    if (defined $buildcfg->{'subcollectionmap'}) {
+	&write_line('COLCFG', ["<defaultIndexSubcollection shortname=\"", $default_subcol, "\" />"]);
+    }
+    if ($buildtype eq "mg") {
+      &write_line('COLCFG', ["<defaultIndex shortname=\"", $defaultindex, "\" />"]);
+    }
+
+    if (defined $buildcfg->{'indexstem'}) {
+      my $indexstem = $buildcfg->{'indexstem'};
+      &write_line('COLCFG', ["<indexStem name=\"", $indexstem, "\" />"]);     
+    }
+    if ($buildtype eq "mgpp" || $buildtype eq "lucene") {
+      &write_line('COLCFG', ["<defaultLevel shortname=\"", $default_retrieve_level, "\" />"]);
+    }
+    &write_line('COLCFG', ["</serviceRack>"]);
+
+    # do the browse service
+    my $count = 1;
+    my $phind = 0;
+    my $started_classifiers = 0;
+
+    my $classifiers = $collectcfg->{"classify"};
+    foreach my $cl (@$classifiers) {
+      my $name = "CL$count";
+      $count++;
+      my ($classname) = @$cl[0];
+      if ($classname =~ /^phind$/i) {
+	$phind=1;
+	#should add it into coll config classifiers
+	next;
+      }
+      
+      if (not $started_classifiers) {
+	&write_line('COLCFG', ["<serviceRack name=\"GS2Browse\">"]);
+	if (defined $buildcfg->{'indexstem'}) {
+	  my $indexstem = $buildcfg->{'indexstem'};
+	  &write_line('COLCFG', ["<indexStem name=\"", $indexstem, "\" />"]);     
+	}
+	&write_line('COLCFG', ["<classifierList>"]);		
+	$started_classifiers = 1;
+      }
+      my $content = ''; #use buttonname first, then metadata
+      if ($classname eq "DateList") {
+	$content = "Date";
+      } else {
+	for (my $j=0; $j<scalar(@$cl); $j++) {
+	  my $arg = @$cl[$j];
+	  if ($arg eq "-buttonname"){
+	    $content = @$cl[$j+1];
+	    last;
+	  } elsif ($arg eq "-metadata") {
+	    $content = @$cl[$j+1];
+	  }
+	  
+	}
+      }
+      &write_line('COLCFG', ["<classifier name=\"", $name, "\" content=\"", $content, "\" />"]);
+    }     
+    if ($started_classifiers) {
+      # end the classifiers
+      &write_line('COLCFG', ["</classifierList>"]);
+      # close off the Browse service
+      &write_line('COLCFG', ["</serviceRack>"]);
+    }
+    
+    # the phind classifier is a separate service
+    if ($phind) {
+	# if phind classifier
+	&write_line('COLCFG', ["<serviceRack name=\"PhindPhraseBrowse\" />"]);
+    }
+
+	my $flaxActivities = $collectcfg->{"flaxActivity"};
+    foreach my $fa (@$flaxActivities) {
+    	# Six elements of the array for three attribute name/value pairs: name, desid, and lang.
+    	if(defined $fa and @$fa[0] =~ /\w/ and @$fa[1] =~ /\w/ and @$fa[2] =~ /\w/ and @$fa[3] =~ /\w/  and @$fa[4] =~ /\w/ and @$fa[5] =~ /\w/) {
+	    	&write_line('COLCFG', ["<serviceRack type=\"flaxActivity\" ", @$fa[0], "=\"", @$fa[1], "\" ", @$fa[2], "=\"", @$fa[3], "\" ", @$fa[4], "=\"", @$fa[5], "\" />"]);
+	    }
+    }  
+    
+    &write_line('COLCFG', ["</serviceRackList>"]);
+    &write_line('COLCFG', ["</buildConfig>"]);
+
+    close (COLCFG);
+  }
+
+
+#########################################################
+
+1;
