diff options
Diffstat (limited to 'solenv/bin/make_ext_update_info.pl')
-rwxr-xr-x | solenv/bin/make_ext_update_info.pl | 613 |
1 files changed, 613 insertions, 0 deletions
diff --git a/solenv/bin/make_ext_update_info.pl b/solenv/bin/make_ext_update_info.pl new file mode 100755 index 000000000000..f350dc489386 --- /dev/null +++ b/solenv/bin/make_ext_update_info.pl @@ -0,0 +1,613 @@ +: +eval 'exec perl -wS $0 ${1+"$@"}' + if 0; +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 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; + } + } + } +} |