1*b1cdbd2cSJim Jagielski#**************************************************************
2*b1cdbd2cSJim Jagielski#
3*b1cdbd2cSJim Jagielski#  Licensed to the Apache Software Foundation (ASF) under one
4*b1cdbd2cSJim Jagielski#  or more contributor license agreements.  See the NOTICE file
5*b1cdbd2cSJim Jagielski#  distributed with this work for additional information
6*b1cdbd2cSJim Jagielski#  regarding copyright ownership.  The ASF licenses this file
7*b1cdbd2cSJim Jagielski#  to you under the Apache License, Version 2.0 (the
8*b1cdbd2cSJim Jagielski#  "License"); you may not use this file except in compliance
9*b1cdbd2cSJim Jagielski#  with the License.  You may obtain a copy of the License at
10*b1cdbd2cSJim Jagielski#
11*b1cdbd2cSJim Jagielski#    http://www.apache.org/licenses/LICENSE-2.0
12*b1cdbd2cSJim Jagielski#
13*b1cdbd2cSJim Jagielski#  Unless required by applicable law or agreed to in writing,
14*b1cdbd2cSJim Jagielski#  software distributed under the License is distributed on an
15*b1cdbd2cSJim Jagielski#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*b1cdbd2cSJim Jagielski#  KIND, either express or implied.  See the License for the
17*b1cdbd2cSJim Jagielski#  specific language governing permissions and limitations
18*b1cdbd2cSJim Jagielski#  under the License.
19*b1cdbd2cSJim Jagielski#
20*b1cdbd2cSJim Jagielski#**************************************************************
21*b1cdbd2cSJim Jagielski
22*b1cdbd2cSJim Jagielskipackage installer::patch::MsiTable;
23*b1cdbd2cSJim Jagielski
24*b1cdbd2cSJim Jagielski=head1 NAME
25*b1cdbd2cSJim Jagielski
26*b1cdbd2cSJim Jagielski    package installer::patch::MsiTable - Class that represents one table of an Msi file.
27*b1cdbd2cSJim Jagielski
28*b1cdbd2cSJim Jagielski=cut
29*b1cdbd2cSJim Jagielski
30*b1cdbd2cSJim Jagielskiuse installer::patch::MsiRow;
31*b1cdbd2cSJim Jagielski
32*b1cdbd2cSJim Jagielskiuse strict;
33*b1cdbd2cSJim Jagielski
34*b1cdbd2cSJim Jagielski=head new ($class, $filename, $table_name)
35*b1cdbd2cSJim Jagielski
36*b1cdbd2cSJim Jagielski    Create a new MsiTable object from the output of a previous
37*b1cdbd2cSJim Jagielski    msidb.exe run.  The table is named $table_name, its data is read
38*b1cdbd2cSJim Jagielski    from $filename.
39*b1cdbd2cSJim Jagielski
40*b1cdbd2cSJim Jagielski=cut
41*b1cdbd2cSJim Jagielskisub new ($$$)
42*b1cdbd2cSJim Jagielski{
43*b1cdbd2cSJim Jagielski    my ($class, $filename, $table_name) = @_;
44*b1cdbd2cSJim Jagielski
45*b1cdbd2cSJim Jagielski    my $self = {
46*b1cdbd2cSJim Jagielski        'name' => $table_name,
47*b1cdbd2cSJim Jagielski        'filename' => $filename,
48*b1cdbd2cSJim Jagielski        'columns' => undef,
49*b1cdbd2cSJim Jagielski        'column_specs' => undef,
50*b1cdbd2cSJim Jagielski        'codepage' => undef,
51*b1cdbd2cSJim Jagielski        'is_valid' => 1,
52*b1cdbd2cSJim Jagielski        'is_modified' => 0
53*b1cdbd2cSJim Jagielski    };
54*b1cdbd2cSJim Jagielski    bless($self, $class);
55*b1cdbd2cSJim Jagielski
56*b1cdbd2cSJim Jagielski    if (defined $filename &&  -f $filename)
57*b1cdbd2cSJim Jagielski    {
58*b1cdbd2cSJim Jagielski        $self->ReadFile($filename);
59*b1cdbd2cSJim Jagielski    }
60*b1cdbd2cSJim Jagielski    return $self;
61*b1cdbd2cSJim Jagielski}
62*b1cdbd2cSJim Jagielski
63*b1cdbd2cSJim Jagielski
64*b1cdbd2cSJim Jagielski
65*b1cdbd2cSJim Jagielski
66*b1cdbd2cSJim Jagielskisub SetColumnData ($@)
67*b1cdbd2cSJim Jagielski{
68*b1cdbd2cSJim Jagielski    my ($self, @data) = @_;
69*b1cdbd2cSJim Jagielski
70*b1cdbd2cSJim Jagielski    if (((scalar @data) % 2) != 0)
71*b1cdbd2cSJim Jagielski    {
72*b1cdbd2cSJim Jagielski        installer::logger::PrintError("column data has to have an even number of elements: (<column-name> <data-spec>)+)\n");
73*b1cdbd2cSJim Jagielski        $self->{'is_valid'} = 0;
74*b1cdbd2cSJim Jagielski        return;
75*b1cdbd2cSJim Jagielski    }
76*b1cdbd2cSJim Jagielski
77*b1cdbd2cSJim Jagielski    $self->{'columns'} = [];
78*b1cdbd2cSJim Jagielski    $self->{'column_specs'} = [];
79*b1cdbd2cSJim Jagielski    while (scalar @data > 0)
80*b1cdbd2cSJim Jagielski    {
81*b1cdbd2cSJim Jagielski        my $name = shift @data;
82*b1cdbd2cSJim Jagielski        my $spec = shift @data;
83*b1cdbd2cSJim Jagielski        push @{$self->{'columns'}}, $name;
84*b1cdbd2cSJim Jagielski        push @{$self->{'column_specs'}}, $spec;
85*b1cdbd2cSJim Jagielski    }
86*b1cdbd2cSJim Jagielski}
87*b1cdbd2cSJim Jagielski
88*b1cdbd2cSJim Jagielski
89*b1cdbd2cSJim Jagielski
90*b1cdbd2cSJim Jagielski
91*b1cdbd2cSJim Jagielskisub SetIndexColumns ($@)
92*b1cdbd2cSJim Jagielski{
93*b1cdbd2cSJim Jagielski    my ($self, @index_columns) = @_;
94*b1cdbd2cSJim Jagielski
95*b1cdbd2cSJim Jagielski    $self->{'index_columns'} = [@index_columns];
96*b1cdbd2cSJim Jagielski}
97*b1cdbd2cSJim Jagielski
98*b1cdbd2cSJim Jagielski
99*b1cdbd2cSJim Jagielski
100*b1cdbd2cSJim Jagielski
101*b1cdbd2cSJim Jagielskisub SetCodepage ($$)
102*b1cdbd2cSJim Jagielski{
103*b1cdbd2cSJim Jagielski    my ($self, $codepage) = @_;
104*b1cdbd2cSJim Jagielski
105*b1cdbd2cSJim Jagielski    $self->{'codepage'} = $codepage;
106*b1cdbd2cSJim Jagielski}
107*b1cdbd2cSJim Jagielski
108*b1cdbd2cSJim Jagielski
109*b1cdbd2cSJim Jagielski
110*b1cdbd2cSJim Jagielski
111*b1cdbd2cSJim Jagielskisub IsValid ($)
112*b1cdbd2cSJim Jagielski{
113*b1cdbd2cSJim Jagielski    my ($self) = @_;
114*b1cdbd2cSJim Jagielski    return $self->{'is_valid'};
115*b1cdbd2cSJim Jagielski}
116*b1cdbd2cSJim Jagielski
117*b1cdbd2cSJim Jagielski
118*b1cdbd2cSJim Jagielski
119*b1cdbd2cSJim Jagielski
120*b1cdbd2cSJim Jagielskisub Trim ($)
121*b1cdbd2cSJim Jagielski{
122*b1cdbd2cSJim Jagielski    my $line = shift;
123*b1cdbd2cSJim Jagielski
124*b1cdbd2cSJim Jagielski    $line =~ s/(^\s+|\s+$)//g;
125*b1cdbd2cSJim Jagielski
126*b1cdbd2cSJim Jagielski    return $line;
127*b1cdbd2cSJim Jagielski}
128*b1cdbd2cSJim Jagielski
129*b1cdbd2cSJim Jagielski
130*b1cdbd2cSJim Jagielski
131*b1cdbd2cSJim Jagielski=head2 ReadFile($self, $filename)
132*b1cdbd2cSJim Jagielski
133*b1cdbd2cSJim Jagielski    Read the content of the table from the specified .idt file.
134*b1cdbd2cSJim Jagielski    For each row a MsiRow object is appended to $self->{'rows'}.
135*b1cdbd2cSJim Jagielski
136*b1cdbd2cSJim Jagielski=cut
137*b1cdbd2cSJim Jagielskisub ReadFile ($$)
138*b1cdbd2cSJim Jagielski{
139*b1cdbd2cSJim Jagielski    my ($self, $filename) = @_;
140*b1cdbd2cSJim Jagielski
141*b1cdbd2cSJim Jagielski    if ( ! (-f $filename && -r $filename))
142*b1cdbd2cSJim Jagielski    {
143*b1cdbd2cSJim Jagielski        printf STDERR ("can not open idt file %s for reading\n", $filename);
144*b1cdbd2cSJim Jagielski        $self->{'is_valid'} = 0;
145*b1cdbd2cSJim Jagielski        return;
146*b1cdbd2cSJim Jagielski    }
147*b1cdbd2cSJim Jagielski
148*b1cdbd2cSJim Jagielski    open my $in, "<", $filename;
149*b1cdbd2cSJim Jagielski
150*b1cdbd2cSJim Jagielski    my $columns = Trim(<$in>);
151*b1cdbd2cSJim Jagielski    $self->{'columns'} = [split(/\t/, $columns)];
152*b1cdbd2cSJim Jagielski
153*b1cdbd2cSJim Jagielski    my $column_specs = Trim(<$in>);
154*b1cdbd2cSJim Jagielski    $self->{'column_specs'} = [split(/\t/, $column_specs)];
155*b1cdbd2cSJim Jagielski
156*b1cdbd2cSJim Jagielski    # Table name, index columns.
157*b1cdbd2cSJim Jagielski    my $line = Trim(<$in>);
158*b1cdbd2cSJim Jagielski    my @items = split(/\t/, $line);
159*b1cdbd2cSJim Jagielski    my $item_count = scalar @items;
160*b1cdbd2cSJim Jagielski    if ($item_count>=1 && $items[0] eq $self->{'name'})
161*b1cdbd2cSJim Jagielski    {
162*b1cdbd2cSJim Jagielski        # No codepage.
163*b1cdbd2cSJim Jagielski    }
164*b1cdbd2cSJim Jagielski    elsif ($item_count>=2 && $items[1] eq $self->{'name'})
165*b1cdbd2cSJim Jagielski    {
166*b1cdbd2cSJim Jagielski        $self->{'codepage'} = shift @items;
167*b1cdbd2cSJim Jagielski    }
168*b1cdbd2cSJim Jagielski    else
169*b1cdbd2cSJim Jagielski    {
170*b1cdbd2cSJim Jagielski        printf STDERR ("reading wrong table data for table '%s' (got %s)\n", $self->{'name'}, $items[0]);
171*b1cdbd2cSJim Jagielski        $self->{'is_valid'} = 0;
172*b1cdbd2cSJim Jagielski        return;
173*b1cdbd2cSJim Jagielski    }
174*b1cdbd2cSJim Jagielski    shift @items;
175*b1cdbd2cSJim Jagielski    $self->{'index_columns'} = [@items];
176*b1cdbd2cSJim Jagielski    $self->{'index_column_index'} = $self->GetColumnIndex($items[0]);
177*b1cdbd2cSJim Jagielski
178*b1cdbd2cSJim Jagielski    my $rows = [];
179*b1cdbd2cSJim Jagielski    while (<$in>)
180*b1cdbd2cSJim Jagielski    {
181*b1cdbd2cSJim Jagielski        # Remove all trailing returns and newlines.  Keep trailing spaces and tabs.
182*b1cdbd2cSJim Jagielski        s/[\r\n]+$//g;
183*b1cdbd2cSJim Jagielski
184*b1cdbd2cSJim Jagielski        my @items = split(/\t/, $_);
185*b1cdbd2cSJim Jagielski        push @$rows, new installer::patch::MsiRow($self, @items);
186*b1cdbd2cSJim Jagielski    }
187*b1cdbd2cSJim Jagielski    $self->{'rows'} = $rows;
188*b1cdbd2cSJim Jagielski
189*b1cdbd2cSJim Jagielski    return $self;
190*b1cdbd2cSJim Jagielski}
191*b1cdbd2cSJim Jagielski
192*b1cdbd2cSJim Jagielski
193*b1cdbd2cSJim Jagielski
194*b1cdbd2cSJim Jagielski
195*b1cdbd2cSJim Jagielski=head WriteFile($self, $filename)
196*b1cdbd2cSJim Jagielski
197*b1cdbd2cSJim Jagielski    Write a text file containing the current table content.
198*b1cdbd2cSJim Jagielski
199*b1cdbd2cSJim Jagielski=cut
200*b1cdbd2cSJim Jagielskisub WriteFile ($$)
201*b1cdbd2cSJim Jagielski{
202*b1cdbd2cSJim Jagielski    my ($self, $filename) = @_;
203*b1cdbd2cSJim Jagielski
204*b1cdbd2cSJim Jagielski    open my $out, ">".$self->{'filename'};
205*b1cdbd2cSJim Jagielski
206*b1cdbd2cSJim Jagielski    print $out join("\t", @{$self->{'columns'}})."\r\n";
207*b1cdbd2cSJim Jagielski    print $out join("\t", @{$self->{'column_specs'}})."\r\n";
208*b1cdbd2cSJim Jagielski    if (defined $self->{'codepage'})
209*b1cdbd2cSJim Jagielski    {
210*b1cdbd2cSJim Jagielski        print $out $self->{'codepage'} . "\t";
211*b1cdbd2cSJim Jagielski    }
212*b1cdbd2cSJim Jagielski    print $out $self->{'name'} . "\t";
213*b1cdbd2cSJim Jagielski    print $out join("\t",@{$self->{'index_columns'}})."\r\n";
214*b1cdbd2cSJim Jagielski
215*b1cdbd2cSJim Jagielski    foreach my $row (@{$self->{'rows'}})
216*b1cdbd2cSJim Jagielski    {
217*b1cdbd2cSJim Jagielski        print $out $row->Format("\t")."\r\n";
218*b1cdbd2cSJim Jagielski    }
219*b1cdbd2cSJim Jagielski
220*b1cdbd2cSJim Jagielski    close $out;
221*b1cdbd2cSJim Jagielski}
222*b1cdbd2cSJim Jagielski
223*b1cdbd2cSJim Jagielski
224*b1cdbd2cSJim Jagielski
225*b1cdbd2cSJim Jagielski
226*b1cdbd2cSJim Jagielskisub UpdateTimestamp ($)
227*b1cdbd2cSJim Jagielski{
228*b1cdbd2cSJim Jagielski    my $self = shift;
229*b1cdbd2cSJim Jagielski
230*b1cdbd2cSJim Jagielski    utime(undef,undef, $self->{'filename'});
231*b1cdbd2cSJim Jagielski}
232*b1cdbd2cSJim Jagielski
233*b1cdbd2cSJim Jagielski
234*b1cdbd2cSJim Jagielski
235*b1cdbd2cSJim Jagielski
236*b1cdbd2cSJim Jagielskisub GetName ($)
237*b1cdbd2cSJim Jagielski{
238*b1cdbd2cSJim Jagielski    my $self = shift;
239*b1cdbd2cSJim Jagielski
240*b1cdbd2cSJim Jagielski    return $self->{'name'};
241*b1cdbd2cSJim Jagielski}
242*b1cdbd2cSJim Jagielski
243*b1cdbd2cSJim Jagielski
244*b1cdbd2cSJim Jagielski
245*b1cdbd2cSJim Jagielski
246*b1cdbd2cSJim Jagielski=head2 GetColumnCount($self)
247*b1cdbd2cSJim Jagielski
248*b1cdbd2cSJim Jagielski    Return the number of columns in the table.
249*b1cdbd2cSJim Jagielski
250*b1cdbd2cSJim Jagielski=cut
251*b1cdbd2cSJim Jagielskisub GetColumnCount ($)
252*b1cdbd2cSJim Jagielski{
253*b1cdbd2cSJim Jagielski    my ($self) = @_;
254*b1cdbd2cSJim Jagielski
255*b1cdbd2cSJim Jagielski    return scalar @{$self->{'columns'}};
256*b1cdbd2cSJim Jagielski}
257*b1cdbd2cSJim Jagielski
258*b1cdbd2cSJim Jagielski
259*b1cdbd2cSJim Jagielski
260*b1cdbd2cSJim Jagielski
261*b1cdbd2cSJim Jagielski=head2 GetRowCount($self)
262*b1cdbd2cSJim Jagielski
263*b1cdbd2cSJim Jagielski    Return the number of rows in the table.
264*b1cdbd2cSJim Jagielski
265*b1cdbd2cSJim Jagielski=cut
266*b1cdbd2cSJim Jagielskisub GetRowCount ($)
267*b1cdbd2cSJim Jagielski{
268*b1cdbd2cSJim Jagielski    my ($self) = @_;
269*b1cdbd2cSJim Jagielski
270*b1cdbd2cSJim Jagielski    return scalar @{$self->{'rows'}};
271*b1cdbd2cSJim Jagielski}
272*b1cdbd2cSJim Jagielski
273*b1cdbd2cSJim Jagielski
274*b1cdbd2cSJim Jagielski
275*b1cdbd2cSJim Jagielski
276*b1cdbd2cSJim Jagielski=head2 GetColumnIndx($self, $column_name)
277*b1cdbd2cSJim Jagielski
278*b1cdbd2cSJim Jagielski    Return the 0 based index of the column named $column_name.  Use
279*b1cdbd2cSJim Jagielski    this to speed up (slightly) access to column values when accessing
280*b1cdbd2cSJim Jagielski    many or all rows of a table.
281*b1cdbd2cSJim Jagielski
282*b1cdbd2cSJim Jagielski=cut
283*b1cdbd2cSJim Jagielskisub GetColumnIndex ($$)
284*b1cdbd2cSJim Jagielski{
285*b1cdbd2cSJim Jagielski    my ($self, $column_name) = @_;
286*b1cdbd2cSJim Jagielski
287*b1cdbd2cSJim Jagielski    my $index = 0;
288*b1cdbd2cSJim Jagielski    foreach my $name (@{$self->{'columns'}})
289*b1cdbd2cSJim Jagielski    {
290*b1cdbd2cSJim Jagielski        if ($name eq $column_name)
291*b1cdbd2cSJim Jagielski        {
292*b1cdbd2cSJim Jagielski            return $index;
293*b1cdbd2cSJim Jagielski        }
294*b1cdbd2cSJim Jagielski        ++$index;
295*b1cdbd2cSJim Jagielski    }
296*b1cdbd2cSJim Jagielski
297*b1cdbd2cSJim Jagielski    printf STDERR ("did not find column %s in %s\n", $column_name, join(" and ", @{$self->{'columns'}}));
298*b1cdbd2cSJim Jagielski    return -1;
299*b1cdbd2cSJim Jagielski}
300*b1cdbd2cSJim Jagielski
301*b1cdbd2cSJim Jagielski
302*b1cdbd2cSJim Jagielski
303*b1cdbd2cSJim Jagielski=head2 GetRowIndex($self, $index_column_index, $index_column_value)
304*b1cdbd2cSJim Jagielski
305*b1cdbd2cSJim Jagielski    Return the index, starting at 0, of the (first) row that has value $index_column_value
306*b1cdbd2cSJim Jagielski    in column with index $index_column_index.
307*b1cdbd2cSJim Jagielski
308*b1cdbd2cSJim Jagielski    Return -1 if now such row is found.
309*b1cdbd2cSJim Jagielski
310*b1cdbd2cSJim Jagielski=cut
311*b1cdbd2cSJim Jagielskisub GetRowIndex ($$$)
312*b1cdbd2cSJim Jagielski{
313*b1cdbd2cSJim Jagielski    my ($self, $index_column_index, $index_column_value) = @_;
314*b1cdbd2cSJim Jagielski
315*b1cdbd2cSJim Jagielski    my $rows = $self->{'rows'};
316*b1cdbd2cSJim Jagielski    for (my ($row_index,$row_count)=(0,scalar @$rows); $row_index<$row_count; ++$row_index)
317*b1cdbd2cSJim Jagielski    {
318*b1cdbd2cSJim Jagielski        my $row = $rows->[$row_index];
319*b1cdbd2cSJim Jagielski        if ($row->GetValue($index_column_index) eq $index_column_value)
320*b1cdbd2cSJim Jagielski        {
321*b1cdbd2cSJim Jagielski            return $row_index;
322*b1cdbd2cSJim Jagielski        }
323*b1cdbd2cSJim Jagielski    }
324*b1cdbd2cSJim Jagielski
325*b1cdbd2cSJim Jagielski    return -1;
326*b1cdbd2cSJim Jagielski}
327*b1cdbd2cSJim Jagielski
328*b1cdbd2cSJim Jagielski
329*b1cdbd2cSJim Jagielski
330*b1cdbd2cSJim Jagielski
331*b1cdbd2cSJim Jagielski=head2 GetValue($self, $selector_column, $selector_column_value, $value_column)
332*b1cdbd2cSJim Jagielski
333*b1cdbd2cSJim Jagielski    Find the row in which the $selector_column has value
334*b1cdbd2cSJim Jagielski    $selector_column_value and return its value in the $value_column.
335*b1cdbd2cSJim Jagielski
336*b1cdbd2cSJim Jagielski=cut
337*b1cdbd2cSJim Jagielski
338*b1cdbd2cSJim Jagielskisub GetValue ($$$$)
339*b1cdbd2cSJim Jagielski{
340*b1cdbd2cSJim Jagielski    my ($self, $selector_column, $selector_column_value, $value_column) = @_;
341*b1cdbd2cSJim Jagielski
342*b1cdbd2cSJim Jagielski    my $row = $self->GetRow($selector_column, $selector_column_value);
343*b1cdbd2cSJim Jagielski    if (defined $row)
344*b1cdbd2cSJim Jagielski    {
345*b1cdbd2cSJim Jagielski        return $row->GetValue($value_column);
346*b1cdbd2cSJim Jagielski    }
347*b1cdbd2cSJim Jagielski    else
348*b1cdbd2cSJim Jagielski    {
349*b1cdbd2cSJim Jagielski        return undef;
350*b1cdbd2cSJim Jagielski    }
351*b1cdbd2cSJim Jagielski}
352*b1cdbd2cSJim Jagielski
353*b1cdbd2cSJim Jagielski
354*b1cdbd2cSJim Jagielski
355*b1cdbd2cSJim Jagielski
356*b1cdbd2cSJim Jagielski=head2 GetRow($self, $column, $value)
357*b1cdbd2cSJim Jagielski
358*b1cdbd2cSJim Jagielski    Return the (first) row which has $value in $column.
359*b1cdbd2cSJim Jagielski
360*b1cdbd2cSJim Jagielski=cut
361*b1cdbd2cSJim Jagielskisub GetRow ($$$)
362*b1cdbd2cSJim Jagielski{
363*b1cdbd2cSJim Jagielski    my ($self, $column, $value) = @_;
364*b1cdbd2cSJim Jagielski
365*b1cdbd2cSJim Jagielski    my $column_index = $self->GetColumnIndex($column);
366*b1cdbd2cSJim Jagielski    if ($column_index<0)
367*b1cdbd2cSJim Jagielski    {
368*b1cdbd2cSJim Jagielski        printf STDERR "ERROR: unknown column $column in table $self->{'name'}\n";
369*b1cdbd2cSJim Jagielski        return undef;
370*b1cdbd2cSJim Jagielski    }
371*b1cdbd2cSJim Jagielski
372*b1cdbd2cSJim Jagielski    foreach my $row (@{$self->{'rows'}})
373*b1cdbd2cSJim Jagielski    {
374*b1cdbd2cSJim Jagielski        if ($row->GetValue($column_index) eq $value)
375*b1cdbd2cSJim Jagielski        {
376*b1cdbd2cSJim Jagielski            return $row;
377*b1cdbd2cSJim Jagielski        }
378*b1cdbd2cSJim Jagielski    }
379*b1cdbd2cSJim Jagielski
380*b1cdbd2cSJim Jagielski    printf STDERR ("ERROR: did not find row for %s->%s in %s\n",
381*b1cdbd2cSJim Jagielski        $column,
382*b1cdbd2cSJim Jagielski        $value,
383*b1cdbd2cSJim Jagielski        table $self->{'name'});
384*b1cdbd2cSJim Jagielski
385*b1cdbd2cSJim Jagielski    return undef;
386*b1cdbd2cSJim Jagielski}
387*b1cdbd2cSJim Jagielski
388*b1cdbd2cSJim Jagielski
389*b1cdbd2cSJim Jagielski
390*b1cdbd2cSJim Jagielski
391*b1cdbd2cSJim Jagielski=head2 GetAllRows ($self)
392*b1cdbd2cSJim Jagielski
393*b1cdbd2cSJim Jagielski    Return the reference to an array that contains all rows of the table.
394*b1cdbd2cSJim Jagielski
395*b1cdbd2cSJim Jagielski=cut
396*b1cdbd2cSJim Jagielski
397*b1cdbd2cSJim Jagielskisub GetAllRows ($)
398*b1cdbd2cSJim Jagielski{
399*b1cdbd2cSJim Jagielski    my $self = shift;
400*b1cdbd2cSJim Jagielski
401*b1cdbd2cSJim Jagielski    return $self->{'rows'};
402*b1cdbd2cSJim Jagielski}
403*b1cdbd2cSJim Jagielski
404*b1cdbd2cSJim Jagielski
405*b1cdbd2cSJim Jagielski
406*b1cdbd2cSJim Jagielski
407*b1cdbd2cSJim Jagielski=head2 SetRow($self, {$key, $value}*)
408*b1cdbd2cSJim Jagielski
409*b1cdbd2cSJim Jagielski    Replace an existing row.  If no matching row is found then add the row.
410*b1cdbd2cSJim Jagielski
411*b1cdbd2cSJim Jagielski    The row is defined by a set of key/value pairs.  Their order is defined by the keys (column names)
412*b1cdbd2cSJim Jagielski    and their indices as defined in $self->{'columns'}.
413*b1cdbd2cSJim Jagielski
414*b1cdbd2cSJim Jagielski    Rows are compared by their values of the index column.  By default this is the first element of
415*b1cdbd2cSJim Jagielski    $self->{'index_columns'} but is overruled by the last key that starts with a '*'.
416*b1cdbd2cSJim Jagielski
417*b1cdbd2cSJim Jagielski=cut
418*b1cdbd2cSJim Jagielskisub SetRow ($@)
419*b1cdbd2cSJim Jagielski{
420*b1cdbd2cSJim Jagielski    my $self = shift;
421*b1cdbd2cSJim Jagielski    my @data = @_;
422*b1cdbd2cSJim Jagielski
423*b1cdbd2cSJim Jagielski    my @items = ();
424*b1cdbd2cSJim Jagielski    my $index_column = $self->{'index_columns'}->[0];
425*b1cdbd2cSJim Jagielski
426*b1cdbd2cSJim Jagielski    # Key/Value has to have an even number of entries.
427*b1cdbd2cSJim Jagielski    MsiTools::Die("invalid arguments given to MsiTable::SetRow()\n") if (scalar @data%2) != 0;
428*b1cdbd2cSJim Jagielski
429*b1cdbd2cSJim Jagielski    # Find column indices for column names.
430*b1cdbd2cSJim Jagielski    while (scalar @data > 0)
431*b1cdbd2cSJim Jagielski    {
432*b1cdbd2cSJim Jagielski        my $column_name = shift @data;
433*b1cdbd2cSJim Jagielski        if ($column_name =~ /^\*(.*)$/)
434*b1cdbd2cSJim Jagielski        {
435*b1cdbd2cSJim Jagielski            # Column name starts with a '*'.  Use it as index column.
436*b1cdbd2cSJim Jagielski            $column_name = $1;
437*b1cdbd2cSJim Jagielski            $index_column = $1;
438*b1cdbd2cSJim Jagielski        }
439*b1cdbd2cSJim Jagielski        my $value = shift @data;
440*b1cdbd2cSJim Jagielski        my $column_index = $self->GetColumnIndex($column_name);
441*b1cdbd2cSJim Jagielski        $items[$column_index] = $value;
442*b1cdbd2cSJim Jagielski    }
443*b1cdbd2cSJim Jagielski
444*b1cdbd2cSJim Jagielski    my $index_column_index = $self->GetColumnIndex($index_column);
445*b1cdbd2cSJim Jagielski    my $row_index = $self->GetRowIndex($index_column_index, $items[$index_column_index]);
446*b1cdbd2cSJim Jagielski
447*b1cdbd2cSJim Jagielski    if ($row_index < 0)
448*b1cdbd2cSJim Jagielski    {
449*b1cdbd2cSJim Jagielski        # Row does not yet exist.  Add it.
450*b1cdbd2cSJim Jagielski        push @{$self->{'rows'}}, installer::patch::MsiRow->new($self, @items);
451*b1cdbd2cSJim Jagielski    }
452*b1cdbd2cSJim Jagielski    else
453*b1cdbd2cSJim Jagielski    {
454*b1cdbd2cSJim Jagielski        # Row does already exist.  Replace it.
455*b1cdbd2cSJim Jagielski        $self->{'rows'}->[$row_index] = installer::patch::MsiRow->new($self, @items);
456*b1cdbd2cSJim Jagielski    }
457*b1cdbd2cSJim Jagielski
458*b1cdbd2cSJim Jagielski    $self->MarkAsModified();
459*b1cdbd2cSJim Jagielski}
460*b1cdbd2cSJim Jagielski
461*b1cdbd2cSJim Jagielski
462*b1cdbd2cSJim Jagielski
463*b1cdbd2cSJim Jagielski
464*b1cdbd2cSJim Jagielskisub MarkAsModified ($)
465*b1cdbd2cSJim Jagielski{
466*b1cdbd2cSJim Jagielski    my $self = shift;
467*b1cdbd2cSJim Jagielski
468*b1cdbd2cSJim Jagielski    $self->{'is_modified'} = 1;
469*b1cdbd2cSJim Jagielski}
470*b1cdbd2cSJim Jagielski
471*b1cdbd2cSJim Jagielski
472*b1cdbd2cSJim Jagielski
473*b1cdbd2cSJim Jagielski
474*b1cdbd2cSJim Jagielskisub MarkAsUnmodified ($)
475*b1cdbd2cSJim Jagielski{
476*b1cdbd2cSJim Jagielski    my $self = shift;
477*b1cdbd2cSJim Jagielski
478*b1cdbd2cSJim Jagielski    $self->{'is_modified'} = 0;
479*b1cdbd2cSJim Jagielski}
480*b1cdbd2cSJim Jagielski
481*b1cdbd2cSJim Jagielski
482*b1cdbd2cSJim Jagielski
483*b1cdbd2cSJim Jagielski
484*b1cdbd2cSJim Jagielskisub IsModified ($)
485*b1cdbd2cSJim Jagielski{
486*b1cdbd2cSJim Jagielski    my $self = shift;
487*b1cdbd2cSJim Jagielski
488*b1cdbd2cSJim Jagielski    return $self->{'is_modified'};
489*b1cdbd2cSJim Jagielski}
490*b1cdbd2cSJim Jagielski
491*b1cdbd2cSJim Jagielski
492*b1cdbd2cSJim Jagielski1;
493