1#************************************************************** 2# 3# Licensed to the Apache Software Foundation (ASF) under one 4# or more contributor license agreements. See the NOTICE file 5# distributed with this work for additional information 6# regarding copyright ownership. The ASF licenses this file 7# to you under the Apache License, Version 2.0 (the 8# "License"); you may not use this file except in compliance 9# with the License. You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, 14# software distributed under the License is distributed on an 15# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16# KIND, either express or implied. See the License for the 17# specific language governing permissions and limitations 18# under the License. 19# 20#************************************************************** 21 22package installer::patch::ReleasesList; 23 24use XML::Parser; 25use File::Spec; 26 27use strict; 28 29=head1 NAME 30 31 package installer::patch::ReleasesList - Functions for accessing the instsetoo_native/data/releases.xml file 32 33=cut 34 35 36my $Instance = undef; 37 38=head2 Instance() 39 40 Return the singleton instance. 41 42=cut 43sub Instance() 44{ 45 if ( ! defined $Instance) 46 { 47 $Instance = new installer::patch::ReleasesList( 48 File::Spec->catfile($ENV{'SRC_ROOT'}, "instsetoo_native", "data", "releases.xml")); 49 } 50 return $Instance; 51} 52 53 54 55 56=head2 new($class, $filename) 57 58 Internal constructor. Don't call. 59 60=cut 61sub new ($$) 62{ 63 my ($class, $filename) = @_; 64 65 my $self = { 66 'releases' => [] 67 }; 68 bless($self, $class); 69 70 71 $self->Read($filename); 72 73 74 return $self; 75} 76 77 78 79 80=head2 GetFirstChild ($node, $child_name) 81 82 Internal function that returns the first child. Use only when the 83 first child is the (expected) only child in a list. 84 85=cut 86sub GetFirstChild ($$) 87{ 88 my ($node, $child_name) = @_; 89 90 if ( ! defined $node) 91 { 92 return undef; 93 } 94 else 95 { 96 my $value = $node->{$child_name}; 97 if (ref($value) eq 'ARRAY') 98 { 99 return $value->[0]; 100 } 101 else 102 { 103 return $value; 104 } 105 } 106} 107 108 109 110 111=head2 GetText ($node) 112 113 Internal function that returns the trimmed text content of a node. 114 115=cut 116sub GetText ($;$) 117{ 118 my ($node, $default_text) = @_; 119 120 if ( ! defined $node) 121 { 122 if (defined $default_text) 123 { 124 return $default_text; 125 } 126 else 127 { 128 return ""; 129 } 130 } 131 else 132 { 133 my $text = $node->{'__text__'}; 134 $text =~ s/(^\s+|\s+$)//g; 135 return $text; 136 } 137} 138 139 140 141sub GetAttribute ($$) 142{ 143 my ($node, $attribute_name) = @_; 144 145 my $attributes = $node->{'__attributes__'}; 146 if ( ! defined $attributes) 147 { 148 return undef; 149 } 150 else 151 { 152 return $attributes->{$attribute_name}; 153 } 154} 155 156 157 158 159sub PrintNode($$); 160sub ReadDomTree ($) 161{ 162 my ($filename) = @_; 163 164 my $root = {}; 165 my $data = { 166 'current_node' => $root, 167 'node_stack' => [] 168 }; 169 my $parser = new XML::Parser( 170 'Handlers' => { 171 'Start' => sub {HandleStartTag($data, @_)}, 172 'End' => sub{HandleEndTag($data, @_)}, 173 'Char' => sub{HandleText($data, @_)} 174 }); 175 $parser->parsefile($filename); 176 177# PrintNode("", $root); 178 179 return $root; 180} 181 182 183 184 185sub PrintNode($$) 186{ 187 my ($indentation, $node) = @_; 188 189 if (defined $node->{'__attributes__'}) 190 { 191 while (my ($name,$attribute) = each(%{$node->{'__attributes__'}})) 192 { 193 printf(" %s%s -> %s\n", $indentation, $name, $attribute); 194 } 195 } 196 197 while (my ($key,$value) = each(%$node)) 198 { 199 if ($key eq '__text__') 200 { 201 printf("%stext '%s'\n", $indentation, $value); 202 } 203 elsif ($key eq '__attributes__') 204 { 205 next; 206 } 207 elsif (ref($value) eq "ARRAY") 208 { 209 foreach my $item (@$value) 210 { 211 printf("%s%s {\n", $indentation, $key); 212 PrintNode($indentation." ", $item); 213 printf("%s}\n", $indentation); 214 } 215 } 216 else 217 { 218 printf("%s%s {\n", $indentation, $key); 219 PrintNode($indentation." ", $value); 220 printf("%s}\n", $indentation); 221 } 222 } 223} 224 225 226sub HandleStartTag ($$$@) 227{ 228 my ($data, $expat, $element, @attributes) = @_; 229 230 # Create new node with attributes. 231 my $node = {'__attributes__' => {@attributes}}; 232 233 # Append it to the list of $element objects. 234 my $current_node = $data->{'current_node'}; 235 $current_node->{$element} = [] unless defined $current_node->{$element}; 236 push @{$current_node->{$element}}, $node; 237 238 # Make the new node the current node. 239 push @{$data->{'node_stack'}}, $current_node; 240 $data->{'current_node'} = $node; 241} 242 243sub HandleEndTag ($$$) 244{ 245 my ($data, $expat, $element) = @_; 246 247 # Restore the parent node as current node. 248 $data->{'current_node'} = pop @{$data->{'node_stack'}}; 249} 250 251sub HandleText ($$$) 252{ 253 my ($data, $expat, $text) = @_; 254 if ($text !~ /^\s*$/) 255 { 256 $data->{'current_node'}->{'__text__'} .= $text; 257 } 258} 259 260=head2 Read($self, $filename) 261 262 Read the releases.xml file as doctree and parse its content. 263 264=cut 265sub Read ($$) 266{ 267 my ($self, $filename) = @_; 268 269 my $document = ReadDomTree($filename); 270 foreach my $release_node (@{$document->{'releases'}->[0]->{'release'}}) 271 { 272 my $version_node = GetFirstChild($release_node, "version"); 273 my $version_major = GetText(GetFirstChild($version_node, "major")); 274 my $version_minor = GetText(GetFirstChild($version_node, "minor"), "0"); 275 my $version_micro = GetText(GetFirstChild($version_node, "micro"), "0"); 276 my $version = sprintf("%d.%d.%d", $version_major, $version_minor, $version_micro); 277 die "could not read version from releases.xml" if $version eq ""; 278 279 push @{$self->{'releases'}}, $version; 280 281 my $download_node = GetFirstChild($release_node, "downloads"); 282 my $package_format = GetText(GetFirstChild($download_node, "package-format")); 283 my $url_template = GetText(GetFirstChild($download_node, "url-template")); 284 my $upgrade_code = GetText(GetFirstChild($download_node, "upgrade-code")); 285 my $build_id = GetText(GetFirstChild($download_node, "build-id")); 286 die "could not read package format from releases.xml" if $package_format eq ""; 287 288 $self->{$version}->{$package_format}->{'upgrade-code'} = $upgrade_code; 289 $self->{$version}->{$package_format}->{'build-id'} = $build_id; 290 291 foreach my $item_node (@{$download_node->{'item'}}) 292 { 293 my ($language, $download_data) = ParseDownloadData($item_node, $url_template); 294 if (defined $download_data && defined $language) 295 { 296 $self->{$version}->{$package_format}->{$language} = $download_data; 297 } 298 } 299 } 300} 301 302 303 304 305=head2 ParseDownloadData ($download_node) 306 307 Parse the data for one set of download data (there is one per release and package format). 308 309=cut 310sub ParseDownloadData ($$) 311{ 312 my ($item_node, $url_template) = @_; 313 314 my $language = GetText(GetFirstChild($item_node, "language")); 315 my $checksum_node = GetFirstChild($item_node, "checksum"); 316 if ( ! defined $checksum_node) 317 { 318 print STDERR "releases data file corrupt (item has no 'checksum' node)\n"; 319 return undef; 320 } 321 my $checksum_type = GetAttribute($checksum_node, "type"); 322 my $checksum_value = GetText($checksum_node); 323 my $file_size = GetText(GetFirstChild($item_node, "size")); 324 my $product_code = GetText(GetFirstChild($item_node, "product-code")); 325 326 my $url = $url_template; 327 $url =~ s/\%L/$language/g; 328 return ( 329 $language, 330 { 331 'URL' => $url, 332 'checksum-type' => $checksum_type, 333 'checksum-value' => $checksum_value, 334 'file-size' => $file_size, 335 'product-code' => $product_code 336 }); 337} 338 339 340 341 342=head2 GetPreviousVersion($version) 343 344 Look up $version in the sorted list of released versions. Return 345 the previous element. Whe $version is not found then return the 346 last element (under the assumption that $version will be the next 347 released version). 348 349=cut 350sub GetPreviousVersion ($) 351{ 352 my ($current_version) = @_; 353 354 my $release_data = installer::patch::ReleasesList::Instance(); 355 my $previous_version = undef; 356 foreach my $version (@{$release_data->{'releases'}}) 357 { 358 if ($version eq $current_version) 359 { 360 return $previous_version; 361 } 362 else 363 { 364 $previous_version = $version; 365 } 366 } 367 368 return $previous_version; 369} 370 371 372 373 374 3751; 376