Mercurial > pub > Impl
diff Lib/Common.pm @ 0:03e58a454b20
Создан репозитарий
author | Sergey |
---|---|
date | Tue, 14 Jul 2009 12:54:37 +0400 |
parents | |
children | 16ada169ca75 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/Common.pm Tue Jul 14 12:54:37 2009 +0400 @@ -0,0 +1,282 @@ +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; \ No newline at end of file