UserTag xml-generator Order type
UserTag xml-generator addAttr
UserTag xml-generator hasEndTag
UserTag xml-generator Interpolate
UserTag xml-generator Documentation <<EOD
=head2 xml_generator

This UserTag generates XML tags based upon one of two types of
data:

=over 4

=item delimited

Accepts a delimited and separated (default is TAB delimiter and newline sepraror)
list of records such as that generated by an C<[item-list]>, C<[sql]>,
or C<[loop search=""]> MML tag.

=item session

When the type is not delimited, it can contain any hash reference into
the Minivend session. Examples are:

	values       The form values
	scratch      Scratch values
	errors       Error values
	other        Any other Session key, for example "source" for
	             [data session source]

If the value is a hash, then it will be sent as an XML record with the
top level equal to C<session>, and a second_level tag equal to the hash
name, and keys as separate XML container tags. If the paramater I<that is equal
to the type> is given, only those fields will be shown. Otherwise the
entire hash will be shown. For example, this tag:

	[xml-generator type="values" values="fname lname"][/xml-generator]

will generate:

	<session>
		<values>
			<fname>First</fname>
			<lname>Last</lname>
		</values>
	</session>

it is a scalar, then only the second level will be done:

	[xml-generator type="cybercash_id"][/xml-generator]
	
will do the equivalent of:

	<session>
		<cybercash_id>[data session cybercash_id]</cybercash_id>
	</session>

So bringing it all together, the following:

	[xml-generator	type="values scratch source"
					values="fname lname"
					scratch="downloads"][/xml-generator]

will generate:

	<session>
		<values>
			<fname>First</fname>
			<lname>Last</lname>
		</values>
		<scratch>
			<downloads>0</downloads>
		</scratch>
		<source>Partner1</source>
	</session>

=back

Other parameters include:

=over 4

=item toplevel_tag

The toplevel tag name to use. Defaults to C<table> for the delimited type,
and C<session> for the other.

=item table_name

A table name to output for the delimited type, i.e. 
C<<>C<table name="table_name">C<>>.

=item attributes

The attributes (if any) to pass on to the top level tag. For instance,

	[xml-generator
			attributes="date"
			date="[tag time]%d-%b-%Y[/tag]"
			toplevel_tag=order
			] 

will generate a toplevel tag pair of:

	<order date="05-Mar-2000">
	</order>

=item no_second

Prevents the second-level tags from being generated. Extending the
last example in the C<session> type above, this

	[xml-generator	type="values scratch source"
					no_second=1
					values="fname lname"
					scratch="downloads"][/xml-generator]


will generate:

	<session>
		<fname>First</fname>
		<lname>Last</lname>
		<downloads>0</downloads>
		<source>Partner1</source>
	</session>

	

EOD

UserTag xml-generator Routine <<EOR
sub {
	my ($type, $opt, $body) = @_;

	my @fields;
	my @lines;
	my $out;
	my $attr_string = '';
	if($opt->{attributes}) {
		my @attr = split /[\s,]+/, $opt->{attributes};
		for(@attr) {
			next unless length $opt->{$_};
			my $v = $opt->{$_};
			$v =~ s/"/\\"/g;
			$attr_string .= qq{\n\t$_="$v"};
		}
	}
	my %hash = (
					spacer => '[\s,]+',
					separator => "\n",
					delimiter => "\t",
					joiner => "\n",
					n => "\n",
					r => "\r",
					f => "\f",
					t => "\t",
					0 => "\0",
				);
	for(qw/separator delimiter joiner spacer/) {
		if($opt->{$_}) {
			$opt->{$_} =~ s/\\([nrf0])/$hash{$1}/g;
		}
		else {
			$opt->{$_} = $hash{$_};
		}
	}

	$type = 'delimited' unless $type;
	if($type eq 'delimited') {
		my $delim = $opt->{delimiter};
		if($opt->{field_names}) {
			@fields = grep /\S/, split /[\s,]+/, $opt->{field_names};	
		}
		else {
			$body =~ s/^(.*)\r?\n//;
			$opt->{field_names} = $1;
			$opt->{field_names} =~ s/\s+$//;
			$opt->{field_names} =~ s/^\s+//;
			@fields = grep /\S/, split /\t/, $opt->{field_names};	
		}
		$body =~ s/\s+$//;
		@lines = split /$opt->{separator}/, $body;
		$out = '<';
		$out .= $opt->{toplevel_tag} || 'table';
		$out .= $attr_string;
		$out .= ">\n";
		my $rtag = $opt->{record_tag} || 'record';
		my $keypos = 0;
		if($opt->{key_name}) {
			my $i = -1;
			my $found;
			for (@fields) {
				$i++;
				next unless $_ eq $opt->{key_name};
				$found = 1;
				last;
			}
			$keypos = $i if $found;
		}
		for(@lines) {
			warn "keypos=$keypos\n";
			my @f = split /$delim/o, $_;
			my $key = $f[$keypos];
			$key =~ s/"/\\"/g;
			$out .= qq{\t<$rtag key="$key">\n};
			for (my $i = 0; $i < scalar @fields; $i++) {
				$out .= qq{\t\t<$fields[$i]>$f[$i]</$fields[$i]>\n};
			}
			$out .= qq{\t</$rtag>\n};
		}
		$out .= "</";
		$out .= $opt->{toplevel_tag} || 'table';
		$out .= ">";
	}
	else {
		my @ones = grep /\S/, split /$opt->{spacer}/, $type;
		$out = '<';
		$out .= $opt->{toplevel_tag} || 'session';
		$out .= $attr_string;
		$out .= ">\n";
		my @keys;
		for(@ones) {
			my $ref = $_ eq 'CGI' ? \%CGI::values : ($Vend::Session->{$_} || {});
			if($opt->{$_}) {
				@keys = split /$opt->{spacer}/o, $opt->{$_};
			}
			else {
				@keys = keys %$ref;
			}
			my $spacer;
			if($opt->{no_second}) {
				$spacer = "\t";
			}
			else {
				$out .= qq{\t<$_>\n};
				$spacer = "\t\t";
			}
			foreach my $k (@keys) {
				$out .= qq{$spacer<$k>$ref->{$k}</$k>$opt->{separator}};
			}
			$out .= qq{\t</$_>\n}
				unless $opt->{no_second};
		}
		$out .= "</";
		$out .= $opt->{toplevel_tag} || 'session';
		$out .= ">";
	}
	return $out;
}
EOR
