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
22package installer::patch::MsiTable;
23
24=head1 NAME
25
26    package installer::patch::MsiTable - Class that represents one table of an Msi file.
27
28=cut
29
30use installer::patch::MsiRow;
31
32use strict;
33
34=head new ($class, $filename, $table_name)
35
36    Create a new MsiTable object from the output of a previous
37    msidb.exe run.  The table is named $table_name, its data is read
38    from $filename.
39
40=cut
41sub new ($$$)
42{
43    my ($class, $filename, $table_name) = @_;
44
45    my $self = {
46        'name' => $table_name,
47        'is_valid' => 1
48    };
49    bless($self, $class);
50
51    if ( -f $filename)
52    {
53        $self->ReadFile($filename);
54    }
55    return $self;
56}
57
58
59
60
61sub IsValid ($)
62{
63    my ($self) = @_;
64    return $self->{'is_valid'};
65}
66
67
68
69
70sub Trim ($)
71{
72    my $line = shift;
73
74    $line =~ s/(^\s+|\s+$)//g;
75
76    return $line;
77}
78
79
80
81=head2 ReadFile($self, $filename)
82
83    Read the content of the table from the specified .idt file.
84    For each row a MsiRow object is appended to $self->{'rows'}.
85
86=cut
87sub ReadFile ($$)
88{
89    my ($self, $filename) = @_;
90
91    if ( ! (-f $filename && -r $filename))
92    {
93        printf STDERR ("can not open idt file %s for reading\n", $filename);
94        $self->{'is_valid'} = 0;
95        return;
96    }
97
98    open my $in, "<", $filename;
99
100    my $columns = Trim(<$in>);
101    $self->{'columns'} = [split(/\t/, $columns)];
102
103    my $column_specs = Trim(<$in>);
104    $self->{'column_specs'} = [split(/\t/, $column_specs)];
105
106    # Table name, index columns.
107    my $line = Trim(<$in>);
108    my @items = split(/\t/, $line);
109    if (scalar @items == 3)
110    {
111        $self->{'codepage'} = shift @items;
112    }
113    my $table_name = shift @items;
114    if ($table_name ne $self->{'name'})
115    {
116        printf STDERR ("reading wrong table data for table '%s' (got %s)\n", $self->{'name'}, $table_name);
117        $self->{'is_valid'} = 0;
118        return;
119    }
120    $self->{'index_columns'} = [@items];
121    $self->{'index_column_index'} = $self->GetColumnIndex($items[0]);
122
123    my $rows = [];
124    while (<$in>)
125    {
126        # Remove all trailing returns and newlines.  Keep trailing spaces and tabs.
127        s/[\r\n]+$//g;
128
129        my @items = split(/\t/, $_);
130        push @$rows, new installer::patch::MsiRow($self, @items);
131    }
132    $self->{'rows'} = $rows;
133
134    return $self;
135}
136
137
138
139=head2 GetColumnCount($self)
140
141    Return the number of columns in the table.
142
143=cut
144sub GetColumnCount ($)
145{
146    my ($self) = @_;
147
148    return scalar @{$self->{'columns'}};
149}
150
151
152
153
154=head2 GetRowCount($self)
155
156    Return the number of rows in the table.
157
158=cut
159sub GetRowCount ($)
160{
161    my ($self) = @_;
162
163    return scalar @{$self->{'rows'}};
164}
165
166
167
168
169=head2 GetColumnIndx($self, $column_name)
170
171    Return the 0 based index of the column named $column_name.  Use
172    this to speed up (slightly) access to column values when accessing
173    many or all rows of a table.
174
175=cut
176sub GetColumnIndex ($$)
177{
178    my ($self, $column_name) = @_;
179
180    my $index = 0;
181    foreach my $name (@{$self->{'columns'}})
182    {
183        if ($name eq $column_name)
184        {
185            return $index;
186        }
187        ++$index;
188    }
189
190    printf STDERR ("did not find column %s in %s\n", $column_name, join(" and ", @{$self->{'columns'}}));
191    return -1;
192}
193
194
195
196
197=head2 GetValue($self, $selector_column, $selector_column_value, $value_column)
198
199    Find the row in which the $selector_column has value
200    $selector_column_value and return its value in the $value_column.
201
202=cut
203
204sub GetValue ($$$$)
205{
206    my ($self, $selector_column, $selector_column_value, $value_column) = @_;
207
208    my $row = $self->GetRow($selector_column, $selector_column_value);
209    if (defined $row)
210    {
211        return $row->GetValue($value_column);
212    }
213    else
214    {
215        return undef;
216    }
217}
218
219
220
221
222=head2 GetRow($self, $column, $value)
223
224    Return the (first) row which has $value in $column.
225
226=cut
227sub GetRow ($$$)
228{
229    my ($self, $column, $value) = @_;
230
231    my $column_index = $self->GetColumnIndex($column);
232    if ($column_index<0)
233    {
234        printf STDERR "ERROR: unknown column $column in table $self->{'name'}\n";
235        return undef;
236    }
237
238    foreach my $row (@{$self->{'rows'}})
239    {
240        if ($row->GetValue($column_index) eq $value)
241        {
242            return $row;
243        }
244    }
245
246    printf STDERR ("ERROR: did not find row for %s->%s in %s\n",
247        $column,
248        $value,
249        table $self->{'name'});
250
251    return undef;
252}
253
254
255
256
257=head2 GetAllRows ($self)
258
259    Return the reference to an array that contains all rows of the table.
260
261=cut
262
263sub GetAllRows ($)
264{
265    my $self = shift;
266
267    return $self->{'rows'};
268}
269
270
271
272
273
2741;
275