Mercurial > pub > Impl
view Lib/Common.pm @ 86:52eeec77504b
TAP fixes
author | wizard |
---|---|
date | Mon, 19 Apr 2010 02:38:18 +0400 |
parents | 16ada169ca75 |
children | 76515373dac0 |
line wrap: on
line source
package Common; use strict; no strict 'refs'; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(&ACCESS_NONE &ACCESS_READ &ACCESS_WRITE &ACCESS_ALL &DeclareProperty &DumpCaller &PropertyList &CloneObject); our $Debug; $Debug = 1 if not defined $Debug; my %ListProperties; my %GlobalContext; 1; sub ACCESS_NONE () { 0 } sub ACCESS_READ () { 1 } sub ACCESS_WRITE () { 2 } sub ACCESS_ALL () {ACCESS_READ | ACCESS_WRITE} sub PropertyList { return $ListProperties{ref($_[0]) || $_[0] || caller} || {}; } sub DeclareProperty { my ($attrName,$accessRights,%Mutators) = @_; my $Package = caller; my $Method = $Package.'::'.$attrName; my $fldName; my $getMutator = $Mutators{'get'}; my $setMutator = $Mutators{'set'}; ($fldName = $Method) =~ s/:+/_/g; $ListProperties{$Package} = {} if not exists $ListProperties{$Package}; $ListProperties{$Package}->{$attrName} = $fldName; if ($Debug) { *$Method = sub { my $this = shift; die new Exception( 'too many args ['.scalar(@_).'\]' , "'$Method' called from: ".DumpCaller() ) if (@_ > 1); my $Rights = $accessRights; $Rights = ACCESS_ALL if $Package eq caller; if (@_){ die new Exception("access denied 'write $Method'", "'$Method' called from: ".DumpCaller()) if not $Rights & ACCESS_WRITE; if (defined $setMutator) { &$setMutator($this,$fldName,shift); } else { $this->{$fldName} = $_[0]; } } elsif (defined wantarray) { die new Exception("access denied 'read $Method'", "'$Method' called from: ".DumpCaller()) if not $Rights & ACCESS_READ; if (defined $getMutator) { &$getMutator($this,$fldName); } else { if (wantarray){ if(ref $this->{$fldName} eq 'ARRAY' ) { return @{$this->{$fldName}}; } elsif (not exists $this->{$fldName}) { return; } else { return $this->{$fldName}; } } else { return $this->{$fldName}; } } } else { undef; } }; *$Method = \$fldName; } else { *$Method = sub { my $this = shift; #return undef if @_ > 1; #my $Rights = $accessRights; #$Rights = ACCESS_ALL if $Package eq caller; if (@_){ # return undef if not $Rights & ACCESS_WRITE; if (defined $setMutator) { &$setMutator($this,$fldName,shift); } else { $this->{$fldName} = shift; } } elsif (defined wantarray) { # return undef if not $Rights & ACCESS_READ; if (defined $getMutator) { &$getMutator($this,$fldName); } else { if (wantarray){ if(ref $this->{$fldName} eq 'ARRAY' ) { return @{$this->{$fldName}}; } elsif (not defined $this->{$fldName}) { return; } else { return $this->{$fldName}; } } else { return $this->{$fldName}; } } } else { undef; } }; *$Method = \$fldName; } } sub DumpCaller { return join(" ",(caller($_[0]))[1,2],(caller($_[0]+1))[3]); } sub Owner { return undef if not tied $_[0]; return undef if not tied($_[0])->UNIVERSAL::can('owner'); return tied($_[0])->owner(); }; sub CloneObject { my $object = shift; if (ref $object == undef) { return $object; } elsif (ref $object eq 'SCALAR') { return \CloneObject(${$object}); } elsif (ref $object eq 'ARRAY') { return [map{CloneObject($_)}@{$object}]; } elsif (ref $object eq 'HASH') { my %clone; while (my ($key,$value) = each %{$object}) { $clone{$key} = CloneObject($value); } return \%clone; } elsif (ref $object eq 'REF') { return \CloneObject(${$object}); } else { if ($object->can('Clone')) { return $object->Clone(); } else { die new Exception('Object doesn\'t supports cloning'); } } } package Exception; use base qw(IMPL::Exception); package Persistent; import Common; sub newSurogate { my $class = ref($_[0]) || $_[0]; return bless {}, $class; } sub load { my ($this,$context) = @_; die new Exception("invalid deserialization context") if ref($context) ne 'ARRAY'; die new Exception("This is not an object") if not ref $this; my %Props = (@{$context}); foreach my $BaseClass(@{ref($this).'::ISA'}) { while (my ($key,$value) = each %{PropertyList($BaseClass)}) { $this->{$value} = $Props{$value} if exists $Props{$value}; } } while (my ($key,$value) = each %{PropertyList(ref($this))}) { $this->{$value} = $Props{$key} if exists $Props{$key}; } return 1; } sub save { my ($this,$context) = @_; foreach my $BaseClass(@{ref($this).'::ISA'}) { while (my ($key,$value) = each %{PropertyList($BaseClass)}) { $context->AddVar($value,$this->{$value}); } } while (my ($key,$value) = each %{PropertyList(ref($this))}) { $context->AddVar($key,$this->{$value}); } return 1; } sub restore { my ($class,$context,$surogate) = @_; my $this = $surogate || $class->newNewSurogate; $this->load($context); return $this; } package Object; import Common; sub new { my $class = shift; my $self = bless {}, ref($class) || $class; $self->CTOR(@_); return $self; } sub cast { return bless {}, ref $_[0] || $_[0]; } our %objects_count; our %leaked_objects; sub CTOR { my $this= shift; $objects_count{ref $this} ++ if $Debug; my %args = @_ if scalar (@_) > 0; return if scalar(@_) == 0; warn "invalid args in CTOR. type: ".(ref $this) if scalar(@_) % 2 != 0; my @packages = (ref($this)); my $countArgs = int(scalar(@_) / 2); #print "Set ", join(', ',keys %args), "\n"; LOOP_PACKS: while(@packages) { my $package = shift @packages; #print "\t$package\n"; my $refProps = PropertyList($package); foreach my $name (keys %{$refProps}) { my $fld = $refProps->{$name}; if (exists $args{$name}) { $this->{$fld} = $args{$name}; #print "\t$countArgs, $name\n"; delete $args{$name}; $countArgs --; last LOOP_PACKS if $countArgs < 1; } else { #print "\t-$name ($fld)\n"; } } push @packages, @{$package.'::ISA'}; } } sub Dispose { my $this = shift; if ($Debug and UNIVERSAL::isa($this,'HASH')) { my @keys = grep { $this->{$_} and ref $this->{$_} } keys %{$this}; warn "not all fields of the object were deleted\n".join("\n",@keys) if @keys; } bless $this,'Object::Disposed'; } our $MemoryLeakProtection; sub DESTROY { if ($MemoryLeakProtection) { my $this = shift; warn sprintf("Object leaks: %s of type %s %s",$this,ref $this,UNIVERSAL::can($this,'_dump') ? $this->_dump : ''); } } package Object::Disposed; our $AUTOLOAD; sub AUTOLOAD { return if $AUTOLOAD eq __PACKAGE__.'::DESTROY'; die new Exception('Object have been disposed',$AUTOLOAD); } END { $MemoryLeakProtection = 0 if not $Debug; } 1;