/usr/local/CPAN/Class-HPLOO/Class/HPLOO/Base.pm
#############################################################################
## Name: Base.pm
## Purpose: Base class for HPLOO classes.
## Author: Graciliano M. P.
## Modified by:
## Created: 30/10/2004
## RCS-ID:
## Copyright: (c) 2004 Graciliano M. P.
## Licence: This program is free software; you can redistribute it and/or
## modify it under the same terms as Perl itself
#############################################################################
package Class::HPLOO::Base ;
use 5.006 ;
use strict qw(vars) ;
use vars qw($VERSION $SYNTAX @ISA) ;
$VERSION = '0.17';
############
# EXPORTER #
############
require Exporter;
@ISA = qw(Exporter UNIVERSAL) ;
our @EXPORT = qw(SUPER new GET_CLASS_HPLOO_HASH CLASS_HPLOO_TIE_KEYS ATTRS CLASS_HPLOO_ATTR CLASS_HPLOO_ATTR_TYPE) ;
our @EXPORT_OK = @EXPORT ;
########################
# GET_CLASS_HPLOO_HASH #
########################
sub GET_CLASS_HPLOO_HASH {
my $pack = ref($_[0]) || ($_[1] ? $_[1] : $_[0]) ;
return \%{$pack . '::CLASS_HPLOO'} ;
}
#########
# SUPER #
#########
sub SUPER {
my ($prev_pack , undef , undef , $sub0) = caller(1) ;
$prev_pack = undef if $prev_pack eq 'Class::HPLOO::Base' ;
my ($pack,$sub) = ( $sub0 =~ /^(?:(.*?)::|)(\w+)$/ );
my $sub_is_new_hploo = $sub0 =~ /^(.*?(?:::)?$sub)\::$sub$/ ? 1 : undef ;
##print "SUPER[$sub_is_new_hploo]>> @_ >> $pack,$sub >> $prev_pack\n" ;
unshift(@_ , $prev_pack) if ( $sub_is_new_hploo && $prev_pack && ((!ref($_[0]) && $_[0] ne $prev_pack && !UNIVERSAL::isa($_[0] , $prev_pack)) || (ref($_[0]) && !UNIVERSAL::isa($_[0] , $prev_pack)) ) ) ;
if ( defined @{"$pack\::ISA"} ) {
my $isa_sub = ISA_FIND_NEW($pack, ($sub_is_new_hploo?'new':$sub) ,1) ;
my ($sub_name) = ( $isa_sub =~ /(\w+)$/gi );
if ( $sub0 ne $isa_sub && !ref($_[0]) && $isa_sub =~ /^(.*?(?:::)?$sub_name)\::$sub_name$/ ) {
@_ = ( bless({},$_[0]) , @_[1..$#_] ) ;
}
if ( $sub0 eq $isa_sub && UNIVERSAL::isa($_[0] , $pack) ) {
my @isa = Class::HPLOO::Base::FIND_SUPER_WALK( ref($_[0]) , $pack ) ;
my $pk = $isa[-1] ;
if ( $sub_is_new_hploo ) {
if ( UNIVERSAL::isa($pk , 'Class::HPLOO::Base') ) {
($sub) = ( $pk =~ /(\w+)$/gi );
}
else { $sub = 'new' ;}
}
my $isa_sub = $pk->can($sub) ;
##print "SUPER WALK>> $pk , $sub >> $isa_sub\n" ;
return &$isa_sub( ARGS_WRAPPER(@_) ) if $isa_sub ;
}
##print "SUPER ISA>> $isa_sub >> $pack >> ". join(',',@{"$pack\::ISA"}) ."\n" ;
return &$isa_sub(@_) if $isa_sub && defined &$isa_sub && $sub0 ne $isa_sub ;
}
$sub = $sub_is_new_hploo ? 'new' : $sub ;
##print "SUPER CALL>> $pack $sub >> @_\n" ;
die("Can't find SUPER method for $sub0!") if "$pack\::$sub" eq $sub0 ;
return &{"$pack\::$sub"}(@_) ;
}
###################
# FIND_SUPER_WALK #
###################
sub FIND_SUPER_WALK {
my $class_main = shift ;
my $class_end = shift ;
my $only_stak = shift ;
my (@stack) ;
my $stack = $only_stak || {} ;
##print "FIND>> $class_main , $class_end\n" ;
my $found ;
foreach my $isa_i ( @{"$class_main\::ISA"} ) {
next if $$stack{$isa_i}++ ;
$found = 1 if $isa_i eq $class_end ;
push(@stack , $isa_i , FIND_SUPER_WALK($isa_i , $class_end , $stack) );
}
return ($found ? @stack : ()) if $only_stak ;
return @stack ;
}
################
# ISA_FIND_NEW #
################
sub ISA_FIND_NEW {
my $pack = shift ;
my $sub = shift ;
my $look_deep = shift ;
my $count = shift ;
return if $count > 100 ;
##print "ISA_FIND_NEW>> $pack >> $sub\n" ;
my ($sub_name) ;
if ( UNIVERSAL::isa($pack , 'Class::HPLOO::Base') ) {
($sub_name) = $sub eq 'new' ? ( $pack =~ /(\w+)$/ ) : ($sub) ;
}
else { $sub_name = $sub ;}
my $isa_sub = "$pack\::$sub_name" ;
if ( $look_deep || !defined &$isa_sub ) {
foreach my $isa_i ( @{"$pack\::ISA"} ) {
next if $isa_i eq $pack || $isa_i eq 'Class::HPLOO::Base' ;
last if $isa_i eq 'UNIVERSAL' ;
$isa_sub = ISA_FIND_NEW($isa_i , $sub , 0 , $count+1) ;
last if $isa_sub ;
}
}
$isa_sub = undef if !defined &$isa_sub ;
##print "%%> $pack >> $isa_sub\n" ;
return $isa_sub ;
}
##################
# NEW_CALL_BEGIN #
##################
sub new_call_BEGIN {
my $class = shift ; $class = ref($class) if ref($class) ;
my $this = $class ;
my @isas = \@{"$class\::ISA"} ;
foreach my $isas_i ( @isas ) {
foreach my $ISA_i ( @$isas_i ) {
if ( defined @{"$ISA_i\::ISA"} && @{"$ISA_i\::ISA"} > 2 ) {
push(@isas , \@{"$ISA_i\::ISA"}) ;
}
last if $ISA_i eq 'Class::HPLOO::Base' ;
my $ret ;
my ($sub) = ( $ISA_i =~ /(\w+)$/ );
$sub = "$ISA_i\::$sub\_BEGIN" ;
$ret = &$sub($this,@_) if defined &$sub ;
$this = $ret if $ret && UNIVERSAL::isa($ret,$class) ;
}
}
return $this ;
}
################
# NEW_CALL_END #
################
sub new_call_END {
my $class = shift ; $class = ref($class) if ref($class) ;
my @isas = \@{"$class\::ISA"} ;
foreach my $isas_i ( @isas ) {
foreach my $ISA_i ( @$isas_i ) {
if ( defined @{"$ISA_i\::ISA"} && @{"$ISA_i\::ISA"} > 2 ) {
push(@isas , \@{"$ISA_i\::ISA"}) ;
}
last if $ISA_i eq 'Class::HPLOO::Base' ;
my ($sub) = ( $ISA_i =~ /(\w+)$/ );
$sub = "$ISA_i\::$sub\_END" ;
&$sub(@_) if defined &$sub ;
}
}
return ;
}
#######
# NEW #
#######
my $NEW_REF = \&new ;
sub new {
my $class = shift ;
my $this = ref($class) ? $class : undef ;
my $class_bless = $class ;
($class,$class_bless,$this) = @$class if ref($class) eq 'ARRAY' ;
$class = ref($class) if ref($class) ;
$class_bless = ref($class_bless) if ref($class_bless) ;
my ($class_end) = ( $class =~ /(\w+)$/ );
##print "BASE NEW>> $class,$class_bless,$this >> $class_end >> @_ >> [". join(" ", @{"$class\::ISA"}) ."]\n" ;
if ( !defined &{"$class\::$class_end"} && @{"$class\::ISA"} > 1 ) {
foreach my $ISA_i ( @{"$class\::ISA"} ) {
last if $ISA_i eq 'Class::HPLOO::Base' ;
my $sub = "$ISA_i\::new" ;
if ( defined &$sub ) {
my $new_ref = \&$sub ;
return &$sub([$ISA_i,$class,$this],@_) if $new_ref == $NEW_REF || (defined &{"$ISA_i\::__CLASS__"} && defined &{"$ISA_i\::SUPER"} && defined &{"$ISA_i\::new_call_END"} ) ;
return &$sub($class,@_) ;
}
}
}
$this ||= $class ;
$this = new_call_BEGIN( $this , @_) ;
$this = bless({} , $class_bless) if !ref($this) || !UNIVERSAL::isa($this,$class_bless) ;
no warnings ;
my $undef = \'' ;
sub UNDEF {$undef} ;
my $CLASS_HPLOO = GET_CLASS_HPLOO_HASH($this) ;
if ( $$CLASS_HPLOO{ATTR} ) { CLASS_HPLOO_TIE_KEYS($this) }
my $ret_this = defined &{"$class\::$class_end"} ? $this->$class_end(@_) : undef ;
if ( ref($ret_this) && UNIVERSAL::isa($ret_this,$class_bless) ) {
$this = $ret_this ;
if ( $$CLASS_HPLOO{ATTR} && UNIVERSAL::isa($this,'HASH') ) { CLASS_HPLOO_TIE_KEYS($this) }
}
elsif ( $ret_this == $undef ) { $this = undef }
new_call_END($class,$this,@_) ;
return $this ;
}
########################
# CLASS_HPLOO_TIE_KEYS #
########################
sub CLASS_HPLOO_TIE_KEYS {
my $this = shift ;
my $CLASS_HPLOO = GET_CLASS_HPLOO_HASH($this) ;
if ( $$CLASS_HPLOO{ATTR} ) {
foreach my $Key ( keys %{$$CLASS_HPLOO{ATTR}} ) {
tie( $this->{$Key} => 'Class::HPLOO::Base::HPLOO_TIESCALAR' , $this , $Key , $$CLASS_HPLOO{ATTR}{$Key}{tp} , $$CLASS_HPLOO{ATTR}{$Key}{pr} , \$this->{CLASS_HPLOO_ATTR}{$Key} , \$this->{CLASS_HPLOO_CHANGED} , ref($this) ) if !exists $this->{$Key} ;
}
}
}
#########
# ATTRS #
#########
sub ATTRS { return @{[@{ GET_CLASS_HPLOO_HASH($_[0] , scalar caller)->{ATTR_ORDER} }]} } ;
####################
# CLASS_HPLOO_ATTR #
####################
sub CLASS_HPLOO_ATTR {
my $class = caller ;
my @attrs = split(/\s*,\s*/ , $_[0]) ;
my $CLASS_HPLOO = GET_CLASS_HPLOO_HASH( undef , $class ) ;
foreach my $attrs_i ( @attrs ) {
$attrs_i =~ s/^\s+//s ;
$attrs_i =~ s/\s+$//s ;
my ($name) = ( $attrs_i =~ /(\w+)$/gi ) ;
my ($type) = ( $attrs_i =~ /^((?:\w+\s+)*?&?\w+|(?:\w+\s+)*?\w+(?:(?:::|\.)\w+)*)\s+\w+$/gi ) ;
my $type0 = $type ;
$type0 =~ s/\s+/ /gs ;
$type = lc($type) ;
$type =~ s/(?:^|\s*)bool$/boolean/gs ;
$type =~ s/(?:^|\s*)int$/integer/gs ;
$type =~ s/(?:^|\s*)float$/floating/gs ;
$type =~ s/(?:^|\s*)str$/string/gs ;
$type =~ s/(?:^|\s*)sub$/sub_$name/gs ;
$type =~ s/\s//gs ;
$type = 'any' if $type !~ /^(?:(?:ref)|(?:ref)?(?:array|hash)(?:boolean|integer|floating|string|sub_\w+|any|&\w+)|(?:ref)?(?:array|hash)|(?:array|hash)?(?:boolean|integer|floating|string|sub_\w+|any|&\w+))$/ ;
if ( $type eq 'any' && $type0 =~ /^((?:ref\s*)?(?:array|hash) )?(\w+(?:(?:::|\.)\w+)*)$/ ) {
my ($tp1 , $tp2) = ($1 , $2) ;
$tp1 =~ s/\s+//gs ;
$tp2 = 'UNIVERSAL' if $tp2 =~ /^(?:obj|object)$/i ;
$tp2 =~ s/\.+/::/gs ;
$type = "$tp1$tp2" ;
}
my $parse_ref = $type =~ /^(?:array|hash)/ ? 1 : 0 ;
push(@{ $$CLASS_HPLOO{ATTR_ORDER} } , $name) if !$$CLASS_HPLOO{ATTR}{$name} ;
$$CLASS_HPLOO{ATTR}{$name}{tp} = $type ;
$$CLASS_HPLOO{ATTR}{$name}{pr} = $parse_ref ;
my $return ;
if ( $type =~ /^sub_(\w+)$/ ) {
my $sub = $1 ;
$return = qq~
return (&$sub(\$this,\@_))[0] if defined &$sub ;
return undef ;
~ ;
}
else {
$return = $parse_ref ? qq~
ref(\$this->{CLASS_HPLOO_ATTR}{$name}) eq 'ARRAY' ? \@{\$this->{CLASS_HPLOO_ATTR}{$name}} :
ref(\$this->{CLASS_HPLOO_ATTR}{$name}) eq 'HASH' ? \%{\$this->{CLASS_HPLOO_ATTR}{$name}} :
\$this->{CLASS_HPLOO_ATTR}{$name}
~ :
"\$this->{CLASS_HPLOO_ATTR}{$name}" ;
}
eval(qq~
package $class ;
sub set_$name {
my \$this = shift ;
if ( !defined \$this->{$name} ) {
tie( \$this->{$name} => 'Class::HPLOO::Base::HPLOO_TIESCALAR' , \$this , '$name' , '$type' , $parse_ref , \\\$this->{CLASS_HPLOO_ATTR}{$name} , \\\$this->{CLASS_HPLOO_CHANGED} , ref(\$this) ) ;
}
\$this->{CLASS_HPLOO_CHANGED}{$name} = 1 ;
\$this->{CLASS_HPLOO_ATTR}{$name} = CLASS_HPLOO_ATTR_TYPE( ref(\$this) , '$type',\@_) ;
}
~) if !defined &{"$class\::set_$name"} ;
eval(qq~
package $class ;
sub get_$name {
my \$this = shift ;
$return ;
}
~) if !defined &{"$class\::get_$name"} ;
}
}
#########################
# CLASS_HPLOO_ATTR_TYPE #
#########################
sub CLASS_HPLOO_ATTR_TYPE {
my $class = shift ;
my $type = shift ;
if ($type eq 'any') { return $_[0] }
elsif ($type eq 'string') {
return "$_[0]" ;
}
elsif ($type eq 'boolean') {
return if $_[0] =~ /^(?:false|null|undef)$/i ;
return 1 if $_[0] ;
return ;
}
elsif ($type eq 'integer') {
my $val = $_[0] ;
my ($sig) = ( $val =~ /^(-)/ );
$val =~ s/[^0-9]//gs ;
$val = "$sig$val" ;
return $val ;
}
elsif ($type eq 'floating') {
my $val = $_[0] ;
$val =~ s/[\s_]+//gs ;
if ( $val !~ /^\d+\.\d+$/ ) {
($val) = ( $val =~ /(\d+)/ ) ;
$val .= '.0' ;
}
return $val ;
}
elsif ($type =~ /^sub_(\w+)$/) {
my $sub = $1 ;
return (&$sub(@_))[0] if defined &$sub ;
}
elsif ($type =~ /^&(\w+)$/) {
my $sub = $1 ;
return (&$sub(@_))[0] if defined &$sub ;
}
elsif ($type eq 'ref') {
my $val = $_[0] ;
return $val if ref($val) ;
}
elsif ($type eq 'array') {
my @val = @_ ;
return \@val ;
}
elsif ($type eq 'hash') {
my %val = @_ ;
return \%val ;
}
elsif ($type eq 'refarray') {
my $val = $_[0] ;
return $val if ref($val) eq 'ARRAY' ;
}
elsif ($type eq 'refhash') {
my $val = $_[0] ;
return $val if ref($val) eq 'HASH' ;
}
elsif ($type =~ /^array(&?[\w:]+)/ ) {
my $tp = $1 ;
my @val = @_ ;
my $accept_undef = $tp =~ /^(?:any|string|boolean|integer|floating|sub_\w+|&\w+)$/ ? 1 : undef ;
if ( $accept_undef ) {
return [map { CLASS_HPLOO_ATTR_TYPE($class , $tp , $_) } @val] ;
}
else {
return [map { CLASS_HPLOO_ATTR_TYPE($class , $tp , $_) || () } @val] ;
}
}
elsif ($type =~ /^hash(&?[\w:]+)/ ) {
my $tp = $1 ;
my %val = @_ ;
foreach my $Key ( keys %val ) {
$val{$Key} = CLASS_HPLOO_ATTR_TYPE($class , $tp , $val{$Key}) ;
}
return \%val ;
}
elsif ($type =~ /^refarray(&?[\w:]+)/ ) {
my $tp = $1 ;
return undef if ref($_[0]) ne 'ARRAY' ;
my $ref = CLASS_HPLOO_ATTR_TYPE($class , "array$tp" , @{$_[0]}) ;
@{$_[0]} = @{$ref} ;
return $_[0] ;
}
elsif ($type =~ /^refhash(&?[\w:]+)/ ) {
my $tp = $1 ;
return undef if ref($_[0]) ne 'HASH' ;
my $ref = CLASS_HPLOO_ATTR_TYPE($class , "hash$tp" , %{$_[0]}) ;
%{$_[0]} = %{$ref} ;
return $_[0] ;
}
elsif ($type =~ /^\w+(?:::\w+)*$/ ) {
return( UNIVERSAL::isa($_[0] , $type) ? $_[0] : undef ) ;
}
return undef ;
}
#######################################
# CLASS::HPLOO::BASE::HPLOO_TIESCALAR #
#######################################
package Class::HPLOO::Base::HPLOO_TIESCALAR ;
sub TIESCALAR {
shift ;
my $obj = shift ;
my $this = bless( { nm => $_[0] , tp => $_[1] , pr => $_[2] , rf => $_[3] , rfcg => $_[4] , pk => ($_[5] || scalar caller) } , __PACKAGE__ ) ;
if ( $this->{tp} =~ /^sub_(\w+)$/ ) {
my $CLASS_HPLOO = Class::HPLOO::Base::GET_CLASS_HPLOO_HASH( undef , $this->{pk} ) ;
if ( !ref($$CLASS_HPLOO{OBJ_TBL}) ) {
eval { require Hash::NoRef } ;
if ( !$@ ) {
$$CLASS_HPLOO{OBJ_TBL} = {} ;
tie( %{$$CLASS_HPLOO{OBJ_TBL}} , 'Hash::NoRef') ;
}
else { $@ = undef }
}
$$CLASS_HPLOO{OBJ_TBL}{ ++$$CLASS_HPLOO{OBJ_TBL}{x} } = $obj ;
$this->{oid} = $$CLASS_HPLOO{OBJ_TBL}{x} ;
}
return $this ;
}
sub STORE {
my $this = shift ;
my $ref = $this->{rf} ;
my $ref_changed = $this->{rfcg} ;
if ( $ref_changed ) {
if ( ref $$ref_changed ne 'HASH' ) { $$ref_changed = {} }
$$ref_changed->{$this->{nm}} = 1 ;
}
if ( $this->{pr} ) {
my $tp = $this->{tp} =~ /^ref/ ? $this->{tp} : 'ref' . $this->{tp} ;
$$ref = &{"$this->{pk}::CLASS_HPLOO_ATTR_TYPE"}($this->{pk} , $tp , @_) ;
}
else {
$$ref = &{"$this->{pk}::CLASS_HPLOO_ATTR_TYPE"}($this->{pk} , $this->{tp} , @_) ;
}
}
sub FETCH {
my $this = shift ;
my $ref = $this->{rf} ;
if ( $this->{tp} =~ /^sub_(\w+)$/ ) {
my $CLASS_HPLOO = Class::HPLOO::Base::GET_CLASS_HPLOO_HASH( undef , $this->{pk} ) ;
my $sub = $this->{pk} . '::' . $1 ;
my $obj = $$CLASS_HPLOO{OBJ_TBL}{ $this->{oid} } ;
return (&$sub($obj,@_))[0] if defined &$sub ;
}
else {
if ( $this->{tp} =~ /^(?:ref)?(?:array|hash)/ ) {
my $ref_changed = $this->{rfcg} ;
if ( $ref_changed ) {
if ( ref $$ref_changed ne 'HASH' ) { $$ref_changed = {} }
$$ref_changed->{$this->{nm}} = 1 ;
}
}
return $$ref ;
}
}
sub UNTIE {}
sub DESTROY {}
#######
# END #
#######
1;