Update ooo320-m1
[ooovba.git] / solenv / bin / make_ext_update_info.pl
blob86824ea407f6f21a5dcad97a7f6262416602c4d4
2 eval 'exec perl -wS $0 ${1+"$@"}'
3 if 0;
4 #*************************************************************************
6 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7 #
8 # Copyright 2008 by Sun Microsystems, Inc.
10 # OpenOffice.org - a multi-platform office productivity suite
12 # $RCSfile: make_ext_update_info.pl,v $
14 # $Revision: 1.3 $
16 # This file is part of OpenOffice.org.
18 # OpenOffice.org is free software: you can redistribute it and/or modify
19 # it under the terms of the GNU Lesser General Public License version 3
20 # only, as published by the Free Software Foundation.
22 # OpenOffice.org is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 # GNU Lesser General Public License version 3 for more details
26 # (a copy is included in the LICENSE file that accompanied this code).
28 # You should have received a copy of the GNU Lesser General Public License
29 # version 3 along with OpenOffice.org. If not, see
30 # <http://www.openoffice.org/license.html>
31 # for a copy of the LGPLv3 License.
33 #*************************************************************************
35 #here the definition for d would be written into dependencies. The reason is that when the event handler
36 #for the element is called, we can only find out the namespace but not the prefix. So we cannot
37 #distinguish if the namespace is used because the element was prefixed or because it uses the default
38 #namespace.
39 use warnings;
40 use strict;
42 use XML::Parser;
43 use Getopt::Long;
44 use Carp;
46 sub getUpdateInfoFileName($);
47 sub writeUpdateInformationData($);
48 sub findAttribute($$);
49 sub getNotDefPrefs($$$);
50 sub collectPrefixes($$$$);
51 sub determineNsDefinitions($$$);
52 sub determineNsDefinitionForItem($$$);
54 my $inDescription = 0;
55 my $inDependencies = 0;
56 my $inIdentifier = 0;
57 my $inVersion = 0;
58 my $descNS = "http://openoffice.org/extensions/description/2006";
59 my $indent;
60 my $identifier;
61 my $version;
63 #contains prefixes and the corresponding namespaces which are used in the <dependencies>
64 #element and all children of the description.xml
65 my @usedNsInDependencies;
67 #Maps prefix to namespaces which are valid in <dependencies>. That is, they are
68 #either defined in <dependencies> or in the hirarchy above <dependencies>
69 my %validPrefsInDep;
70 #Contains the prefixes which are defined in <dependencies>
71 my @newPrefsInDep;
72 #Contains the prefixes/namespaces which need to be defined in <dependencies> but which are currently
73 #not. For example a prefix is defined in the parent and is used in a child of <dependencies>
74 my %notDefInDep;
76 #prefix used in start and end element
77 my $prefix;
79 #The default namespace valid in <dependencies>
80 my $defNsInDep;
81 #The prefix which we use for the default namespace used in <dependencies>
82 my $generatedPrefix;
84 my $helptext =
85 "make_ext_update_info.pl produces an update information file for an extension. ".
86 "It will use a dummy URL as URL for the extension update unless a URL has been ".
87 "provided with the --update_url option. The name of the update ".
88 "information file, which must be provided with the --out switch, should be formed ".
89 "according to this scheme: \n\n".
90 "extension_identifier.update.xml\n\n".
91 "extension_identifier should correspond to the extension identifier. In some cases ".
92 "this may not be possible because the identifier may contain characters which are not ".
93 "allowd in file names.\n\n".
94 "usage:\n".
95 "perl make_ext_update_info.pl [--help][--update_url url] --out update_information_file description.xml \n\n".
96 "Options: \n".
97 "--help - prints the help message and exits \n".
98 "--out file - the update information file to be written including the path \n".
99 "--update-url url - inserts the url under the <update-download> element. It may be necessary to enclose the urls in quotes in case they contain characters such as \"?\". ".
100 "It can be used multiple times\n\n";
102 #handling of arguments
103 my $help = 0;
104 my $out;
105 my @update_urls;
106 if (!GetOptions('help|?' => \$help,
107 'out=s' => \$out,
108 'update-url=s'=> \@update_urls))
110 print $helptext;
111 exit -1;
113 my $cArgs = scalar @ARGV;
114 die "You need to provide a description.xml\n\n$helptext" if $cArgs ==0;
115 die "You need to provide the name of the update information file ".
116 "with the --out switch.\n" unless ($out);
117 die "Too many arguments. \n\n$helptext" if $cArgs > 1;
118 print $helptext if $help;
121 #open the update information file for writing
122 my $FH;
123 open $FH, "> $out" or die $!;
125 #write the xml header and root element
126 print $FH '<?xml version="1.0" encoding="UTF-8"?>', "\n";
127 print $FH '<description xmlns="http://openoffice.org/extensions/update/2006"', "\n";
128 print $FH ' xmlns:xlink="http://www.w3.org/1999/xlink">', "\n";
130 #obtain from description.xml the data for the update information
131 writeUpdateInformationData($ARGV[0]);
132 #We will die if there is no <version> or <identifier> in the description.xml
133 die "Error: The description.xml does not contain a <identifier> element.\n" unless $identifier;
134 die "Error: The description.xml does not contain a <version> element. \n" unless $version;
136 #write the write the update-download element and the children.
137 #the indention of <update-download> corresponds to that of <version>
138 print $FH ' 'x$indent, '<update-download>', "\n";
139 #check if update-urls have been provided through --update-url option
140 if (scalar @update_urls)
142 my $urlIndent = $indent > 8 ? 8 : 2 * $indent;
143 #use provided urls
144 for (@update_urls)
146 print $FH ' 'x$urlIndent, '<src xlink:href="'.$_.'" />', "\n";
149 else
151 #use dummy update url
152 print $FH ' 'x8, '<src xlink:href="http://extensions.openoffice.org/testarea/dummy.oxt" />', "\n";
154 print $FH ' 'x$indent, '</update-download>', "\n";
156 print $FH '</description>', "\n";
157 close $FH;
159 exit 0;
163 sub start_handler
165 my $parser = shift;
166 my $name = shift;
168 if ($name eq "description"
169 && $descNS eq $parser->namespace($name))
171 $inDescription = 1;
173 elsif ($inDescription
174 && $name eq "version"
175 && $descNS eq $parser->namespace($name))
177 $inVersion = 1;
178 $version = 1;
179 $indent = $parser->current_column();
180 print $FH " "x$indent, $parser->original_string();
182 elsif ($inDescription
183 && $name eq "identifier"
184 && $descNS eq $parser->namespace($name))
186 $inIdentifier = 1;
187 $identifier = 1;
188 print $FH " "x$parser->current_column(), $parser->original_string();
190 elsif ($inDescription
191 && $name eq "dependencies"
192 && $descNS eq $parser->namespace($name))
194 $inDependencies = 1;
195 my $dep = $parser->original_string();
196 #add the additional namespace definitions, which we have discovered during the first
197 #parsing
198 #cut of the closing > or /> from the start element, so we can append the namespace definitions
199 $dep =~ /(\s*<.*) ((\s*\/>)|(\s*>))/x;
200 my $dep1 = $1;
201 $dep1.= " xmlns:".$_.'="'.$notDefInDep{$_}.'"' for (keys %notDefInDep);
202 $dep1.= $2;
203 print $FH " "x$parser->current_column(), $dep1;
205 elsif ($inDependencies)
207 #$prefix is global because we need to use it in the end element as well.
208 $prefix = "";
209 my $fullString;
210 my $orig = $parser->original_string();
211 #Split up the string so we can insert the prefix for the element.
212 # <OpenOffice.org-minimal-version>
213 # <d:OpenOffice.org-minimal-version>
214 $orig=~/(\s*<)(.*?)\s/x;
215 #in $2 is the element name, look for the prefix
216 if ($2 !~/(.*?):/ && $parser->namespace($name)) {
217 #no prefix, that is element uses default namespace.
218 #Now check if the default namespace in <dependencies> is the same as the one in this
219 #element. If not, then the default ns was defined "after" <dependencies>. Because all
220 #children of <dependencies> are copied into the update information, so will this default
221 #namespace definition. Hence this element will have the same default namespace in the
222 #update information.
223 my $defNsDep = $validPrefsInDep{"#default"};
224 #we must have #default, see the if statement above
225 my $defNsCur = $parser->expand_ns_prefix("#default");
227 if ($defNsDep eq $defNsCur) {
228 #Determine if there is in <dependency> a prefix defined (only valid there and need not
229 #directly defined in this element). If there is no prefix defined then we will
230 #add a new definition to <dependencies>.
231 for (keys %validPrefsInDep) {
232 if (($validPrefsInDep{$_} eq $defNsDep) && $_ ne "#default") {
233 $prefix = $_; last;
236 if (! $prefix) {
237 #If there was no prefix, we will add new prefix definition to <dependency>
238 #Which prefix this is has been determined during the first parsing.
239 for (keys %notDefInDep) {
240 if (($notDefInDep{$_} eq $defNsCur) && $_ ne "#default") {
241 $prefix = $_; last;
245 #die if we have no prefix
246 confess "No prefix defined for default namespace " unless $prefix;
247 #get the full part after <
248 $orig=~/(\s*<)(.*)/x;
249 $fullString= $1.$prefix.":".$2;
253 $fullString = $orig unless $fullString;
255 # We record anything within <dependencies> </dependencies>.
256 print $FH $fullString;
260 sub end_handler
262 my $parser = shift;
263 my $name = shift;
265 if ($name eq "description"
266 && $descNS eq $parser->namespace($name))
268 $inDescription = 0;
270 elsif ($inDescription
271 && $name eq "version"
272 && $descNS eq $parser->namespace($name))
274 $inVersion = 0;
275 print $FH $parser->original_string(), "\n";
277 elsif ($inDescription
278 && $name eq "identifier"
279 && $descNS eq $parser->namespace($name))
281 $inIdentifier = 0;
282 print $FH $parser->original_string(), "\n";
284 elsif($inDescription
285 && $name eq "dependencies"
286 && $descNS eq $parser->namespace($name))
288 $inDependencies = 0;
289 print $FH $parser->original_string(), "\n";
291 elsif ($inDependencies)
293 my $orig = $parser->original_string();
294 #$orig is empty if we have tags like this: <name />
295 if ($orig && $prefix) {
296 $orig=~/(\s*<\/)(.*)/x;
297 $orig= $1.$prefix.":".$2;
299 print $FH $orig;
303 #We write the complete content between start and end tags of
304 # <identifier>, <version>, <dependencies>
305 sub default_handler
307 my $parser = shift;
308 my $name = shift;
309 if ($inIdentifier || $inVersion) {
310 print $FH $parser->original_string();
311 } elsif ($inDependencies) {
312 print $FH $parser->original_string();
315 } # End of default_handler
317 #sax handler used for the first parsing to recognize the used prefixes in <dependencies > and its
318 #children and to find out if we need to define a new prefix for the current default namespace.
319 sub start_handler_infos
321 my $parser = shift;
322 my $name = shift;
323 if ($name eq "description"
324 && $descNS eq $parser->namespace($name)) {
325 $inDescription = 1;
327 elsif ($inDescription
328 && $name eq "dependencies"
329 && $descNS eq $parser->namespace($name)) {
330 $inDependencies = 1;
331 #build the map of prefix/namespace which are valid in <dependencies>
332 my @cur = $parser->current_ns_prefixes();
333 for (@cur) {
334 $validPrefsInDep{$_} = $parser->expand_ns_prefix($_);
336 #remember the prefixes defined in <dependencies>
337 @newPrefsInDep = $parser->new_ns_prefixes();
339 collectPrefixes($parser, $name, \@_, \@usedNsInDependencies);
340 return if $generatedPrefix;
342 #determine if need to create a new prefix for the current element if it uses a default ns.
343 #Split up the string so we can see if there is a prefix used
344 # <OpenOffice.org-minimal-version>
345 # <d:OpenOffice.org-minimal-version>
346 my $orig = $parser->original_string();
347 $orig=~/(\s*<)(.*?)\s/x;
348 #in $2 is the element name, look for the prefix
349 if ($2 !~/(.*?):/ && $parser->namespace($name)) {
350 #no prefix, that is element uses default namespace.
351 #Now check if the default namespace in <dependencies> is the same as the one in this
352 #element. If not, then the default ns was defined "after" <dependencies>. Because all
353 #children of <dependencies> are copied into the update information, so will this default
354 #namespace definition. Hence this element will have the same default namespace in the
355 #update information.
356 my $defNsDep = $validPrefsInDep{"#default"};
357 #we must have #default, see the if statement above
358 my $defNsCur = $parser->expand_ns_prefix("#default");
360 if ($defNsDep eq $defNsCur) {
361 #Determine if there is in <dependency> a prefix defined (only valid there and need not
362 #directly defined in this element). If there is no prefix defined then we will
363 #add a new definition to <dependencies>.
364 for (keys %validPrefsInDep) {
365 if (($validPrefsInDep{$_} eq $defNsDep) && $_ ne "#default") {
366 $prefix = $_; last;
370 if (! $prefix) {
372 #define a new prefix
373 #actually there can be only onle prefix, which is the case when the element
374 #uses the same default namespace as <dependencies> otherwise, the default
375 #namespace was redefined by the children of <dependencies>. These are completely
376 #copied and still valid in the update information file
377 $generatedPrefix = "a";
378 $defNsInDep = $defNsDep;
384 elsif ($inDependencies) {
385 determineNsDefinitions($parser, $name, \@_);
386 collectPrefixes($parser, $name, \@_, \@usedNsInDependencies);
389 #sax handler used for the first parsing to recognize the used prefixes in <dependencies > and its
390 #children
391 sub end_handler_infos
393 my $parser = shift;
394 my $name = shift;
396 if ($name eq "description"
397 && $descNS eq $parser->namespace($name)) {
398 $inDescription = 0;
400 elsif($inDescription
401 && $name eq "dependencies"
402 && $descNS eq $parser->namespace($name)) {
403 $inDependencies = 0;
407 sub writeUpdateInformationData($)
409 my $desc = shift;
411 #parse description xml to collect information about all used
412 #prefixes and names within <dependencies>
414 my $parser = new XML::Parser(ErrorContext => 2,
415 Namespaces => 1);
416 $parser->setHandlers(Start => \&start_handler_infos,
417 End => \&end_handler_infos);
419 $parser->parsefile($desc);
423 #remove duplicates in the array containing the prefixes
424 if ($generatedPrefix) {
425 my %hashtmp;
426 @usedNsInDependencies = grep(!$hashtmp{$_}++, @usedNsInDependencies);
428 #check that the prefix for the default namespace in <dependencies> does not clash
429 #with any other prefixes
430 my $clash;
431 do {
432 $clash = 0;
433 for (@usedNsInDependencies) {
434 if ($_ eq $generatedPrefix) {
435 $generatedPrefix++;
436 $clash = 1; last;
439 } while ($clash);
440 $notDefInDep{$generatedPrefix} = $defNsInDep;
442 #if $notDefInDep contains the prefix #default then we need to add the generated prefix as well
444 #add the special prefix for the default namespace into the map of prefixes that will be
445 #added to the <dependencies> element in the update information file
448 ($inDependencies, $inDescription) = (0,0);
450 my $parser = new XML::Parser(ErrorContext => 2,
451 Namespaces => 1);
452 $parser->setHandlers(
453 Start => \&start_handler,
454 End => \&end_handler,
455 Default => \&default_handler);
456 $parser->parsefile($desc);
460 # param 1: name of the attribute we look for
461 # param 2: array of name value pairs, the first subscript is the attribute and the second
462 # is the value.
463 sub findAttribute($$)
465 my ($name, $args_r) = @_;
466 my @args = @{$args_r};
467 my $value;
468 while (my $attr = shift(@args))
470 if ($attr eq $name) {
471 $value = shift(@args);
472 die "href attribut has no valid URL" unless $value;
473 last;
474 } else { # shift away the following value for the attribute
475 shift(@args);
478 return $value;
481 #collect the prefixes used in an xml element
482 #param 1: parser,
483 #param 2: element name,
484 #param 3: array of name and values of attributes
485 #param 4: out parameter, the array containing the prefixes
486 sub collectPrefixes($$$$)
488 my $parser = shift;
489 my $name = shift;
490 my $attr_r = shift;
491 my $out_r = shift;
492 #get the prefixes which are currently valid
493 my @cur = $parser->current_ns_prefixes();
494 my %map_ns;
495 #get the namespaces for the prefixes
496 for (@cur) {
497 if ($_ eq '#default') {
498 next;
500 my $ns = $parser->expand_ns_prefix($_);
501 $map_ns{$ns} = $_;
503 #investigat ns of element
504 my $pref = $map_ns{$parser->namespace($name)};
505 push(@{$out_r}, $pref) if $pref;
506 #now go over the attributes
508 while (my $attr = shift(@{$attr_r})) {
509 my $ns = $parser->namespace($attr);
510 if (! $ns) {
511 shift(@{$attr_r});
512 next;
514 $pref = $map_ns{$ns};
515 push( @{$out_r}, $pref) if $pref;
516 shift(@{$attr_r});
518 #also add newly defined prefixes
519 my @newNs = $parser->new_ns_prefixes();
520 for (@newNs) {
521 if ($_ eq '#default') {
522 next;
524 push (@{$out_r}, $_);
528 #The function is called for each child element of dependencies. It finds out the prefixes
529 #which are used by the children and which are defined by the parents of <dependencies>. These
530 #would be lost when copying the children of <dependencies> into the update information file.
531 #Therefore these definitions are collected so that they then can be written in the <dependencies>
532 #element of the update information file.
533 #param 1: parser
534 #param 2: namsepace
535 #param 3: the @_ received in the start handler
536 sub determineNsDefinitions($$$)
538 my ($parser, $name, $attr_r) = @_;
539 my @attr = @{$attr_r};
541 determineNsDefinitionForItem($parser, $name, 1);
543 while (my $attr = shift(@attr)) {
544 determineNsDefinitionForItem($parser, $attr, 0);
545 shift @attr;
549 #do not call this function for the element that does not use a prefix
550 #param 1: parser
551 #param 2: name of the element or attribute
552 #param 3: 1 if called for an elment name and 0 when called for attribue
553 sub determineNsDefinitionForItem($$$)
555 my ($parser, $name) = @_;
556 my $ns = $parser->namespace($name);
557 if (! $ns) {
558 return;
560 #If the namespace was not kwown in <dependencies> then it was defined in one of its children
561 #or in this element. Then we are done since this namespace definition is copied into the
562 #update information.
563 my $bNsKnownInDep;
564 for ( keys %validPrefsInDep) {
565 if ( $validPrefsInDep{$_} eq $ns) {
566 $bNsKnownInDep = 1;
567 last;
570 #If the namespace of the current element is known in <dependencies> then check if the same
571 #prefix is used. If not, then the prefix was defined in one of the children of <dependencies>
572 #and was assigned the same namespace. Because we copy of children into the update information,
573 #this definition is also copied.
574 if ($bNsKnownInDep) {
575 #create a map of currently valid prefix/namespace
576 my %curPrefToNs;
577 my @curNs = $parser->current_ns_prefixes();
578 for (@curNs) {
579 $curPrefToNs{$_} = $parser->expand_ns_prefix($_);
581 #find the prefix used in <dependencies> to define the namespace of the current element
582 my $validDepPref;
583 for (keys %validPrefsInDep) {
584 if ($validPrefsInDep{$_} eq $ns) {
585 #ignore #default
586 next if $_ eq "#default";
587 $validDepPref = $_;
588 last;
591 #find the prefix defined in the current element used for the namespace of the element
592 my $curPref;
593 for (keys %curPrefToNs) {
594 if ($curPrefToNs{$_} eq $ns) {
595 #ignore #default
596 next if $_ eq "#default";
597 $curPref = $_;
598 last;
601 if ($curPref && $validDepPref && ($curPref eq $validDepPref)) {
602 #If the prefixes and ns are the same, then the prefix definition of <dependencies> or its
603 #parent can be used. However, we need to find out which prefixed are NOT defined in
604 #<dependencies> so we can add them to it when we write the update information.
605 my $bDefined = 0;
606 for (@newPrefsInDep) {
607 if ($curPref eq $_) {
608 $bDefined = 1;
609 last;
612 if (! $bDefined) {
613 $notDefInDep{$curPref} = $ns;