File Coverage

blib/lib/HP200LX/DB/vpt.pm
Criterion Covered Total %
statement 34 221 15.3
branch 3 96 3.1
condition 0 18 0.0
subroutine 5 16 31.2
pod 0 13 0.0
total 42 364 11.5


line stmt bran cond sub pod time code
1             #!/usr/local/bin/perl
2             # FILE .../CPAN/hp200lx-db/DB/vpt.pm
3             #
4             # View Point Management
5             # + retrieve view point definintions
6             # + retrieve view point tables
7             #
8             # Note:
9             # View Points are managed using two associated entities:
10             # 1. a view point definition, defining properties such as
11             # + column arrangement
12             # + criteria to select data records included in the view point
13             # + sorting criteria
14             # 2. a view point table, containing the actual list of data record
15             # indices in the appropriately sorted sequece and filtered using
16             # the defined SSL criterium.
17             # 3. SSL == Select and Sort List (or so)
18             #
19             # At least one view point (VPT #0) is always present, it does not allow
20             # a SSL criterium and always includes all data. However, sorting criteria
21             # and column arrangement are possible
22             #
23             # included by DB.pm
24             #
25             # exported methods:
26             # $db->find_viewptdef retrieve a view point definition
27             # $db->get_viewptdef_count
28             #
29             # exported functions:
30             # get_viewptdef decode a view point definition
31             # get_viewpttable decode a view point table
32             # find_viewpttable retrieve a view point table
33             # refresh_viewpt actively refresh a view point
34             #
35             # internal functions:
36             # refresh_viewpt_table perform the refreshing of a view point
37             # time_cmp sort function to compare two time vals
38             # sort_viewpt sort a complete view point table
39             # parse_ssl_tok_str analyze the SSL string
40             #
41             # diagnostics and debugging methods:
42             # show_viewptdef print details about a view point
43             #
44             # T2D:
45             # + re-calculate a view point table
46             # DONE: SSL parser and evaluater are present but not complete
47             # MISSING: sorting all the fields
48             # + converter for SSL string to SSL tokens (and vica versa?)
49             # This can be used to edit the SSL string in an application
50             # + currently, there is no difference between a view point which
51             # needs to be rebuilt and a view point with no data records.
52             # In both cases, the view point table is empty.
53             # DONE: view points are re-calculated even if no data is there.
54             #
55             # written: 1998-06-01
56             # latest update: 2001-03-03 20:54:08
57             # $Id: vpt.pm,v 1.4 2001/03/05 02:04:20 gonter Exp $
58             #
59              
60             package HP200LX::DB::vpt;
61              
62 1     1   5 use strict;
  1         2  
  1         40  
63 1     1   5 use vars qw($VERSION @ISA @EXPORT);
  1         1  
  1         65  
64 1     1   5 use Exporter;
  1         44  
  1         2614  
65              
66             $VERSION= '0.09';
67             @ISA= qw(Exporter);
68             @EXPORT= qw(get_viewptdef find_viewptdef get_viewptdef_count
69             get_viewpttable find_viewpttable
70             refresh_viewpt
71             );
72              
73             my $delim= '-' x 74; # optic delimiter
74             my $no_val= 65535; # NIL, empty list, -1 etc.
75             my $MAX_SORT_FIELDS= 3; # HP-200LX limitation
76              
77             # ----------------------------------------------------------------------------
78             sub get_viewptdef
79             {
80 1     1 0 10 my $def= shift;
81              
82             # print "\n", $delim, "\n", ">>>> viewptdef\n"; &HP200LX::DB::hex_dump ($def);
83              
84 1         4 my ($tok_lng, $str_lng, $flg)= unpack ('vvv', $def);
85             # a view point name may have up to 32 characters but the first NULL
86             # character indicates the end too. The rest contains garabge!
87             # my $name= &HP200LX::DB::upto_EOS (substr ($def, 7, 32));
88 1         3 my $name= substr ($def, 7, 32);
89 1         9 $name=~ s/\0.*$//s; # ignore new lines!
90              
91 1         3 $def= substr ($def, 39);
92             # print "name='$name'\n";
93              
94             # extract sorting information
95 1         3 my ($s1, $s2, $s3, $a1, $a2, $a3)= unpack ('vvvvvv', $def);
96 1         283 my $sort=
97             [ { 'idx' => $s1, 'asc' => $a1 },
98             { 'idx' => $s2, 'asc' => $a2 },
99             { 'idx' => $s3, 'asc' => $a3 },
100             ];
101              
102             # extract column arangements
103 1         3 my (@cols, $i);
104 1         3 $def= substr ($def, 12);
105             # &HP200LX::DB::hex_dump ($def);
106 1         6 for ($i= 0; $i < 20; $i++)
107             {
108 4         12 my ($num, $width)= unpack ('cc', substr ($def, $i*2, 2));
109 4 100       10 last if ($num == -1);
110 3         15 push (@cols, { num => $num, width => $width });
111             }
112              
113             # T2D: $def= SSL String; decode SSL tokens+strings
114 1         3 $def= substr ($def, 40);
115              
116 1         18 my $vptd=
117             {
118             'name' => $name,
119             'index' => 0, # filled in by calling module
120             'flags' => $flg,
121             'tok_lng' => $tok_lng,
122             'str_lng' => $str_lng,
123             'tok_str' => substr ($def, 0, $tok_lng),
124             'str_str' => substr ($def, $tok_lng, $str_lng),
125             'sort' => $sort,
126             'cols' => \@cols,
127             };
128              
129             # &show_viewptdef ($vptd, *STDOUT);
130 1         5 bless ($vptd);
131             }
132              
133             # ----------------------------------------------------------------------------
134             sub get_viewptdef_count
135             {
136 0     0 0 0 my $db= shift;
137 0         0 my $vptdl= $db->{viewptdef}; # view point definition list
138              
139 0         0 $#$vptdl;
140             }
141              
142             # ----------------------------------------------------------------------------
143             sub find_viewptdef
144             {
145 0     0 0 0 my $db= shift;
146 0         0 my $view= shift; # name or number of the view
147              
148 0         0 my $vptdl= $db->{viewptdef}; # view point definition list
149              
150 0 0       0 if ($view =~ /^\d+$/)
151             {
152 0 0 0     0 return ($view >= 0 && $view <= $#$vptdl) ? $vptdl->[$view] : undef;
153             }
154              
155             # T2D: this should be part of a function to retrieve
156             # the view point number of a named view point!!!
157 0         0 my ($v, $vptd);
158 0         0 foreach $v (@$vptdl)
159             {
160             # print "match: name=$v->{name} view=$view\n";
161 0 0       0 if ($v->{name} eq $view) { print "found! v=$v\n"; $vptd= $v; last; }
  0         0  
  0         0  
  0         0  
162             }
163 0         0 print "vptd=$vptd\n";
164 0         0 $vptd;
165             }
166              
167             # ----------------------------------------------------------------------------
168             sub show_viewptdef
169             {
170 0     0 0 0 my $vptd= shift;
171 0         0 local *FX= shift;
172 0         0 my ($i, $ci);
173              
174 0 0       0 unless (defined ($vptd))
175             {
176 0         0 print FX "viewpoint not defined!\n";
177 0         0 return;
178             }
179              
180 0         0 print FX $delim, "\nViewpoint '", $vptd->{name},
181             "' flags=", $vptd->{flags},
182             " tok_lng=", $vptd->{tok_lng},
183             " str_lng=", $vptd->{str_lng}, "\n";
184 0         0 my $s= $vptd->{'sort'};
185 0         0 my $c= $vptd->{cols};
186 0         0 for ($i= 0; $i < 3; $i++)
187             {
188 0         0 printf FX ("sort field: %3d %d\n", $s->[$i]->{idx}, $s->[$i]->{asc});
189             }
190              
191 0         0 foreach $ci (@$c)
192             {
193 0         0 printf FX ("column field: %3d width=%2d\n", $ci->{num}, $ci->{width});
194             }
195              
196 0         0 my $tok_str= $vptd->{tok_str};
197 0         0 print FX "SSL tokens: lng=", length ($tok_str), "\n";
198 0         0 &HP200LX::DB::hex_dump ($tok_str, *FX);
199              
200 0         0 my $str_str= $vptd->{str_str};
201 0         0 print FX "SSL string: lng=", length ($str_str), "\n";
202 0         0 &HP200LX::DB::hex_dump ($str_str, *FX);
203              
204 0         0 print FX $delim, "\n\n";
205             }
206              
207             # ----------------------------------------------------------------------------
208             sub get_viewpttable
209             {
210 1     1 0 2 my $def= shift;
211 1         2 my ($l, $v);
212 1         36 my @vptt= ();
213 1         3 my $lng= length ($def);
214              
215             # print "\n", $delim, "\n", ">>>> viewpttable\n"; &HP200LX::DB::hex_dump ($def);
216 1         5 for ($l= 0; $l < $lng; $l += 2)
217             {
218 1         4 ($v)= unpack ('v', substr ($def, $l, 2));
219 1 50       3 last if ($v == $no_val);
220 1         4 push (@vptt, $v);
221             }
222              
223 1         4 \@vptt;
224             }
225              
226             # ----------------------------------------------------------------------------
227             sub pack_viewpt_table
228             {
229 0     0 0   my $tbl= shift;
230 0           my $t;
231 0           my $def= ''; # must be initialized!
232 0           foreach $t (@$tbl)
233             {
234 0           $def .= pack ('v', $t);
235             }
236             # $def= pack ('v', $no_val) unless ($def); # dummy entry if empty
237             # NOTE: adding $no_val results in too many entries
238 0           $def;
239             }
240              
241             # ----------------------------------------------------------------------------
242             sub find_viewpttable
243             {
244 0     0 0   my $db= shift;
245 0           my $view= shift; # number of the view
246              
247 0           my $vpttl= $db->{viewpttable}; # view point table list
248              
249             # print "find_viewpttable 1 view=$view\n";
250 0 0 0       return undef unless ($view >= 0 && $view <= $#$vpttl);
251             # print "find_viewpttable 2 view=$view\n";
252 0           my $vptt= $vpttl->[$view];
253              
254 0 0         $vptt= $db->refresh_viewpt ($view) if ($#$vptt < 0);
255             # &HP200LX::DB::hex_dump ($vptt);
256 0           $vptt;
257             }
258              
259             # ----------------------------------------------------------------------------
260             sub refresh_viewpt
261             {
262 0     0 0   my $db= shift;
263 0           my $view= shift; # number of the view
264 0 0         $view= -1 unless (defined ($view));
265              
266 0           my $vpttl= $db->{viewpttable}; # view point table list
267 0           my $vptdl= $db->{viewptdef}; # view point definition list
268 0           my ($vptd, $vptt, $view_start, $view_end);
269 0           my $T10= $db->{Types}->[10];
270              
271 0 0         if (($view_start= $view_end= $view) == -1)
272             {
273 0           $view_start= 0;
274 0           $view_end= $#$vptdl;
275             }
276             # print "refresh: view=$view start=$view_start end=$view_end\n";
277              
278 0           for ($view= $view_start; $view <= $view_end; $view++)
279             {
280 0           $vptd= $vptdl->[$view];
281             # &show_viewptdef ($vptd, *STDOUT);
282 0           $vptt= $vpttl->[$view]= &refresh_viewpt_table ($db, $vptd);
283 0           print "refreshed vptt[$view]: ", $#$vptt+1, " entries\n";
284 0           $T10->[$view]->{data}= &pack_viewpt_table ($vptt);
285             }
286              
287 0           $vptt;
288             }
289              
290             # ----------------------------------------------------------------------------
291             # This method refreshes one particular view point table.
292             # A view point depends on a filter definition (called SSL in HP-LX lingo)
293             # which selects those entries that are used in a view point.
294             # Those entries that match are then sorted using up to three (HP-LX limitation)
295             # sort fields; I call this the chain of search fields. This chain may
296             # have no entries at all, in this case, the records are presented
297             # in the order they appear in the GDB field itself.
298             sub refresh_viewpt_table
299             {
300 0     0 0   my $db= shift;
301 0           my $vptd= shift;
302 0           my $vptt= [];
303              
304 0           my @SSL= &parse_ssl_tok_str ($vptd->{tok_str});
305 0           my $ssls= $vptd->{str_str};
306 0           my $fd= $db->{fielddef};
307 0           my $sort= {}; # sort definition tree
308 0           my @SORT; # names of fields used for the sort
309              
310 0           my ($i, $j, $x, $y, $z, $op, $match);
311             # print ">>>> vptd keys: ", join (', ', keys %$vptd), "\n";
312              
313             # prepare chain of sort fields
314 0           my $rec= $sort= $vptd->{'sort'};
315             # print ">>>> vptd sorting: sort='$sort' ", join (',', @$sort);
316 0           for ($i= 0; $i < $MAX_SORT_FIELDS; $i++)
317             {
318 0           $y= $rec->[$i];
319 0           $x= $fd->[$y->{idx}];
320 0 0         last if ($y->{idx} == $no_val);
321 0           push (@SORT, $y->{name}= $x->{name});
322              
323             # get the sort mode handy:
324             # 0= ascending string, 1= descending string,
325             # 2= ascending number, 3= descending number
326             # 4= ascending time, 5= descending time
327             # T2D: sorting date and other fields, time seems to work...
328              
329 0           my $ft= $x->{ftype};
330 0 0         if ($ft == 4) { $z= 1; } # number
  0 0          
331 0           elsif ($ft == 7) { $z= 2; } # time
332 0           else { $z= 0; }
333              
334 0 0         $z= $z*2+ (($y->{asc}) ? 0 : 1);
335 0           $y->{smode}= $z;
336             # print "sort mode: x=$x name=$x->{name} ft=$ft z=$z\n";
337             }
338              
339 0 0         my $T= ($#SORT == -1) ? [] : {}; # sorted records by sort fields
340             # SPECIAL CASE: no sort fields means that fields are sorted by
341             # the order they occur in the GDB file!
342             # We use an array reference for this case, otherwise the
343             # array reference is at the end of the chain of sort-field names.
344              
345 0           my $cnt= $db->get_last_index (); # total number of records
346             # print "refreshing view point; ssl_str=$ssls num(SSL)=$#SSL dbcnt=$cnt\n";
347 0           for ($i= 0; $i <= $cnt; $i++)
348             {
349 0           $rec= $db->FETCH ($i);
350             # print "rec: ", join (':', keys %$rec), "\n";
351              
352 0 0         if ($#SSL < 0)
353             {
354 0           $match= 1; # no SSL string thus use everything!
355             }
356             else
357             { # SSL was defined
358 0           $match= 0;
359             # this is the SSL match engine, it works like a mini FORTH interpreter
360 0           my @ST= (); # Forth Stack
361 0           my $SSL;
362 0           foreach $SSL (@SSL)
363             {
364 0           $op= $SSL->{op};
365              
366 0 0         if ($op == 0x0012)
367             { # convert field index to name
368 0           $x= $fd->[$SSL->{idx}]->{name};
369 0           $SSL->{name}= $x;
370 0           $op= $SSL->{op}= 0x0112;
371             }
372              
373 0 0         if ($op == 0x0001) { push (@ST, !pop (@ST)); }
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
374 0   0       elsif ($op == 0x0002) { push (@ST, pop (@ST) || pop (@ST)); }
375 0   0       elsif ($op == 0x0003) { push (@ST, pop (@ST) && pop (@ST)); }
376 0           elsif ($op == 0x0004) { push (@ST, pop (@ST) == pop (@ST)); }
377 0           elsif ($op == 0x0009) { push (@ST, pop (@ST) != pop (@ST)); }
378             elsif ($op == 0x000B)
379             {
380 0           $x= pop (@ST);
381 0           $y= pop (@ST);
382 0           $z= ($y =~ /$x/);
383             # print "contains: $x in $y -> $z\n";
384 0           push (@ST, $z);
385             }
386 0           elsif ($op == 0x0011) { push (@ST, $SSL->{str}); }
387 0           elsif ($op == 0x0112) { push (@ST, $rec->{$SSL->{name}}); }
388             elsif ($op == 0x0018)
389             {
390 0           $z= pop (@ST);
391 0 0         $match= 1 if ($z);
392             # print "MATCH: $match\n";
393             }
394             else
395             {
396 0           print "unimplemented SSL op=$op\n";
397             }
398             }
399             }
400              
401 0 0         if ($match)
402             { # sorting: build up a sort tree
403             # search the array reference holding the record indices
404             # the tree looks something like this:
405             # $T->{$rec->{$SORT[0]}}->...->{$rec->{$SORT[n]}}= [ rec indices ]
406             # The sort tree may be 1, 2, or 3 levels deep.
407 0           $x= $T;
408 0           $j= 0;
409 0           for ($j= 0; $j <= $#SORT; $j++)
410             {
411 0           $y= $rec->{$SORT[$j]};
412 0 0         if (defined ($z= $x->{$y})) { $x= $z; }
  0            
413 0 0         else { $x= $x->{$y}= ($j == $#SORT) ? [] : {}; }
414             }
415 0           push (@$x, $i);
416             }
417             }
418              
419 0           my @sort= @$sort;
420 0           &sort_viewpt ($vptt, $T, @sort);
421              
422 0           $vptt;
423             }
424              
425             # ----------------------------------------------------------------------------
426             # compare two time strings
427             sub time_cmp
428             {
429             # my ($a, $b)= @_;
430 0     0 0   my $la= length ($a);
431 0           my $lb= length ($b);
432              
433             # print "a=$a b=$b la=$la lb=$lb\n";
434 0 0         if ($la == $lb) { return ($a cmp $b); }
  0 0          
435 0           elsif ($la < $lb) { return -1; }
436 0           else { return 1; }
437             }
438              
439             # ----------------------------------------------------------------------------
440             # the HP-LX compares strings in lower case
441             sub cmpc
442             {
443 0     0 0   my ($la, $lb)= ($a, $b);
444 0           $la=~ tr/A-Z/a-z/;
445 0           $lb=~ tr/A-Z/a-z/;
446 0 0         if ($la eq $lb) { return ($a cmp $b); }
  0 0          
447 0           elsif ($la lt $lb) { return -1; }
448 0           else { return 1; }
449             }
450              
451             # ----------------------------------------------------------------------------
452             sub sort_viewpt
453             {
454 0     0 0   my ($vptt, $T, @sort)= @_;
455 0           my (@keys, $key);
456              
457 0 0         if (ref ($T) eq 'ARRAY')
    0          
458             { # final leaf in the sort tree reached, push the array up...
459 0           push (@$vptt, @$T);
460             }
461             elsif (ref ($T) eq 'HASH')
462             {
463 0           my $s= shift (@sort);
464 0           my $sm= $s->{smode};
465              
466 0 0         if ($sm == 0) { @keys= sort cmpc keys %$T; }
  0 0          
    0          
    0          
    0          
    0          
467 0           elsif ($sm == 1) { @keys= reverse sort cmpc keys %$T; }
468 0           elsif ($sm == 2) { @keys= sort {$a <=> $b} keys %$T; }
  0            
469 0           elsif ($sm == 3) { @keys= sort {$b <=> $a} keys %$T; }
  0            
470 0           elsif ($sm == 4) { @keys= sort time_cmp keys %$T; }
471 0           elsif ($sm == 5) { @keys= reverse sort time_cmp keys %$T; }
472              
473 0           foreach $key (@keys)
474             {
475 0           &sort_viewpt ($vptt, $T->{$key}, @sort);
476             }
477             }
478             }
479              
480             # ----------------------------------------------------------------------------
481             sub parse_ssl_tok_str
482             {
483 0     0 0   my $str= shift;
484              
485             # print ">>> parse_ssl_tok_str str='$str'\n"; HP200LX::DB::hex_dump ($str);
486 0 0         return () unless ($str);
487              
488 0           my @res;
489 0           my $i= 0;
490 0           my ($ci, $nv);
491 0           while (1)
492             {
493 0           $ci= unpack ('C', substr ($str, $i, 1));
494              
495 0 0 0       if ($ci >= 0x01 && $ci <= 0x0B) # string contains
    0 0        
    0          
    0          
496             {
497 0           $i++;
498 0           push (@res, { op => $ci });
499             }
500             elsif ($ci == 0x11) # String token
501             {
502 0           $i++;
503 0           $nv= '';
504 0           while (1)
505             {
506 0           $ci= substr ($str, $i++, 1);
507 0 0         last if ($ci eq "\x00");
508 0           $nv .= $ci;
509             }
510 0           print "str: $nv\n";
511 0           push (@res, { op => 0x11, str => $nv });
512             }
513             elsif ($ci == 0x12 || $ci == 0x13) # name or boolean token (field index token)
514             {
515 0           $nv= unpack ('v', substr ($str, $i+1, 2));
516 0           $i += 3;
517 0           print "field index: $nv\n";
518 0           push (@res, { op => 0x12, idx => $nv });
519             }
520             elsif ($ci == 0x18) # last token
521             {
522 0           push (@res, { op => 0x18 });
523 0           last;
524             }
525             else
526             {
527 0           printf (">>> unknown SSL token [%d] 0x%02X\n", $i, $ci);
528 0           $i++;
529             }
530             }
531              
532 0           print "done parsing\n";
533              
534 0           @res;
535             }
536              
537             # ----------------------------------------------------------------------------
538             1;