| : |
| eval 'exec perl -wS $0 ${1+"$@"}' |
| if 0; |
| #************************************************************** |
| # |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| # |
| #************************************************************** |
| |
| |
| |
| #here the definition for d would be written into dependencies. The reason is that when the event handler |
| #for the element is called, we can only find out the namespace but not the prefix. So we cannot |
| #distinguish if the namespace is used because the element was prefixed or because it uses the default |
| #namespace. |
| use warnings; |
| use strict; |
| |
| use XML::Parser; |
| use Getopt::Long; |
| use Carp; |
| |
| sub getUpdateInfoFileName($); |
| sub writeUpdateInformationData($); |
| sub findAttribute($$); |
| sub getNotDefPrefs($$$); |
| sub collectPrefixes($$$$); |
| sub determineNsDefinitions($$$); |
| sub determineNsDefinitionForItem($$$); |
| |
| my $inDescription = 0; |
| my $inDependencies = 0; |
| my $inIdentifier = 0; |
| my $inVersion = 0; |
| my $descNS = "http://openoffice.org/extensions/description/2006"; |
| my $indent; |
| my $identifier; |
| my $version; |
| |
| #contains prefixes and the corresponding namespaces which are used in the <dependencies> |
| #element and all children of the description.xml |
| my @usedNsInDependencies; |
| |
| #Maps prefix to namespaces which are valid in <dependencies>. That is, they are |
| #either defined in <dependencies> or in the hirarchy above <dependencies> |
| my %validPrefsInDep; |
| #Contains the prefixes which are defined in <dependencies> |
| my @newPrefsInDep; |
| #Contains the prefixes/namespaces which need to be defined in <dependencies> but which are currently |
| #not. For example a prefix is defined in the parent and is used in a child of <dependencies> |
| my %notDefInDep; |
| |
| #prefix used in start and end element |
| my $prefix; |
| |
| #The default namespace valid in <dependencies> |
| my $defNsInDep; |
| #The prefix which we use for the default namespace used in <dependencies> |
| my $generatedPrefix; |
| |
| my $helptext = |
| "make_ext_update_info.pl produces an update information file for an extension. ". |
| "It will use a dummy URL as URL for the extension update unless a URL has been ". |
| "provided with the --update_url option. The name of the update ". |
| "information file, which must be provided with the --out switch, should be formed ". |
| "according to this scheme: \n\n". |
| "extension_identifier.update.xml\n\n". |
| "extension_identifier should correspond to the extension identifier. In some cases ". |
| "this may not be possible because the identifier may contain characters which are not ". |
| "allowd in file names.\n\n". |
| "usage:\n". |
| "perl make_ext_update_info.pl [--help][--update_url url] --out update_information_file description.xml \n\n". |
| "Options: \n". |
| "--help - prints the help message and exits \n". |
| "--out file - the update information file to be written including the path \n". |
| "--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 \"?\". ". |
| "It can be used multiple times\n\n"; |
| |
| #handling of arguments |
| my $help = 0; |
| my $out; |
| my @update_urls; |
| if (!GetOptions('help|?' => \$help, |
| 'out=s' => \$out, |
| 'update-url=s'=> \@update_urls)) |
| { |
| print $helptext; |
| exit -1; |
| } |
| my $cArgs = scalar @ARGV; |
| die "You need to provide a description.xml\n\n$helptext" if $cArgs ==0; |
| die "You need to provide the name of the update information file ". |
| "with the --out switch.\n" unless ($out); |
| die "Too many arguments. \n\n$helptext" if $cArgs > 1; |
| print $helptext if $help; |
| |
| |
| #open the update information file for writing |
| my $FH; |
| open $FH, "> $out" or die $!; |
| |
| #write the xml header and root element |
| print $FH '<?xml version="1.0" encoding="UTF-8"?>', "\n"; |
| print $FH '<description xmlns="http://openoffice.org/extensions/update/2006"', "\n"; |
| print $FH ' xmlns:xlink="http://www.w3.org/1999/xlink">', "\n"; |
| |
| #obtain from description.xml the data for the update information |
| writeUpdateInformationData($ARGV[0]); |
| #We will die if there is no <version> or <identifier> in the description.xml |
| die "Error: The description.xml does not contain a <identifier> element.\n" unless $identifier; |
| die "Error: The description.xml does not contain a <version> element. \n" unless $version; |
| |
| #write the write the update-download element and the children. |
| #the indention of <update-download> corresponds to that of <version> |
| print $FH ' 'x$indent, '<update-download>', "\n"; |
| #check if update-urls have been provided through --update-url option |
| if (scalar @update_urls) |
| { |
| my $urlIndent = $indent > 8 ? 8 : 2 * $indent; |
| #use provided urls |
| for (@update_urls) |
| { |
| print $FH ' 'x$urlIndent, '<src xlink:href="'.$_.'" />', "\n"; |
| } |
| } |
| else |
| { |
| #use dummy update url |
| print $FH ' 'x8, '<src xlink:href="http://extensions.openoffice.org/testarea/dummy.oxt" />', "\n"; |
| } |
| print $FH ' 'x$indent, '</update-download>', "\n"; |
| |
| print $FH '</description>', "\n"; |
| close $FH; |
| |
| exit 0; |
| |
| |
| |
| sub start_handler |
| { |
| my $parser = shift; |
| my $name = shift; |
| |
| if ($name eq "description" |
| && $descNS eq $parser->namespace($name)) |
| { |
| $inDescription = 1; |
| } |
| elsif ($inDescription |
| && $name eq "version" |
| && $descNS eq $parser->namespace($name)) |
| { |
| $inVersion = 1; |
| $version = 1; |
| $indent = $parser->current_column(); |
| print $FH " "x$indent, $parser->original_string(); |
| } |
| elsif ($inDescription |
| && $name eq "identifier" |
| && $descNS eq $parser->namespace($name)) |
| { |
| $inIdentifier = 1; |
| $identifier = 1; |
| print $FH " "x$parser->current_column(), $parser->original_string(); |
| } |
| elsif ($inDescription |
| && $name eq "dependencies" |
| && $descNS eq $parser->namespace($name)) |
| { |
| $inDependencies = 1; |
| my $dep = $parser->original_string(); |
| #add the additional namespace definitions, which we have discovered during the first |
| #parsing |
| #cut of the closing > or /> from the start element, so we can append the namespace definitions |
| $dep =~ /(\s*<.*) ((\s*\/>)|(\s*>))/x; |
| my $dep1 = $1; |
| $dep1.= " xmlns:".$_.'="'.$notDefInDep{$_}.'"' for (keys %notDefInDep); |
| $dep1.= $2; |
| print $FH " "x$parser->current_column(), $dep1; |
| } |
| elsif ($inDependencies) |
| { |
| #$prefix is global because we need to use it in the end element as well. |
| $prefix = ""; |
| my $fullString; |
| my $orig = $parser->original_string(); |
| #Split up the string so we can insert the prefix for the element. |
| # <OpenOffice.org-minimal-version> |
| # <d:OpenOffice.org-minimal-version> |
| $orig=~/(\s*<)(.*?)\s/x; |
| #in $2 is the element name, look for the prefix |
| if ($2 !~/(.*?):/ && $parser->namespace($name)) { |
| #no prefix, that is element uses default namespace. |
| #Now check if the default namespace in <dependencies> is the same as the one in this |
| #element. If not, then the default ns was defined "after" <dependencies>. Because all |
| #children of <dependencies> are copied into the update information, so will this default |
| #namespace definition. Hence this element will have the same default namespace in the |
| #update information. |
| my $defNsDep = $validPrefsInDep{"#default"}; |
| #we must have #default, see the if statement above |
| my $defNsCur = $parser->expand_ns_prefix("#default"); |
| |
| if ($defNsDep eq $defNsCur) { |
| #Determine if there is in <dependency> a prefix defined (only valid there and need not |
| #directly defined in this element). If there is no prefix defined then we will |
| #add a new definition to <dependencies>. |
| for (keys %validPrefsInDep) { |
| if (($validPrefsInDep{$_} eq $defNsDep) && $_ ne "#default") { |
| $prefix = $_; last; |
| } |
| } |
| if (! $prefix) { |
| #If there was no prefix, we will add new prefix definition to <dependency> |
| #Which prefix this is has been determined during the first parsing. |
| for (keys %notDefInDep) { |
| if (($notDefInDep{$_} eq $defNsCur) && $_ ne "#default") { |
| $prefix = $_; last; |
| } |
| } |
| } |
| #die if we have no prefix |
| confess "No prefix defined for default namespace " unless $prefix; |
| #get the full part after < |
| $orig=~/(\s*<)(.*)/x; |
| $fullString= $1.$prefix.":".$2; |
| } |
| |
| } |
| $fullString = $orig unless $fullString; |
| |
| # We record anything within <dependencies> </dependencies>. |
| print $FH $fullString; |
| } |
| } |
| |
| sub end_handler |
| { |
| my $parser = shift; |
| my $name = shift; |
| |
| if ($name eq "description" |
| && $descNS eq $parser->namespace($name)) |
| { |
| $inDescription = 0; |
| } |
| elsif ($inDescription |
| && $name eq "version" |
| && $descNS eq $parser->namespace($name)) |
| { |
| $inVersion = 0; |
| print $FH $parser->original_string(), "\n"; |
| } |
| elsif ($inDescription |
| && $name eq "identifier" |
| && $descNS eq $parser->namespace($name)) |
| { |
| $inIdentifier = 0; |
| print $FH $parser->original_string(), "\n"; |
| } |
| elsif($inDescription |
| && $name eq "dependencies" |
| && $descNS eq $parser->namespace($name)) |
| { |
| $inDependencies = 0; |
| print $FH $parser->original_string(), "\n"; |
| } |
| elsif ($inDependencies) |
| { |
| my $orig = $parser->original_string(); |
| #$orig is empty if we have tags like this: <name /> |
| if ($orig && $prefix) { |
| $orig=~/(\s*<\/)(.*)/x; |
| $orig= $1.$prefix.":".$2; |
| } |
| print $FH $orig; |
| } |
| } |
| |
| #We write the complete content between start and end tags of |
| # <identifier>, <version>, <dependencies> |
| sub default_handler |
| { |
| my $parser = shift; |
| my $name = shift; |
| if ($inIdentifier || $inVersion) { |
| print $FH $parser->original_string(); |
| } elsif ($inDependencies) { |
| print $FH $parser->original_string(); |
| } |
| |
| } # End of default_handler |
| |
| #sax handler used for the first parsing to recognize the used prefixes in <dependencies > and its |
| #children and to find out if we need to define a new prefix for the current default namespace. |
| sub start_handler_infos |
| { |
| my $parser = shift; |
| my $name = shift; |
| if ($name eq "description" |
| && $descNS eq $parser->namespace($name)) { |
| $inDescription = 1; |
| } |
| elsif ($inDescription |
| && $name eq "dependencies" |
| && $descNS eq $parser->namespace($name)) { |
| $inDependencies = 1; |
| #build the map of prefix/namespace which are valid in <dependencies> |
| my @cur = $parser->current_ns_prefixes(); |
| for (@cur) { |
| $validPrefsInDep{$_} = $parser->expand_ns_prefix($_); |
| } |
| #remember the prefixes defined in <dependencies> |
| @newPrefsInDep = $parser->new_ns_prefixes(); |
| |
| collectPrefixes($parser, $name, \@_, \@usedNsInDependencies); |
| return if $generatedPrefix; |
| |
| #determine if need to create a new prefix for the current element if it uses a default ns. |
| #Split up the string so we can see if there is a prefix used |
| # <OpenOffice.org-minimal-version> |
| # <d:OpenOffice.org-minimal-version> |
| my $orig = $parser->original_string(); |
| $orig=~/(\s*<)(.*?)\s/x; |
| #in $2 is the element name, look for the prefix |
| if ($2 !~/(.*?):/ && $parser->namespace($name)) { |
| #no prefix, that is element uses default namespace. |
| #Now check if the default namespace in <dependencies> is the same as the one in this |
| #element. If not, then the default ns was defined "after" <dependencies>. Because all |
| #children of <dependencies> are copied into the update information, so will this default |
| #namespace definition. Hence this element will have the same default namespace in the |
| #update information. |
| my $defNsDep = $validPrefsInDep{"#default"}; |
| #we must have #default, see the if statement above |
| my $defNsCur = $parser->expand_ns_prefix("#default"); |
| |
| if ($defNsDep eq $defNsCur) { |
| #Determine if there is in <dependency> a prefix defined (only valid there and need not |
| #directly defined in this element). If there is no prefix defined then we will |
| #add a new definition to <dependencies>. |
| for (keys %validPrefsInDep) { |
| if (($validPrefsInDep{$_} eq $defNsDep) && $_ ne "#default") { |
| $prefix = $_; last; |
| } |
| } |
| |
| if (! $prefix) { |
| |
| #define a new prefix |
| #actually there can be only onle prefix, which is the case when the element |
| #uses the same default namespace as <dependencies> otherwise, the default |
| #namespace was redefined by the children of <dependencies>. These are completely |
| #copied and still valid in the update information file |
| $generatedPrefix = "a"; |
| $defNsInDep = $defNsDep; |
| } |
| } |
| } |
| |
| } |
| elsif ($inDependencies) { |
| determineNsDefinitions($parser, $name, \@_); |
| collectPrefixes($parser, $name, \@_, \@usedNsInDependencies); |
| } |
| } |
| #sax handler used for the first parsing to recognize the used prefixes in <dependencies > and its |
| #children |
| sub end_handler_infos |
| { |
| my $parser = shift; |
| my $name = shift; |
| |
| if ($name eq "description" |
| && $descNS eq $parser->namespace($name)) { |
| $inDescription = 0; |
| } |
| elsif($inDescription |
| && $name eq "dependencies" |
| && $descNS eq $parser->namespace($name)) { |
| $inDependencies = 0; |
| } |
| } |
| |
| sub writeUpdateInformationData($) |
| { |
| my $desc = shift; |
| { |
| #parse description xml to collect information about all used |
| #prefixes and names within <dependencies> |
| |
| my $parser = new XML::Parser(ErrorContext => 2, |
| Namespaces => 1); |
| $parser->setHandlers(Start => \&start_handler_infos, |
| End => \&end_handler_infos); |
| |
| $parser->parsefile($desc); |
| |
| |
| } |
| #remove duplicates in the array containing the prefixes |
| if ($generatedPrefix) { |
| my %hashtmp; |
| @usedNsInDependencies = grep(!$hashtmp{$_}++, @usedNsInDependencies); |
| |
| #check that the prefix for the default namespace in <dependencies> does not clash |
| #with any other prefixes |
| my $clash; |
| do { |
| $clash = 0; |
| for (@usedNsInDependencies) { |
| if ($_ eq $generatedPrefix) { |
| $generatedPrefix++; |
| $clash = 1; last; |
| } |
| } |
| } while ($clash); |
| $notDefInDep{$generatedPrefix} = $defNsInDep; |
| } |
| #if $notDefInDep contains the prefix #default then we need to add the generated prefix as well |
| |
| #add the special prefix for the default namespace into the map of prefixes that will be |
| #added to the <dependencies> element in the update information file |
| |
| |
| ($inDependencies, $inDescription) = (0,0); |
| { |
| my $parser = new XML::Parser(ErrorContext => 2, |
| Namespaces => 1); |
| $parser->setHandlers( |
| Start => \&start_handler, |
| End => \&end_handler, |
| Default => \&default_handler); |
| $parser->parsefile($desc); |
| } |
| } |
| |
| # param 1: name of the attribute we look for |
| # param 2: array of name value pairs, the first subscript is the attribute and the second |
| # is the value. |
| sub findAttribute($$) |
| { |
| my ($name, $args_r) = @_; |
| my @args = @{$args_r}; |
| my $value; |
| while (my $attr = shift(@args)) |
| { |
| if ($attr eq $name) { |
| $value = shift(@args); |
| die "href attribut has no valid URL" unless $value; |
| last; |
| } else { # shift away the following value for the attribute |
| shift(@args); |
| } |
| } |
| return $value; |
| } |
| |
| #collect the prefixes used in an xml element |
| #param 1: parser, |
| #param 2: element name, |
| #param 3: array of name and values of attributes |
| #param 4: out parameter, the array containing the prefixes |
| sub collectPrefixes($$$$) |
| { |
| my $parser = shift; |
| my $name = shift; |
| my $attr_r = shift; |
| my $out_r = shift; |
| #get the prefixes which are currently valid |
| my @cur = $parser->current_ns_prefixes(); |
| my %map_ns; |
| #get the namespaces for the prefixes |
| for (@cur) { |
| if ($_ eq '#default') { |
| next; |
| } |
| my $ns = $parser->expand_ns_prefix($_); |
| $map_ns{$ns} = $_; |
| } |
| #investigat ns of element |
| my $pref = $map_ns{$parser->namespace($name)}; |
| push(@{$out_r}, $pref) if $pref; |
| #now go over the attributes |
| |
| while (my $attr = shift(@{$attr_r})) { |
| my $ns = $parser->namespace($attr); |
| if (! $ns) { |
| shift(@{$attr_r}); |
| next; |
| } |
| $pref = $map_ns{$ns}; |
| push( @{$out_r}, $pref) if $pref; |
| shift(@{$attr_r}); |
| } |
| #also add newly defined prefixes |
| my @newNs = $parser->new_ns_prefixes(); |
| for (@newNs) { |
| if ($_ eq '#default') { |
| next; |
| } |
| push (@{$out_r}, $_); |
| } |
| } |
| |
| #The function is called for each child element of dependencies. It finds out the prefixes |
| #which are used by the children and which are defined by the parents of <dependencies>. These |
| #would be lost when copying the children of <dependencies> into the update information file. |
| #Therefore these definitions are collected so that they then can be written in the <dependencies> |
| #element of the update information file. |
| #param 1: parser |
| #param 2: namsepace |
| #param 3: the @_ received in the start handler |
| sub determineNsDefinitions($$$) |
| { |
| my ($parser, $name, $attr_r) = @_; |
| my @attr = @{$attr_r}; |
| |
| determineNsDefinitionForItem($parser, $name, 1); |
| |
| while (my $attr = shift(@attr)) { |
| determineNsDefinitionForItem($parser, $attr, 0); |
| shift @attr; |
| } |
| } |
| |
| #do not call this function for the element that does not use a prefix |
| #param 1: parser |
| #param 2: name of the element or attribute |
| #param 3: 1 if called for an elment name and 0 when called for attribue |
| sub determineNsDefinitionForItem($$$) |
| { |
| my ($parser, $name) = @_; |
| my $ns = $parser->namespace($name); |
| if (! $ns) { |
| return; |
| } |
| #If the namespace was not kwown in <dependencies> then it was defined in one of its children |
| #or in this element. Then we are done since this namespace definition is copied into the |
| #update information. |
| my $bNsKnownInDep; |
| for ( keys %validPrefsInDep) { |
| if ( $validPrefsInDep{$_} eq $ns) { |
| $bNsKnownInDep = 1; |
| last; |
| } |
| } |
| #If the namespace of the current element is known in <dependencies> then check if the same |
| #prefix is used. If not, then the prefix was defined in one of the children of <dependencies> |
| #and was assigned the same namespace. Because we copy of children into the update information, |
| #this definition is also copied. |
| if ($bNsKnownInDep) { |
| #create a map of currently valid prefix/namespace |
| my %curPrefToNs; |
| my @curNs = $parser->current_ns_prefixes(); |
| for (@curNs) { |
| $curPrefToNs{$_} = $parser->expand_ns_prefix($_); |
| } |
| #find the prefix used in <dependencies> to define the namespace of the current element |
| my $validDepPref; |
| for (keys %validPrefsInDep) { |
| if ($validPrefsInDep{$_} eq $ns) { |
| #ignore #default |
| next if $_ eq "#default"; |
| $validDepPref = $_; |
| last; |
| } |
| } |
| #find the prefix defined in the current element used for the namespace of the element |
| my $curPref; |
| for (keys %curPrefToNs) { |
| if ($curPrefToNs{$_} eq $ns) { |
| #ignore #default |
| next if $_ eq "#default"; |
| $curPref = $_; |
| last; |
| } |
| } |
| if ($curPref && $validDepPref && ($curPref eq $validDepPref)) { |
| #If the prefixes and ns are the same, then the prefix definition of <dependencies> or its |
| #parent can be used. However, we need to find out which prefixed are NOT defined in |
| #<dependencies> so we can add them to it when we write the update information. |
| my $bDefined = 0; |
| for (@newPrefsInDep) { |
| if ($curPref eq $_) { |
| $bDefined = 1; |
| last; |
| } |
| } |
| if (! $bDefined) { |
| $notDefInDep{$curPref} = $ns; |
| } |
| } |
| } |
| } |