2 eval 'exec perl -wS $0 ${1+"$@"}'
4 #*************************************************************************
6 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
8 # Copyright 2008 by Sun Microsystems, Inc.
10 # OpenOffice.org - a multi-platform office productivity suite
12 # $RCSfile: make_ext_update_info.pl,v $
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
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;
58 my $descNS = "http://openoffice.org/extensions/description/2006";
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>
70 #Contains the prefixes which are defined in <dependencies>
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>
76 #prefix used in start and end element
79 #The default namespace valid in <dependencies>
81 #The prefix which we use for the default namespace used in <dependencies>
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".
95 "perl make_ext_update_info.pl [--help][--update_url url] --out update_information_file description.xml \n\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
106 if (!GetOptions
('help|?' => \
$help,
108 'update-url=s'=> \
@update_urls))
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
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;
146 print $FH ' 'x
$urlIndent, '<src xlink:href="'.$_.'" />', "\n";
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";
168 if ($name eq "description"
169 && $descNS eq $parser->namespace($name))
173 elsif ($inDescription
174 && $name eq "version"
175 && $descNS eq $parser->namespace($name))
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))
188 print $FH " "x
$parser->current_column(), $parser->original_string();
190 elsif ($inDescription
191 && $name eq "dependencies"
192 && $descNS eq $parser->namespace($name))
195 my $dep = $parser->original_string();
196 #add the additional namespace definitions, which we have discovered during the first
198 #cut of the closing > or /> from the start element, so we can append the namespace definitions
199 $dep =~ /(\s*<.*) ((\s*\/>)|(\s
*>))/x
;
201 $dep1.= " xmlns:".$_.'="'.$notDefInDep{$_}.'"' for (keys %notDefInDep);
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.
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
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") {
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") {
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;
265 if ($name eq "description"
266 && $descNS eq $parser->namespace($name))
270 elsif ($inDescription
271 && $name eq "version"
272 && $descNS eq $parser->namespace($name))
275 print $FH $parser->original_string(), "\n";
277 elsif ($inDescription
278 && $name eq "identifier"
279 && $descNS eq $parser->namespace($name))
282 print $FH $parser->original_string(), "\n";
285 && $name eq "dependencies"
286 && $descNS eq $parser->namespace($name))
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;
303 #We write the complete content between start and end tags of
304 # <identifier>, <version>, <dependencies>
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
323 if ($name eq "description"
324 && $descNS eq $parser->namespace($name)) {
327 elsif ($inDescription
328 && $name eq "dependencies"
329 && $descNS eq $parser->namespace($name)) {
331 #build the map of prefix/namespace which are valid in <dependencies>
332 my @cur = $parser->current_ns_prefixes();
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
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") {
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
391 sub end_handler_infos
396 if ($name eq "description"
397 && $descNS eq $parser->namespace($name)) {
401 && $name eq "dependencies"
402 && $descNS eq $parser->namespace($name)) {
407 sub writeUpdateInformationData
($)
411 #parse description xml to collect information about all used
412 #prefixes and names within <dependencies>
414 my $parser = new XML
::Parser
(ErrorContext
=> 2,
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) {
426 @usedNsInDependencies = grep(!$hashtmp{$_}++, @usedNsInDependencies);
428 #check that the prefix for the default namespace in <dependencies> does not clash
429 #with any other prefixes
433 for (@usedNsInDependencies) {
434 if ($_ eq $generatedPrefix) {
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,
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
463 sub findAttribute
($$)
465 my ($name, $args_r) = @_;
466 my @args = @
{$args_r};
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;
474 } else { # shift away the following value for the attribute
481 #collect the prefixes used in an xml element
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
($$$$)
492 #get the prefixes which are currently valid
493 my @cur = $parser->current_ns_prefixes();
495 #get the namespaces for the prefixes
497 if ($_ eq '#default') {
500 my $ns = $parser->expand_ns_prefix($_);
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);
514 $pref = $map_ns{$ns};
515 push( @
{$out_r}, $pref) if $pref;
518 #also add newly defined prefixes
519 my @newNs = $parser->new_ns_prefixes();
521 if ($_ eq '#default') {
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.
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);
549 #do not call this function for the element that does not use a prefix
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);
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
564 for ( keys %validPrefsInDep) {
565 if ( $validPrefsInDep{$_} eq $ns) {
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
577 my @curNs = $parser->current_ns_prefixes();
579 $curPrefToNs{$_} = $parser->expand_ns_prefix($_);
581 #find the prefix used in <dependencies> to define the namespace of the current element
583 for (keys %validPrefsInDep) {
584 if ($validPrefsInDep{$_} eq $ns) {
586 next if $_ eq "#default";
591 #find the prefix defined in the current element used for the namespace of the element
593 for (keys %curPrefToNs) {
594 if ($curPrefToNs{$_} eq $ns) {
596 next if $_ eq "#default";
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.
606 for (@newPrefsInDep) {
607 if ($curPref eq $_) {
613 $notDefInDep{$curPref} = $ns;