blob: 1104d6d2cc6c393f931b11461ebb838f143b07bb [file] [log] [blame]
#**************************************************************
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
#**************************************************************
#*************************************************************************
#
# GenInfoParser - Perl extension for parsing general info databases
#
# usage: see below
#
#*************************************************************************
package GenInfoParser;
use strict;
use Carp;
##### profiling #####
# use Benchmark;
##### ctor #####
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = {};
$self->{'LIST'} = undef;
$self->{'DATA'} = {};
bless ($self, $class);
return $self;
}
##### methods #####
sub load_list
{
# load list into memory
my $self = shift;
my $list_file = shift;
if ( $self->parse_list($list_file) ) {
return 1;
}
return 0;
}
sub get_keys
{
# return a sorted list of keys, the sorting is case insensitive
my $self = shift;
my $access_path = shift;
my ($key, $value, $sub_data_ref) = $self->walk_accesspath($access_path);
my @keys = ();
if ( $sub_data_ref ) {
my @normalized_keys = keys %$sub_data_ref;
foreach my $normalized_key (sort keys %$sub_data_ref) {
push(@keys, $$sub_data_ref{$normalized_key}[0]);
}
} elsif ( $value ) {
chomp $value;
push @keys, ($value);
}
return @keys;
}
sub get_key
{
# returns the key corresponding to the access_path
my $self = shift;
my $access_path = shift;
my ($key, $value, $sub_data_ref) = $self->walk_accesspath($access_path);
return undef if !$key;
return $key;
}
sub get_value
{
# returns the value corresponding to the access_path
my $self = shift;
my $access_path = shift;
my ($key, $value, $sub_data_ref) = $self->walk_accesspath($access_path);
return undef if !$key;
$value = "" if !defined($value);
# trim line ends
$value =~ tr/\r\n//d;
# trim trailing whitespace
$value =~ s/\s+$//;
return $value;
}
##### private methods #####
sub parse_list
{
# parse complete list
my $self = shift;
my $list_file = shift;
my @list_data;
return 0 if ! -r $list_file;
open(FILE, "<$list_file") or croak("can't open $list_file: $!");
# my $t0 = new Benchmark;
$self->parse_block(\*FILE, $self->{'DATA'});
# my $t1 = new Benchmark;
# print STDERR "parsing $list_file took: ", timestr(timediff($t1, $t0)), "\n";
close(FILE);
}
sub parse_block
{
# parse each sub block and place it in a hash
# used data structure:
# $hash{$normalized_key} = [ $key, $value, 0 | $sub_hash_ref ]
my $self = shift;
my $glob_ref = shift;
my $data_ref = shift;
my $current_key = 0;
my $line;
while( $line = <$glob_ref> ) {
# this is the inner loop, any additional pattern matching will
# have a notable affect on runtime behavior
# clean up of $value is done in get_value()
my ($key, $value) = split(' ', $line, 2);
next if !$key; # skip empty lines
my $chr = substr($key, 0, 1);
next if $chr eq '#'; # skip comment lines
last if $chr eq '}'; # return from block;
if ( $chr eq '{' ) {
if ( !$current_key ) {
croak("unexpected block start");
}
else {
# create empty hash and start sub block parse
$$data_ref{$current_key}[2] = {};
$self->parse_block($glob_ref, $$data_ref{$current_key}[2]);
next;
}
}
# sanity check
croak("key $key is not well formed") if $key =~ /\//;
# normalize key for hash lookup
$current_key = lc($key);
# but we have to keep the original - not normalized - key, too
$$data_ref{($current_key)} = [$key, $value, 0];
}
}
sub walk_accesspath
{
# returns the key, value and sub_data_ref which
# corresponds to the access_path
my $self = shift;
my $access_path = shift;
my $sub_data_ref = $self->{'DATA'};
if ( $access_path ) {
my $lookup_ref = 0;
# normalize key
$access_path = lc($access_path);
my @key_sequence = split(/\//, $access_path);
foreach my $key_element (@key_sequence) {
# at least one more key element, but no sub_hash, accesspath invalid
return () if !$sub_data_ref;
$lookup_ref = $$sub_data_ref{$key_element};
# lookup failed, accesspath invalid
return () if !defined($lookup_ref);
# we've got a valid key
$sub_data_ref = $$lookup_ref[2];
}
return ($$lookup_ref[0], $$lookup_ref[1], $sub_data_ref);
}
else {
# empty access path is only vlaid for getting top level key list
return ( undef, undef, $sub_data_ref );
}
}
##### finish #####
1; # needed by use or require
__END__
=head1 NAME
GenInfoParser - Perl extension for parsing general info databases
=head1 SYNOPSIS
# example that will load a general info database called 'stand.lst'
use GenInfoParser;
# Create a new instance of the parser:
$a = GenInfoParser->new();
# Load the database into the parser:
$a->load_list('ssrc633.ini');
# get top level keys from database
@top_level_keys = $a->get_keys();
# get sub list keys
@sub_list_keys = $a->get_keys('src633/Drives/o:/Projects');
# get key/value pair
$key = $a->get_key('src633/Comment/build');
$value = $a->get_value('src633/Comment/build');
=head1 DESCRIPTION
GenInfoParser is a perl extension to load and parse General Info Databses.
It uses a simple object oriented interface to retrieve the information stored
in the database.
Methods:
GenInfoParser::new()
Creates a new instance of the parser. Can't fail.
GenInfoParser::load_list($database)
Loads and parses $database. Returns 1 on success and 0 on failure
GenInfoParser::get_keys($path)
Returns a sorted list of keys from the path $path. Returns an emtpy list if $path
has no sublist. If there is no $path spcified, the method will return the
primary key list. $path can be specified case insensitive. Sorting is done case
insensitive.
GenInfoParser::get_key($path)
Returns the key to $path or 'undef' if an invalid path is given.
Example: $path = 'src633/comment/build' will return 'Build' as key.
Note: $path can be specified case insensitive, but the returned key will
have the exact case as in the database.
GenInfoParser::get_value($path)
Returns the value to $path or 'undef' is invalid path is given.
=head2 EXPORT
GenInfoParser::new()
GenInfoParser::load_list($database)
GenInfoParser::get_keys($path)
GenInfoParser::get_key($path)
GenInfoParser::get_value($path)
=head1 AUTHOR
Jens-Heiner Rechtien, rechtien@sun.com
=head1 SEE ALSO
perl(1).
=cut