The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#=============================== Version.pm ==================================
# Filename:             Version.pm
# Description:          Version number handling class.
# Original Author:      Dale M. Amon
# Revised by:           $Author: amon $ 
# Date:                 $Date: 2008-08-28 23:14:03 $ 
# Version:              $Revision: 1.8 $
# License:		LGPL 2.1, Perl Artistic or BSD
#
#=============================================================================
use strict;

package DMA::Version;
use vars qw{@ISA};
@ISA = qw( UNIVERSAL );

#=============================================================================
#			Class Methods
#=============================================================================

sub new {
  my ($class,$format,$gre,$numfields,@rest) = @_;
  my $self = bless {}, $class;

  $format    || (return undef);
  $gre       || (return undef);
  $numfields || (return undef);

  @$self{'format','gre','numfields'} = ($format,$gre,$numfields);

  # If we have more args, assume we're to immediately init the version string
  ($#rest lt 0) || (return ($self->setlist (@rest)));
  return $self;
}

#------------------------------------------------------------------------------

sub parse {
  my ($class,$lexeme, $gre) = @_;
  my $version;
  $lexeme || return ("","");
  $gre    || ($gre = '\d*\.\d\d');

  ($version,$lexeme) = ($lexeme =~ /(^$gre)?(.*)/);
  return ($version,$lexeme);
}

#=============================================================================
#				Object Methods
#=============================================================================

sub setVersion {
  my ($self,$string) = @_;

  my @list = ($string =~ $self->{'gre'});
  ($#list+1 eq $self->{'numfields'}) || (return undef);

  @$self{'list','version'} = ([@list], $string);
  return $self;
}

#------------------------------------------------------------------------------

sub setlist {
  my ($self,@args) = @_;
  ($#args+1 eq $self->{'numfields'}) || (return undef);

  @$self{'list','version'} = ([@args], sprintf $self->{'format'}, @args);
  return $self;
}

#------------------------------------------------------------------------------

sub version   {return shift->{'version'};}
sub format    {return shift->{'format'};}
sub gre       {return shift->{'gre'};}
sub numfields {return shift->{'numfields'};}
sub list      {return shift->{'list'};}

#=============================================================================
#                       Pod Documentation
#=============================================================================
# You may extract and format the documentation section with the 'perldoc' cmd.

=head1 NAME

 DMA::Version.pm - Version number handling class.

=head1 SYNOPSIS

 use DMA::Version;

 $obj             = DMA::Version->new ($format, $gre, $numfields, @list);
 ($version,$rest) = DMA::Version->parse ($lexeme,$gre);

 $obj             = $obj->setVersion ($string);
 $obj             = $obj->setlist(@list);
 $string          = $obj->version;
 @list            = $obj->list;
 $format          = $obj->format;
 $gre             = $obj->gre;
 $n               = $obj->numfields;

=head1 Inheritance

 UNIVERSAL

=head1 Description

This Class handles parsing, storage  and creation of version strings in any 
format you choose. To handle your desired layout of fields, you must create 
an DMA::Version object with a printf style format code to build one; a 
regular expression to break one into a number of fields; and the number of 
fields the GRE  is expected to create.

At some point we will want more direct use of Linux kernel version 
nomenclature: Version.Patchlevel.Sublevel-Extraversion. Perl and other 
applications also use variants of this. We should subclass to get special 
handling, such as  methods and ivars matching the field nomenclature.

A subclass addition might be methods to increment version elements.

It would really be nice if we could generate a gre and numfields from a 
format string. Probably doable but probably more than an evenings work.

Also would be nice to have some algorithm for comparing version numbers in 
various ways, to be able to pose questions like, Are the Major numbers 
equal? Is the minor number of one gt, eq, lt than another?

=head1 Examples

 use DMA::Version;

 my $foo  = DMA::Version->new ("%d.%02d",'(\d*).(\d\d)',2,2,4);
 my $flg1 = $foo->parse ("3.01");
 my $flg2 = $foo->setlist (2, 3);

 my $ver  = $foo->version;
 my $fmt  = $foo->format;
 my $gre  = $foo->gre;
 my $nf   = $foo->numfields;
 my @ver  = $foo->list;

 my $foo2 = DMA::Version->new ("%d.%02d.%02d",'(\d+).(\d+).(\d+)',3,2,4,20);
 my @ver2 = foo2->list;

=head1 Class Variables

 None.

=head1 Instance Variables

 version          Version string
 list             List of version all components.
 format           Format to build a version string, eg"%d.%02d" 
                  for versions like "2.04".
 gre              General regular expression to break up a string, eg 
                  "(\d+).(\d\d)" for "2.04"
 numfields        Number of fields in the format and gre.

=head1 Class Methods

=over 4

=item B<$obj = DMA::Version-E<gt>new ($format, $gre, $numfields, @list)>

Build a version number object based on $format, $gre and $numfields. If there
are no other arguments, succeed with other ivars undef.

The format is used when generating an output version string from it's parts;
the gre string and numfields are used when breaking down an input string into
a list of items. Note that ' ' delimiters should be used on gre's so that 
backslashes do not get removed during arg passing. In short:

    $format    printf style format string eg, "%d.%02d"
    $gre       gre to split strings into fields (should be single quoted to
               prevent \d's from becoming just d's: '(\d*).(\d\d)'
    $numfields number of fields produced by gre, eg:  2

If @list args are supplied, use the supplied format to build a version string
from them. If successful, return the object. Otherwise return undef to 
indicate failure. 
  
It may be of value to have a wide range of standard version strings for the 
default version parse. It also may be of value for new to handle either a list
of version elements as above, or a single string which is passed to setVersion
as an alternative initializer.

It would be nice to have a way to confirm the gre arg, if present, really is a
valid GRE.

This is the base initializer and should be overridden and chained to by 
subclasses.

=item <($version,$rest) = DMA::Version-E<gt>parse ($lexeme,$gre)>

Parse $lexeme according to the optional version format described by gre. The 
default GRE, if none is specified, is '\d*.\d\d'. Note that the handling of 
the split into match and reset is handled internally so the gre argument need 
only deal with the definition of what is a version number string. Note 
that ' ' delimiters should be used on GRE's so that backslashes do not get
removed during argument passing.

If $lexeme contains a right justified version string, that is returned as 
$version and any remaining chars are placed in $rest. If no version is found,
$version is  and all of $lexeme is return in $rest; if $lexeme is empty or 
undef to start with, both values are .
  
It may be of value to have a wide range of standard version strings for the 
default version parse.

=back 4

=head1 Instance Methods

=over 4

=item B<$format = $obj-E<gt>format>

Return the version format string.

=item B<$gre = $obj-E<gt>gre>

Return the version general regular expression  string.

=item B<@list = $obj-E<gt>list>

Return the list of version elements.

=item B<$n = $obj-E<gt>numfields>

Return the number of fields in the format and gre.

=item B<$obj = $obj-E<gt>setlist(@list)>

Generate the version string from @list according to our format string. Fail 
if the number of elements does not match the number of fields we require.

This is a general class and has no way to check the types of the args
against the format string requirements. So subclass it if that isn't good 
enough for you.

Return self on success,  undef otherwise.

=item B<$obj = $obj-E<gt>setVersion ($string)>

Break up the string into an ordered set of elements as defined by the objects
format string. It will parse $string according to the gre and if successful, 
set the version and list parseable and return self. Return undef if $string 
is not parseable. 

A parse is deemed successful if it the gre places numfields items into list.

=item B<$string = $obj-E<gt>version>

Return the version string.

=back 4

=head1 Private Class Methods

 None.

=head1 Private Instance Methods

 None.

=head1 KNOWN BUGS

 See TODO.

=head1 SEE ALSO

 None.

=head1 AUTHOR

Dale Amon <amon@vnl.com>

=cut

#============================================================================
#                                CVS HISTORY
#============================================================================
# $Log: Version.pm,v $
# Revision 1.8  2008-08-28 23:14:03  amon
# perldoc section regularization.
#
# Revision 1.7  2008-08-15 21:47:52  amon
# Misc documentation and format changes.
#
# Revision 1.6  2008-04-18 14:07:54  amon
# Minor documentation format changes
#
# Revision 1.5  2008-04-11 22:25:23  amon
# Add blank line after cut.
#
# Revision 1.4  2008-04-11 18:56:35  amon
# Fixed quoting problem with formfeeds.
#
# Revision 1.3  2008-04-11 18:39:15  amon
# Implimented new standard for headers and trailers.
#
# Revision 1.2  2008-04-10 15:01:08  amon
# Added license to headers, removed claim that the documentation section still
# relates to the old doc file.
#
# Revision 1.1.1.1  2004-08-31 00:16:50  amon
# Dale's library of primitives in Perl
#
# 20040828	Dale Amon <amon@vnl.com>
#		Changed parse object method to setVersion;
#		created a new parse Class method structured
#		like that in PageId.
#
# 20040813	Dale Amon <amon@vnl.com>
#		Moved to DMA:: from Archivist::
#		to make it easier to enforce layers.
#
# 20021217	Dale Amon <amon@vnl.com>
#		Created.
1;