19f91b7e3SAndre Fischer#**************************************************************
29f91b7e3SAndre Fischer#
39f91b7e3SAndre Fischer#  Licensed to the Apache Software Foundation (ASF) under one
49f91b7e3SAndre Fischer#  or more contributor license agreements.  See the NOTICE file
59f91b7e3SAndre Fischer#  distributed with this work for additional information
69f91b7e3SAndre Fischer#  regarding copyright ownership.  The ASF licenses this file
79f91b7e3SAndre Fischer#  to you under the Apache License, Version 2.0 (the
89f91b7e3SAndre Fischer#  "License"); you may not use this file except in compliance
99f91b7e3SAndre Fischer#  with the License.  You may obtain a copy of the License at
109f91b7e3SAndre Fischer#
119f91b7e3SAndre Fischer#    http://www.apache.org/licenses/LICENSE-2.0
129f91b7e3SAndre Fischer#
139f91b7e3SAndre Fischer#  Unless required by applicable law or agreed to in writing,
149f91b7e3SAndre Fischer#  software distributed under the License is distributed on an
159f91b7e3SAndre Fischer#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
169f91b7e3SAndre Fischer#  KIND, either express or implied.  See the License for the
179f91b7e3SAndre Fischer#  specific language governing permissions and limitations
189f91b7e3SAndre Fischer#  under the License.
199f91b7e3SAndre Fischer#
209f91b7e3SAndre Fischer#**************************************************************
219f91b7e3SAndre Fischer
229f91b7e3SAndre Fischerpackage installer::patch::ReleasesList;
239f91b7e3SAndre Fischer
249f91b7e3SAndre Fischeruse XML::Parser;
259f91b7e3SAndre Fischeruse File::Spec;
269f91b7e3SAndre Fischer
279f91b7e3SAndre Fischeruse strict;
289f91b7e3SAndre Fischer
299f91b7e3SAndre Fischer=head1 NAME
309f91b7e3SAndre Fischer
319f91b7e3SAndre Fischer    package installer::patch::ReleasesList  -  Functions for accessing the instsetoo_native/data/releases.xml file
329f91b7e3SAndre Fischer
339f91b7e3SAndre Fischer=cut
349f91b7e3SAndre Fischer
359f91b7e3SAndre Fischer
369f91b7e3SAndre Fischermy $Instance = undef;
379f91b7e3SAndre Fischer
389f91b7e3SAndre Fischer=head2 Instance()
399f91b7e3SAndre Fischer
409f91b7e3SAndre Fischer    Return the singleton instance.
419f91b7e3SAndre Fischer
429f91b7e3SAndre Fischer=cut
439f91b7e3SAndre Fischersub Instance()
449f91b7e3SAndre Fischer{
459f91b7e3SAndre Fischer    if ( ! defined $Instance)
469f91b7e3SAndre Fischer    {
479f91b7e3SAndre Fischer        $Instance = new installer::patch::ReleasesList(
489f91b7e3SAndre Fischer            File::Spec->catfile($ENV{'SRC_ROOT'}, "instsetoo_native", "data", "releases.xml"));
499f91b7e3SAndre Fischer    }
509f91b7e3SAndre Fischer    return $Instance;
519f91b7e3SAndre Fischer}
529f91b7e3SAndre Fischer
539f91b7e3SAndre Fischer
549f91b7e3SAndre Fischer
559f91b7e3SAndre Fischer
569f91b7e3SAndre Fischer=head2 new($class, $filename)
579f91b7e3SAndre Fischer
589f91b7e3SAndre Fischer    Internal constructor.  Don't call.
599f91b7e3SAndre Fischer
609f91b7e3SAndre Fischer=cut
619f91b7e3SAndre Fischersub new ($$)
629f91b7e3SAndre Fischer{
639f91b7e3SAndre Fischer    my ($class, $filename) = @_;
649f91b7e3SAndre Fischer
659f91b7e3SAndre Fischer    my $self = {
669f91b7e3SAndre Fischer        'releases' => []
679f91b7e3SAndre Fischer    };
689f91b7e3SAndre Fischer    bless($self, $class);
699f91b7e3SAndre Fischer
709f91b7e3SAndre Fischer
719f91b7e3SAndre Fischer    $self->Read($filename);
729f91b7e3SAndre Fischer
739f91b7e3SAndre Fischer
749f91b7e3SAndre Fischer    return $self;
759f91b7e3SAndre Fischer}
769f91b7e3SAndre Fischer
779f91b7e3SAndre Fischer
789f91b7e3SAndre Fischer
799f91b7e3SAndre Fischer
809f91b7e3SAndre Fischer=head2 GetFirstChild ($node, $child_name)
819f91b7e3SAndre Fischer
829f91b7e3SAndre Fischer    Internal function that returns the first child.  Use only when the
839f91b7e3SAndre Fischer    first child is the (expected) only child in a list.
849f91b7e3SAndre Fischer
859f91b7e3SAndre Fischer=cut
869f91b7e3SAndre Fischersub GetFirstChild ($$)
879f91b7e3SAndre Fischer{
889f91b7e3SAndre Fischer    my ($node, $child_name) = @_;
899f91b7e3SAndre Fischer
909f91b7e3SAndre Fischer    if ( ! defined $node)
919f91b7e3SAndre Fischer    {
929f91b7e3SAndre Fischer        return undef;
939f91b7e3SAndre Fischer    }
949f91b7e3SAndre Fischer    else
959f91b7e3SAndre Fischer    {
969f91b7e3SAndre Fischer        my $value = $node->{$child_name};
979f91b7e3SAndre Fischer        if (ref($value) eq 'ARRAY')
989f91b7e3SAndre Fischer        {
999f91b7e3SAndre Fischer            return $value->[0];
1009f91b7e3SAndre Fischer        }
1019f91b7e3SAndre Fischer        else
1029f91b7e3SAndre Fischer        {
1039f91b7e3SAndre Fischer            return $value;
1049f91b7e3SAndre Fischer        }
1059f91b7e3SAndre Fischer    }
1069f91b7e3SAndre Fischer}
1079f91b7e3SAndre Fischer
1089f91b7e3SAndre Fischer
1099f91b7e3SAndre Fischer
1109f91b7e3SAndre Fischer
1119f91b7e3SAndre Fischer=head2 GetText ($node)
1129f91b7e3SAndre Fischer
1139f91b7e3SAndre Fischer    Internal function that returns the trimmed text content of a node.
1149f91b7e3SAndre Fischer
1159f91b7e3SAndre Fischer=cut
1169f91b7e3SAndre Fischersub GetText ($;$)
1179f91b7e3SAndre Fischer{
1189f91b7e3SAndre Fischer    my ($node, $default_text) = @_;
1199f91b7e3SAndre Fischer
1209f91b7e3SAndre Fischer    if ( ! defined $node)
1219f91b7e3SAndre Fischer    {
1229f91b7e3SAndre Fischer        if (defined $default_text)
1239f91b7e3SAndre Fischer        {
1249f91b7e3SAndre Fischer            return $default_text;
1259f91b7e3SAndre Fischer        }
1269f91b7e3SAndre Fischer        else
1279f91b7e3SAndre Fischer        {
1289f91b7e3SAndre Fischer            return "";
1299f91b7e3SAndre Fischer        }
1309f91b7e3SAndre Fischer    }
1319f91b7e3SAndre Fischer    else
1329f91b7e3SAndre Fischer    {
1339f91b7e3SAndre Fischer        my $text = $node->{'__text__'};
1349f91b7e3SAndre Fischer        $text =~ s/(^\s+|\s+$)//g;
1359f91b7e3SAndre Fischer        return $text;
1369f91b7e3SAndre Fischer    }
1379f91b7e3SAndre Fischer}
1389f91b7e3SAndre Fischer
1399f91b7e3SAndre Fischer
1409f91b7e3SAndre Fischer
1419f91b7e3SAndre Fischersub GetAttribute ($$)
1429f91b7e3SAndre Fischer{
1439f91b7e3SAndre Fischer    my ($node, $attribute_name) = @_;
1449f91b7e3SAndre Fischer
1459f91b7e3SAndre Fischer    my $attributes = $node->{'__attributes__'};
1469f91b7e3SAndre Fischer    if ( ! defined $attributes)
1479f91b7e3SAndre Fischer    {
1489f91b7e3SAndre Fischer        return undef;
1499f91b7e3SAndre Fischer    }
1509f91b7e3SAndre Fischer    else
1519f91b7e3SAndre Fischer    {
1529f91b7e3SAndre Fischer        return $attributes->{$attribute_name};
1539f91b7e3SAndre Fischer    }
1549f91b7e3SAndre Fischer}
1559f91b7e3SAndre Fischer
1569f91b7e3SAndre Fischer
1579f91b7e3SAndre Fischer
1589f91b7e3SAndre Fischer
1599f91b7e3SAndre Fischersub PrintNode($$);
160*d575d58fSAndre Fischer
161*d575d58fSAndre Fischer=head2 ReadDomTree ($filename)
162*d575d58fSAndre Fischer
163*d575d58fSAndre Fischer    Read the dom tree for the XML in $filename.
164*d575d58fSAndre Fischer
165*d575d58fSAndre Fischer    Note that
166*d575d58fSAndre Fischer    a) this was initially written for another XML library that provided the dom tree directly.
167*d575d58fSAndre Fischer    b) the dom tree creation is basic and simple but good enough for the current format.
168*d575d58fSAndre Fischer       When the format should change substantially, then we may need a better parser.
169*d575d58fSAndre Fischer
170*d575d58fSAndre Fischer=cut
1719f91b7e3SAndre Fischersub ReadDomTree ($)
1729f91b7e3SAndre Fischer{
1739f91b7e3SAndre Fischer    my ($filename) = @_;
1749f91b7e3SAndre Fischer
1759f91b7e3SAndre Fischer    my $root = {};
1769f91b7e3SAndre Fischer    my $data = {
1779f91b7e3SAndre Fischer        'current_node' => $root,
1789f91b7e3SAndre Fischer        'node_stack' => []
1799f91b7e3SAndre Fischer    };
1809f91b7e3SAndre Fischer    my $parser = new XML::Parser(
1819f91b7e3SAndre Fischer        'Handlers' => {
1829f91b7e3SAndre Fischer            'Start' => sub {HandleStartTag($data, @_)},
1839f91b7e3SAndre Fischer            'End' => sub{HandleEndTag($data, @_)},
1849f91b7e3SAndre Fischer            'Char' => sub{HandleText($data, @_)}
1859f91b7e3SAndre Fischer        });
1869f91b7e3SAndre Fischer    $parser->parsefile($filename);
1879f91b7e3SAndre Fischer
1889f91b7e3SAndre Fischer#    PrintNode("", $root);
1899f91b7e3SAndre Fischer
1909f91b7e3SAndre Fischer    return $root;
1919f91b7e3SAndre Fischer}
1929f91b7e3SAndre Fischer
1939f91b7e3SAndre Fischer
1949f91b7e3SAndre Fischer
1959f91b7e3SAndre Fischer
196*d575d58fSAndre Fischer=head HandleStartTag ($data, $expat, $element_name, @attributes)
197*d575d58fSAndre Fischer
198*d575d58fSAndre Fischer    Callback for start tags.
199*d575d58fSAndre Fischer
200*d575d58fSAndre Fischer    A new hash is appended to the array that is referenced by the parent by $element_name.
201*d575d58fSAndre Fischer    That means that when this function ends there the new hash can be referenced by
202*d575d58fSAndre Fischer        my $parent = $data->{'node_stack'}->[-1];
203*d575d58fSAndre Fischer        my $new_hash = $parent->{$element_name}->[-1];
204*d575d58fSAndre Fischer
205*d575d58fSAndre Fischer    Note that, just like in other implementations of dom trees,
206*d575d58fSAndre Fischer    $parent->{$element_name} is an array, even when there is only one
207*d575d58fSAndre Fischer    element.
208*d575d58fSAndre Fischer
209*d575d58fSAndre Fischer    The new hash is empty or contains the given @attributes as hash.
210*d575d58fSAndre Fischer    When fully read (ie its end tag has been processed) then it can contain two special keys:
211*d575d58fSAndre Fischer    __attributes__ for the attributes
212*d575d58fSAndre Fischer    __text__ for the concatenated text parts
213*d575d58fSAndre Fischer
214*d575d58fSAndre Fischer=cut
215*d575d58fSAndre Fischersub HandleStartTag ($$$@)
216*d575d58fSAndre Fischer{
217*d575d58fSAndre Fischer    my ($data, $expat, $element_name, @attributes) = @_;
218*d575d58fSAndre Fischer
219*d575d58fSAndre Fischer    # Create new node with attributes.
220*d575d58fSAndre Fischer    my $node = {'__attributes__' => {@attributes}};
221*d575d58fSAndre Fischer
222*d575d58fSAndre Fischer    # Append it to the list of $element_name objects.
223*d575d58fSAndre Fischer    my $current_node = $data->{'current_node'};
224*d575d58fSAndre Fischer    $current_node->{$element_name} = [] unless defined $current_node->{$element_name};
225*d575d58fSAndre Fischer    push @{$current_node->{$element_name}}, $node;
226*d575d58fSAndre Fischer
227*d575d58fSAndre Fischer    # Make the new node the current node.
228*d575d58fSAndre Fischer    push @{$data->{'node_stack'}}, $current_node;
229*d575d58fSAndre Fischer    $data->{'current_node'} = $node;
230*d575d58fSAndre Fischer}
231*d575d58fSAndre Fischer
232*d575d58fSAndre Fischer=head HandleEndTag ($data, $expat, $element_name, @attributes)
233*d575d58fSAndre Fischer
234*d575d58fSAndre Fischer    Callback for end tags.
235*d575d58fSAndre Fischer
236*d575d58fSAndre Fischer=cut
237*d575d58fSAndre Fischersub HandleEndTag ($$$)
238*d575d58fSAndre Fischer{
239*d575d58fSAndre Fischer    my ($data, $expat, $element) = @_;
240*d575d58fSAndre Fischer
241*d575d58fSAndre Fischer    # Restore the parent node as current node.
242*d575d58fSAndre Fischer    $data->{'current_node'} = pop @{$data->{'node_stack'}};
243*d575d58fSAndre Fischer}
244*d575d58fSAndre Fischer
245*d575d58fSAndre Fischer=head2 HandleText ($data, $expat, $text)
246*d575d58fSAndre Fischer
247*d575d58fSAndre Fischer    Callback for text.
248*d575d58fSAndre Fischer
249*d575d58fSAndre Fischer    $text is appended to the __text__ member of the current node in
250*d575d58fSAndre Fischer    the dom tree.
251*d575d58fSAndre Fischer
252*d575d58fSAndre Fischer=cut
253*d575d58fSAndre Fischersub HandleText ($$$)
254*d575d58fSAndre Fischer{
255*d575d58fSAndre Fischer    my ($data, $expat, $text) = @_;
256*d575d58fSAndre Fischer    if ($text !~ /^\s*$/)
257*d575d58fSAndre Fischer    {
258*d575d58fSAndre Fischer        $data->{'current_node'}->{'__text__'} .= $text;
259*d575d58fSAndre Fischer    }
260*d575d58fSAndre Fischer}
261*d575d58fSAndre Fischer
262*d575d58fSAndre Fischer
263*d575d58fSAndre Fischer
264*d575d58fSAndre Fischer
265*d575d58fSAndre Fischer=head2 PrintNode ($indentation, $node)
266*d575d58fSAndre Fischer
267*d575d58fSAndre Fischer    For debugging.
268*d575d58fSAndre Fischer    Print $node recursively with initial $indentation.
269*d575d58fSAndre Fischer
270*d575d58fSAndre Fischer=cut
2719f91b7e3SAndre Fischersub PrintNode($$)
2729f91b7e3SAndre Fischer{
2739f91b7e3SAndre Fischer    my ($indentation, $node) = @_;
2749f91b7e3SAndre Fischer
2759f91b7e3SAndre Fischer    if (defined $node->{'__attributes__'})
2769f91b7e3SAndre Fischer    {
2779f91b7e3SAndre Fischer        while (my ($name,$attribute) = each(%{$node->{'__attributes__'}}))
2789f91b7e3SAndre Fischer        {
2799f91b7e3SAndre Fischer            printf("    %s%s -> %s\n", $indentation, $name, $attribute);
2809f91b7e3SAndre Fischer        }
2819f91b7e3SAndre Fischer    }
2829f91b7e3SAndre Fischer
2839f91b7e3SAndre Fischer    while (my ($key,$value) = each(%$node))
2849f91b7e3SAndre Fischer    {
2859f91b7e3SAndre Fischer        if ($key eq '__text__')
2869f91b7e3SAndre Fischer        {
2879f91b7e3SAndre Fischer            printf("%stext '%s'\n", $indentation, $value);
2889f91b7e3SAndre Fischer        }
2899f91b7e3SAndre Fischer        elsif ($key eq '__attributes__')
2909f91b7e3SAndre Fischer        {
2919f91b7e3SAndre Fischer            next;
2929f91b7e3SAndre Fischer        }
2939f91b7e3SAndre Fischer        elsif (ref($value) eq "ARRAY")
2949f91b7e3SAndre Fischer        {
2959f91b7e3SAndre Fischer            foreach my $item (@$value)
2969f91b7e3SAndre Fischer            {
2979f91b7e3SAndre Fischer                printf("%s%s {\n", $indentation, $key);
2989f91b7e3SAndre Fischer                PrintNode($indentation."    ", $item);
2999f91b7e3SAndre Fischer                printf("%s}\n", $indentation);
3009f91b7e3SAndre Fischer            }
3019f91b7e3SAndre Fischer        }
3029f91b7e3SAndre Fischer        else
3039f91b7e3SAndre Fischer        {
3049f91b7e3SAndre Fischer            printf("%s%s {\n", $indentation, $key);
3059f91b7e3SAndre Fischer            PrintNode($indentation."    ", $value);
3069f91b7e3SAndre Fischer            printf("%s}\n", $indentation);
3079f91b7e3SAndre Fischer        }
3089f91b7e3SAndre Fischer    }
3099f91b7e3SAndre Fischer}
3109f91b7e3SAndre Fischer
3119f91b7e3SAndre Fischer
3129f91b7e3SAndre Fischer
3139f91b7e3SAndre Fischer
3149f91b7e3SAndre Fischer=head2 Read($self, $filename)
3159f91b7e3SAndre Fischer
3169f91b7e3SAndre Fischer    Read the releases.xml file as doctree and parse its content.
3179f91b7e3SAndre Fischer
3189f91b7e3SAndre Fischer=cut
3199f91b7e3SAndre Fischersub Read ($$)
3209f91b7e3SAndre Fischer{
3219f91b7e3SAndre Fischer    my ($self, $filename) = @_;
3229f91b7e3SAndre Fischer
3239f91b7e3SAndre Fischer    my $document = ReadDomTree($filename);
3249f91b7e3SAndre Fischer    foreach my $release_node (@{$document->{'releases'}->[0]->{'release'}})
3259f91b7e3SAndre Fischer    {
3269f91b7e3SAndre Fischer        my $version_node = GetFirstChild($release_node, "version");
3279f91b7e3SAndre Fischer        my $version_major = GetText(GetFirstChild($version_node, "major"));
3289f91b7e3SAndre Fischer        my $version_minor = GetText(GetFirstChild($version_node, "minor"), "0");
3299f91b7e3SAndre Fischer        my $version_micro = GetText(GetFirstChild($version_node, "micro"), "0");
3309f91b7e3SAndre Fischer        my $version = sprintf("%d.%d.%d", $version_major, $version_minor, $version_micro);
3319f91b7e3SAndre Fischer        die "could not read version from releases.xml" if $version eq "";
3329f91b7e3SAndre Fischer
3339f91b7e3SAndre Fischer        push @{$self->{'releases'}}, $version;
3349f91b7e3SAndre Fischer
3359f91b7e3SAndre Fischer        my $download_node = GetFirstChild($release_node, "downloads");
3369f91b7e3SAndre Fischer        my $package_format = GetText(GetFirstChild($download_node, "package-format"));
3379f91b7e3SAndre Fischer        my $url_template = GetText(GetFirstChild($download_node, "url-template"));
3389f91b7e3SAndre Fischer        my $upgrade_code = GetText(GetFirstChild($download_node, "upgrade-code"));
3399f91b7e3SAndre Fischer        my $build_id = GetText(GetFirstChild($download_node, "build-id"));
3409f91b7e3SAndre Fischer        die "could not read package format from releases.xml" if $package_format eq "";
3419f91b7e3SAndre Fischer
3429f91b7e3SAndre Fischer        $self->{$version}->{$package_format}->{'upgrade-code'} = $upgrade_code;
3439f91b7e3SAndre Fischer        $self->{$version}->{$package_format}->{'build-id'} = $build_id;
344*d575d58fSAndre Fischer        $self->{$version}->{$package_format}->{'url-template'} = $url_template;
3459f91b7e3SAndre Fischer
346*d575d58fSAndre Fischer        my @languages = ();
3479f91b7e3SAndre Fischer        foreach my $item_node (@{$download_node->{'item'}})
3489f91b7e3SAndre Fischer        {
3499f91b7e3SAndre Fischer            my ($language, $download_data) = ParseDownloadData($item_node, $url_template);
3509f91b7e3SAndre Fischer            if (defined $download_data && defined $language)
3519f91b7e3SAndre Fischer            {
352*d575d58fSAndre Fischer                push @languages, $language;
3539f91b7e3SAndre Fischer                $self->{$version}->{$package_format}->{$language} = $download_data;
3549f91b7e3SAndre Fischer            }
3559f91b7e3SAndre Fischer        }
356*d575d58fSAndre Fischer        $self->{$version}->{$package_format}->{'languages'} = \@languages;
3579f91b7e3SAndre Fischer    }
3589f91b7e3SAndre Fischer}
3599f91b7e3SAndre Fischer
3609f91b7e3SAndre Fischer
3619f91b7e3SAndre Fischer
3629f91b7e3SAndre Fischer
363*d575d58fSAndre Fischer=head2 ParseDownloadData ($item_node, $url_template)
3649f91b7e3SAndre Fischer
3659f91b7e3SAndre Fischer    Parse the data for one set of download data (there is one per release and package format).
3669f91b7e3SAndre Fischer
3679f91b7e3SAndre Fischer=cut
3689f91b7e3SAndre Fischersub ParseDownloadData ($$)
3699f91b7e3SAndre Fischer{
3709f91b7e3SAndre Fischer    my ($item_node, $url_template) = @_;
3719f91b7e3SAndre Fischer
3729f91b7e3SAndre Fischer    my $language = GetText(GetFirstChild($item_node, "language"));
3739f91b7e3SAndre Fischer    my $checksum_node = GetFirstChild($item_node, "checksum");
3749f91b7e3SAndre Fischer    if ( ! defined $checksum_node)
3759f91b7e3SAndre Fischer    {
3769f91b7e3SAndre Fischer        print STDERR "releases data file corrupt (item has no 'checksum' node)\n";
3779f91b7e3SAndre Fischer        return undef;
3789f91b7e3SAndre Fischer    }
3799f91b7e3SAndre Fischer    my $checksum_type = GetAttribute($checksum_node, "type");
3809f91b7e3SAndre Fischer    my $checksum_value = GetText($checksum_node);
3819f91b7e3SAndre Fischer    my $file_size = GetText(GetFirstChild($item_node, "size"));
3829f91b7e3SAndre Fischer    my $product_code = GetText(GetFirstChild($item_node, "product-code"));
3839f91b7e3SAndre Fischer
3849f91b7e3SAndre Fischer    my $url = $url_template;
3859f91b7e3SAndre Fischer    $url =~ s/\%L/$language/g;
3869f91b7e3SAndre Fischer    return (
3879f91b7e3SAndre Fischer        $language,
3889f91b7e3SAndre Fischer        {
3899f91b7e3SAndre Fischer            'URL' => $url,
3909f91b7e3SAndre Fischer            'checksum-type' => $checksum_type,
3919f91b7e3SAndre Fischer            'checksum-value' => $checksum_value,
3929f91b7e3SAndre Fischer            'file-size' => $file_size,
3939f91b7e3SAndre Fischer            'product-code' => $product_code
3949f91b7e3SAndre Fischer        });
3959f91b7e3SAndre Fischer}
3969f91b7e3SAndre Fischer
3979f91b7e3SAndre Fischer
3989f91b7e3SAndre Fischer
3999f91b7e3SAndre Fischer
400*d575d58fSAndre Fischer=head2 Write($self, $filename)
401*d575d58fSAndre Fischer
402*d575d58fSAndre Fischer    Write the content of the releases data to a file named $filename.
403*d575d58fSAndre Fischer
404*d575d58fSAndre Fischer=cut
405*d575d58fSAndre Fischersub Write ($$)
406*d575d58fSAndre Fischer{
407*d575d58fSAndre Fischer    my ($self, $filename) = @_;
408*d575d58fSAndre Fischer
409*d575d58fSAndre Fischer    open my $out, ">", $filename || die "can not write releases data to ".$filename;
410*d575d58fSAndre Fischer    $self->WriteHeader($out);
411*d575d58fSAndre Fischer    $self->WriteContent($out);
412*d575d58fSAndre Fischer    close $out;
413*d575d58fSAndre Fischer}
414*d575d58fSAndre Fischer
415*d575d58fSAndre Fischer
416*d575d58fSAndre Fischer
417*d575d58fSAndre Fischer
418*d575d58fSAndre Fischer=head2 WriteContent ($self, $out)
419*d575d58fSAndre Fischer
420*d575d58fSAndre Fischer    Write the content of the releases.xml list.
421*d575d58fSAndre Fischer
422*d575d58fSAndre Fischer=cut
423*d575d58fSAndre Fischersub WriteContent ($$)
424*d575d58fSAndre Fischer{
425*d575d58fSAndre Fischer    my ($self, $out) = @_;
426*d575d58fSAndre Fischer
427*d575d58fSAndre Fischer    print $out "<releases>\n";
428*d575d58fSAndre Fischer    # Write the data sets for each releases with the same sort order as @{$self->{'releases'}}
429*d575d58fSAndre Fischer    foreach my $version (@{$self->{'releases'}})
430*d575d58fSAndre Fischer    {
431*d575d58fSAndre Fischer        print $out "  <release>\n";
432*d575d58fSAndre Fischer
433*d575d58fSAndre Fischer        my @version_array = split(/\./, $version);
434*d575d58fSAndre Fischer        printf $out "    <version>\n";
435*d575d58fSAndre Fischer        printf $out "      <major>%s</major>\n", $version_array[0];
436*d575d58fSAndre Fischer        printf $out "      <minor>%s</minor>\n", $version_array[1];
437*d575d58fSAndre Fischer        printf $out "      <micro>%s</micro>\n", $version_array[2];
438*d575d58fSAndre Fischer        printf $out "    </version>\n";
439*d575d58fSAndre Fischer
440*d575d58fSAndre Fischer        # Write one download data set per package format.
441*d575d58fSAndre Fischer        while (my ($package_format, $data) = each %{$self->{$version}})
442*d575d58fSAndre Fischer        {
443*d575d58fSAndre Fischer            print $out "    <download>\n";
444*d575d58fSAndre Fischer            printf $out "      <package-format>%s</package-format>\n", $package_format;
445*d575d58fSAndre Fischer            print $out "      <url-template>\n";
446*d575d58fSAndre Fischer            printf $out "        %s\n", $data->{'url-template'};
447*d575d58fSAndre Fischer            print $out "      </url-template>\n";
448*d575d58fSAndre Fischer            printf $out "      <upgrade-code>%s</upgrade-code>\n", $data->{'upgrade-code'};
449*d575d58fSAndre Fischer            printf $out "      <build-id>%s</build-id>\n", $data->{'build-id'};
450*d575d58fSAndre Fischer
451*d575d58fSAndre Fischer            foreach my $language (@{$data->{'languages'}})
452*d575d58fSAndre Fischer            {
453*d575d58fSAndre Fischer                my $language_data = $data->{$language};
454*d575d58fSAndre Fischer                print $out "      <item>\n";
455*d575d58fSAndre Fischer                printf $out "        <language>%s</language>\n", $language;
456*d575d58fSAndre Fischer                printf $out "        <checksum type=\"%s\">%s</checksum>\n",
457*d575d58fSAndre Fischer                    $language_data->{'checksum-type'},
458*d575d58fSAndre Fischer                    $language_data->{'checksum-value'};
459*d575d58fSAndre Fischer                printf $out "        <size>%s</size>\n", $language_data->{'file-size'};
460*d575d58fSAndre Fischer                printf $out "        <product-code>%s</product-code>\n", $language_data->{'product-code'};
461*d575d58fSAndre Fischer                print $out "      </item>\n";
462*d575d58fSAndre Fischer            }
463*d575d58fSAndre Fischer
464*d575d58fSAndre Fischer            print $out "    </download>\n";
465*d575d58fSAndre Fischer        }
466*d575d58fSAndre Fischer
467*d575d58fSAndre Fischer        print $out "    </release>\n";
468*d575d58fSAndre Fischer    }
469*d575d58fSAndre Fischer
470*d575d58fSAndre Fischer    print $out "</releases>\n";
471*d575d58fSAndre Fischer}
472*d575d58fSAndre Fischer
473*d575d58fSAndre Fischer
474*d575d58fSAndre Fischer
475*d575d58fSAndre Fischer
476*d575d58fSAndre Fischer=head2 WriteHeader ($self, $out)
477*d575d58fSAndre Fischer
478*d575d58fSAndre Fischer    Write the header for the releases.xml list.
479*d575d58fSAndre Fischer
480*d575d58fSAndre Fischer=cut
481*d575d58fSAndre Fischersub WriteHeader ($$)
482*d575d58fSAndre Fischer{
483*d575d58fSAndre Fischer    my ($self, $out) = @_;
484*d575d58fSAndre Fischer
485*d575d58fSAndre Fischerprint $out <<EOT;
486*d575d58fSAndre Fischer<?xml version='1.0' encoding='UTF-8'?>
487*d575d58fSAndre Fischer<!--***********************************************************
488*d575d58fSAndre Fischer *
489*d575d58fSAndre Fischer * Licensed to the Apache Software Foundation (ASF) under one
490*d575d58fSAndre Fischer * or more contributor license agreements.  See the NOTICE file
491*d575d58fSAndre Fischer * distributed with this work for additional information
492*d575d58fSAndre Fischer * regarding copyright ownership.  The ASF licenses this file
493*d575d58fSAndre Fischer * to you under the Apache License, Version 2.0 (the
494*d575d58fSAndre Fischer * "License"); you may not use this file except in compliance
495*d575d58fSAndre Fischer * with the License.  You may obtain a copy of the License at
496*d575d58fSAndre Fischer *
497*d575d58fSAndre Fischer *   http://www.apache.org/licenses/LICENSE-2.0
498*d575d58fSAndre Fischer *
499*d575d58fSAndre Fischer * Unless required by applicable law or agreed to in writing,
500*d575d58fSAndre Fischer * software distributed under the License is distributed on an
501*d575d58fSAndre Fischer * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
502*d575d58fSAndre Fischer * KIND, either express or implied.  See the License for the
503*d575d58fSAndre Fischer * specific language governing permissions and limitations
504*d575d58fSAndre Fischer * under the License.
505*d575d58fSAndre Fischer *
506*d575d58fSAndre Fischer ***********************************************************-->
507*d575d58fSAndre FischerEOT
508*d575d58fSAndre Fischer}
509*d575d58fSAndre Fischer
510*d575d58fSAndre Fischer
511*d575d58fSAndre Fischer
512*d575d58fSAndre Fischer
5139f91b7e3SAndre Fischer=head2 GetPreviousVersion($version)
5149f91b7e3SAndre Fischer
5159f91b7e3SAndre Fischer    Look up $version in the sorted list of released versions.  Return
5169f91b7e3SAndre Fischer    the previous element.  Whe $version is not found then return the
5179f91b7e3SAndre Fischer    last element (under the assumption that $version will be the next
5189f91b7e3SAndre Fischer    released version).
5199f91b7e3SAndre Fischer
5209f91b7e3SAndre Fischer=cut
5219f91b7e3SAndre Fischersub GetPreviousVersion ($)
5229f91b7e3SAndre Fischer{
5239f91b7e3SAndre Fischer    my ($current_version) = @_;
5249f91b7e3SAndre Fischer
5259f91b7e3SAndre Fischer    my $release_data = installer::patch::ReleasesList::Instance();
5269f91b7e3SAndre Fischer    my $previous_version = undef;
5279f91b7e3SAndre Fischer    foreach my $version (@{$release_data->{'releases'}})
5289f91b7e3SAndre Fischer    {
5299f91b7e3SAndre Fischer        if ($version eq $current_version)
5309f91b7e3SAndre Fischer        {
5319f91b7e3SAndre Fischer            return $previous_version;
5329f91b7e3SAndre Fischer        }
5339f91b7e3SAndre Fischer        else
5349f91b7e3SAndre Fischer        {
5359f91b7e3SAndre Fischer            $previous_version = $version;
5369f91b7e3SAndre Fischer        }
5379f91b7e3SAndre Fischer    }
5389f91b7e3SAndre Fischer
5399f91b7e3SAndre Fischer    return $previous_version;
5409f91b7e3SAndre Fischer}
5419f91b7e3SAndre Fischer
5429f91b7e3SAndre Fischer
5439f91b7e3SAndre Fischer
5449f91b7e3SAndre Fischer
5459f91b7e3SAndre Fischer
5469f91b7e3SAndre Fischer1;
547