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
22
23
24package installer::windows::feature;
25
26use installer::existence;
27use installer::exiter;
28use installer::files;
29use installer::globals;
30use installer::sorter;
31use installer::worker;
32use installer::windows::idtglobal;
33use installer::windows::language;
34
35##############################################################
36# Returning the gid for a feature.
37# Attention: Maximum length
38##############################################################
39
40sub get_feature_gid
41{
42	my ($onefeature) = @_;
43
44	my $gid = "";
45
46	if ( $onefeature->{'gid'} ) { $gid = $onefeature->{'gid'}; }
47
48	# Attention: Maximum feature length is 38!
49	installer::windows::idtglobal::shorten_feature_gid(\$gid);
50
51	return $gid
52}
53
54##############################################################
55# Returning the gid of the parent.
56# Attention: Maximum length
57##############################################################
58
59sub get_feature_parent
60{
61	my ($onefeature) = @_;
62
63	my $parentgid = "";
64
65	if ( $onefeature->{'ParentID'} ) { $parentgid = $onefeature->{'ParentID'}; }
66
67	# The modules, hanging directly below the root, have to be root modules.
68	# Only then it is possible to make the "real" root module invisible by
69	# setting the display to "0".
70
71	if ( $parentgid eq $installer::globals::rootmodulegid ) { $parentgid = ""; }
72
73	# Attention: Maximum feature length is 38!
74	installer::windows::idtglobal::shorten_feature_gid(\$parentgid);
75
76	return $parentgid
77}
78
79##############################################################
80# Returning the display for a feature.
81# 0: Feature is not shown
82# odd: subfeatures are shown
83# even:  subfeatures are not shown
84##############################################################
85
86sub get_feature_display
87{
88	my ($onefeature) = @_;
89
90	my $display;
91	my $parentid = "";
92
93	if ( $onefeature->{'ParentID'} ) { $parentid = $onefeature->{'ParentID'}; }
94
95	if ( $parentid eq "" )
96	{
97		$display = "0";									# root module is not visible
98	}
99	elsif ( $onefeature->{'gid'} eq "gid_Module_Prg")	# program module shows subfeatures
100	{
101		$display = "1";									# root module shows subfeatures
102	}
103	else
104	{
105		$display = "2";									# all other modules do not show subfeatures
106	}
107
108	# special case: Feature has flag "HIDDEN_ROOT" -> $display is 0
109	my $styles = "";
110	if ( $onefeature->{'Styles'} ) { $styles = $onefeature->{'Styles'}; }
111	if ( $styles =~ /\bHIDDEN_ROOT\b/ ) { $display = "0"; }
112
113	# Special handling for language modules. Only visible in multilingual installation set
114	if (( $styles =~ /\bSHOW_MULTILINGUAL_ONLY\b/ ) && ( ! $installer::globals::ismultilingual )) { $display = "0"; }
115
116	# Special handling for c05office. No program module visible.
117	if (( $onefeature->{'gid'} eq "gid_Module_Prg" ) && ( $installer::globals::product =~ /c05office/i )) { $display = "0";	}
118
119	# making all feature invisible in Language packs!
120	if ( $installer::globals::languagepack ) { $display = "0"; }
121
122	return $display
123}
124
125##############################################################
126# Returning the level for a feature.
127##############################################################
128
129sub get_feature_level
130{
131	my ($onefeature) = @_;
132
133	my $level = "20";	# the default
134
135	my $localdefault = "";
136
137	if ( $onefeature->{'Default'} ) { $localdefault = $onefeature->{'Default'}; }
138
139	if ( $localdefault eq "NO" )	# explicitely set Default = "NO"
140	{
141		$level = "200";				# deselected in default installation, base is 100
142		if ( $installer::globals::patch ) { $level = "20"; }
143	}
144
145	# special handling for Java and Ada
146	if ( $onefeature->{'Name'} )
147	{
148		if ( $onefeature->{'Name'} =~ /java/i ) { $level = $level + 40; }
149	}
150
151	# if FeatureLevel is defined in scp, this will be used
152
153	if ( $onefeature->{'FeatureLevel'} ) { $level = $onefeature->{'FeatureLevel'}; }
154
155	return $level
156}
157
158##############################################################
159# Returning the directory for a feature.
160##############################################################
161
162sub get_feature_directory
163{
164	my ($onefeature) = @_;
165
166	my $directory;
167
168	$directory = "INSTALLLOCATION";
169
170	return $directory
171}
172
173##############################################################
174# Returning the directory for a feature.
175##############################################################
176
177sub get_feature_attributes
178{
179	my ($onefeature) = @_;
180
181	my $attributes;
182
183	# No advertising of features and no leaving on network.
184	# Feature without parent must not have the "2"
185
186	my $parentgid = "";
187	if ( $onefeature->{'ParentID'} ) { $parentgid = $onefeature->{'ParentID'}; }
188
189	if (( $parentgid eq "" ) || ( $parentgid eq $installer::globals::rootmodulegid )) { $attributes = "8"; }
190	else { $attributes = "10"; }
191
192	return $attributes
193}
194
195#################################################################################
196# Replacing one variable in one files
197#################################################################################
198
199sub replace_one_variable
200{
201	my ($translationfile, $variable, $searchstring) = @_;
202
203	for ( my $i = 0; $i <= $#{$translationfile}; $i++ )
204	{
205		${$translationfile}[$i] =~ s/\%$searchstring/$variable/g;
206	}
207}
208
209#################################################################################
210# Replacing the variables in the feature names and descriptions
211#################################################################################
212
213sub replace_variables
214{
215	my ($translationfile, $variableshashref) = @_;
216
217	foreach $key (keys %{$variableshashref})
218	{
219		my $value = $variableshashref->{$key};
220		replace_one_variable($translationfile, $value, $key);
221	}
222}
223
224#################################################################################
225# Collecting the feature recursively.
226#################################################################################
227
228sub collect_modules_recursive
229{
230	my ($modulesref, $parentid, $feature, $directaccess, $directgid, $directparent, $directsortkey, $sorted) = @_;
231
232	my @allchildren = ();
233	my $childrenexist = 0;
234
235	# Collecting children from Module $parentid
236
237	my $modulegid;
238	foreach $modulegid ( keys %{$directparent})
239	{
240		if ( $directparent->{$modulegid} eq $parentid )
241		{
242			my %childhash = ( "gid" => "$modulegid", "Sortkey" => "$directsortkey->{$modulegid}");
243			push(@allchildren, \%childhash);
244			$childrenexist = 1;
245		}
246	}
247
248	# Sorting children
249
250	if ( $childrenexist )
251	{
252		# Sort children
253		installer::sorter::sort_array_of_hashes_numerically(\@allchildren, "Sortkey");
254
255		# Adding children to new array
256		my $childhashref;
257		foreach $childhashref ( @allchildren )
258		{
259			my $gid = $childhashref->{'gid'};
260
261			# Saving all lines, that have this 'gid'
262
263			my $unique;
264			foreach $unique ( keys %{$directgid} )
265			{
266				if ( $directgid->{$unique} eq $gid )
267				{
268					push(@{$feature}, ${$modulesref}[$directaccess->{$unique}]);
269					if ( $sorted->{$unique} == 1 ) { installer::exiter::exit_program("ERROR: Sorting feature failed! \"$unique\" already sorted.", "sort_feature"); }
270					$sorted->{$unique} = 1;
271				}
272			}
273
274			collect_modules_recursive($modulesref, $gid, $feature, $directaccess, $directgid, $directparent, $directsortkey, $sorted);
275		}
276	}
277}
278
279#################################################################################
280# Sorting the feature in specified order. Evaluated is the key "Sortkey", that
281# is set in scp2 projects.
282# The display order of modules in Windows Installer is dependent from the order
283# in the idt file. Therefore the order of the modules array has to be adapted
284# to the Sortkey order, before the idt file is created.
285#################################################################################
286
287sub sort_feature
288{
289	my ($modulesref) = @_;
290
291	my @feature = ();
292
293	my %directaccess = ();
294	my %directparent = ();
295	my %directgid = ();
296	my %directsortkey = ();
297	my %sorted = ();
298
299	for ( my $i = 0; $i <= $#{$modulesref}; $i++ )
300	{
301		my $onefeature = ${$modulesref}[$i];
302
303		my $uniquekey = $onefeature->{'uniquekey'};
304		my $modulegid = $onefeature->{'gid'};
305
306		$directaccess{$uniquekey} = $i;
307
308		$directgid{$uniquekey} = $onefeature->{'gid'};
309
310		# ParentID and Sortkey are not saved for the 'uniquekey', but only for the 'gid'
311
312		if ( $onefeature->{'ParentID'} ) { $directparent{$modulegid} = $onefeature->{'ParentID'}; }
313		else { $directparent{$modulegid} = ""; }
314
315		if ( $onefeature->{'Sortkey'} ) { $directsortkey{$modulegid} = $onefeature->{'Sortkey'}; }
316		else { $directsortkey{$modulegid} = "9999"; }
317
318		# Bookkeeping:
319		$sorted{$uniquekey} = 0;
320	}
321
322	# Searching all feature recursively, beginning with ParentID = ""
323	my $parentid = "";
324	collect_modules_recursive($modulesref, $parentid, \@feature, \%directaccess, \%directgid, \%directparent, \%directsortkey, \%sorted);
325
326	# Bookkeeping
327	my $modulekey;
328	foreach $modulekey ( keys %sorted )
329	{
330		if ( $sorted{$modulekey} == 0 )
331		{
332            $installer::logger::Lang->printf(
333                "Warning: Module \"%s\" could not be sorted. Added to the end of the module array.\n",
334                $modulekey);
335			push(@feature, ${$modulesref}[$directaccess{$modulekey}]);
336		}
337	}
338
339	return \@feature;
340}
341
342#################################################################################
343# Adding a unique key to the modules array. The gid is not unique for
344# multilingual modules. Only the combination from gid and specific language
345# is unique. Uniqueness is required for sorting mechanism.
346#################################################################################
347
348sub add_uniquekey
349{
350	my ( $modulesref ) = @_;
351
352	for ( my $i = 0; $i <= $#{$modulesref}; $i++ )
353	{
354		my $uniquekey = ${$modulesref}[$i]->{'gid'};
355		if ( ${$modulesref}[$i]->{'specificlanguage'} ) { $uniquekey = $uniquekey . "_" . ${$modulesref}[$i]->{'specificlanguage'}; }
356		${$modulesref}[$i]->{'uniquekey'} = $uniquekey;
357	}
358}
359
360#################################################################################
361# Creating the file Feature.idt dynamically
362# Content:
363# Feature Feature_Parent Title Description Display Level Directory_ Attributes
364#################################################################################
365
366sub create_feature_table
367{
368	my ($modulesref, $basedir, $languagesarrayref, $allvariableshashref) = @_;
369
370	for ( my $m = 0; $m <= $#{$languagesarrayref}; $m++ )
371	{
372		my $onelanguage = ${$languagesarrayref}[$m];
373
374		my $infoline;
375
376		my @featuretable = ();
377
378		installer::windows::idtglobal::write_idt_header(\@featuretable, "feature");
379
380		for ( my $i = 0; $i <= $#{$modulesref}; $i++ )
381		{
382			my $onefeature = ${$modulesref}[$i];
383
384			# Java and Ada only, if the correct settings are set
385			my $styles = "";
386			if ( $onefeature->{'Styles'} ) { $styles = $onefeature->{'Styles'}; }
387			if (( $styles =~ /\bJAVAMODULE\b/ ) && ( ! ($allvariableshashref->{'JAVAPRODUCT'} ))) { next; }
388			if (( $styles =~ /\bADAMODULE\b/ ) && ( ! ($allvariableshashref->{'ADAPRODUCT'} ))) { next; }
389
390			# Controlling the language!
391			# Only language independent feature or feature with the correct language will be included into the table
392
393			if (! (!(( $onefeature->{'ismultilingual'} )) || ( $onefeature->{'specificlanguage'} eq $onelanguage )) )  { next; }
394
395			my %feature = ();
396
397			$feature{'feature'} = get_feature_gid($onefeature);
398			$feature{'feature_parent'} = get_feature_parent($onefeature);
399			# if ( $onefeature->{'ParentID'} eq "" ) { $feature{'feature_parent'} = ""; }	# Root has no parent
400			$feature{'Title'} = $onefeature->{'Name'};
401			$feature{'Description'} = $onefeature->{'Description'};
402			$feature{'Display'} = get_feature_display($onefeature);
403			$feature{'Level'} = get_feature_level($onefeature);
404			$feature{'Directory_'} = get_feature_directory($onefeature);
405			$feature{'Attributes'} = get_feature_attributes($onefeature);
406
407			my $oneline = $feature{'feature'} . "\t" . $feature{'feature_parent'} . "\t" . $feature{'Title'} . "\t"
408					. $feature{'Description'} . "\t" . $feature{'Display'} . "\t" . $feature{'Level'} . "\t"
409					. $feature{'Directory_'} . "\t" . $feature{'Attributes'} . "\n";
410
411			push(@featuretable, $oneline);
412
413			# collecting all feature in global feature collector (so that properties can be set in property table)
414			if ( ! installer::existence::exists_in_array($feature{'feature'}, \@installer::globals::featurecollector) )
415			{
416				push(@installer::globals::featurecollector, $feature{'feature'});
417			}
418
419			# collecting all language feature in feature collector for check of language selection
420			if (( $styles =~ /\bSHOW_MULTILINGUAL_ONLY\b/ ) && ( $onefeature->{'ParentID'} ne $installer::globals::rootmodulegid ))
421			{
422				$installer::globals::multilingual_only_modules{$feature{'feature'}} = 1;
423			}
424
425			# collecting all application feature in global feature collector for check of application selection
426			if ( $styles =~ /\bAPPLICATIONMODULE\b/ )
427			{
428				$installer::globals::application_modules{$feature{'feature'}} = 1;
429			}
430		}
431
432		# Saving the file
433
434		my $featuretablename = $basedir . $installer::globals::separator . "Feature.idt" . "." . $onelanguage;
435		installer::files::save_file($featuretablename ,\@featuretable);
436        $installer::logger::Lang->printf("Created idt file: %s\n", $featuretablename);
437	}
438}
439
4401;
441