view lib/IMPL/Config/Bag.pm @ 414:ec6f2d389d1e ref20150831

working on IMPL::Config::Bag
author cin
date Fri, 02 Oct 2015 06:56:24 +0300
parents af8d359ee4cc
children 3d24b10dd0d5
line wrap: on
line source

package IMPL::Config::Bag;
use strict;

use IMPL::lang qw(:base);
use IMPL::declare {
	base => [
		'IMPL::Object' => undef
	],
	props => [
		_parents   => '*rw',    # array of parent bags
		_state     => '*rw',    #array of previous timestamps
		_cache     => '*rw',    # own or cached entries
		_timestamp => '*rw',
		_entries   => '*rw',    # each entry is represented by hash
		  # { isa => linear_isa_hash, owner => owner_of_the_entry, value => value }
	]
};

sub CTOR {
	my ( $this, $p ) = @_;

	if ($p) {
		my @parents;
		push @parents, @{ $p->{$_parents} } if $p->{$_parents};
		push @parents, $p;
		$this->{$_parents} = \@parents;
		$this->{$_state} = [ map $_->{$_timestamp}, @parents ];
	}

	$this->{$_timestamp} = 0;
	$this->{$_cache}     = {};
	$this->{$_entries}   = [];
}

sub GetParent {
	$_[0]->{$_parents} && $_[0]->{$_parents}[0];
}

sub _Validate {
	my ($this) = @_;

	my $parents = $this->{$_parents}
	  or return;
	my $state = $this->{$_state};
	my $len   = @$parents;
	my $flush;

	for ( my $i = 0 ; $i < $len ; $i++ ) {
		if ($flush) {
			$parents->[$i]{$_cache} = {};
			$state->[$i] = $parents->[$i]{$_timestamp};
		}
		else {
			$flush = ( $parents->[$i]{$_timestamp} != $state->[$i] );
		}
	}

	if ($flush) {
		$this->{$_cache} = {};
		return 0;
	}

	return 1;
}

sub Resolve {
	my ( $this, $role ) = @_;

	if ( my $d = $this->GetDescriptor() ) {
		return $d->{value};
	}
	else {
		return;
	}
}

sub GetDescriptor {
	my ( $this, $role ) = @_;

	my $d = $this->{$_cache}{$role};

# return descriptor if this is own descriptor and its level is 1 (i.e. it can't be overriden by the parent cache)
# otherwise the cache must be validated
	return $d
	  if $d
	  and ( ( $d->{owner} == $this and $d->{isa}{$role} == 1 )
		or $this->_Validate() );

	# the cache chain is valid
	# $d is not a valid descriptor

	$d = undef;
	my $prev;

	if ( my $parents = $this->{$_parents} ) {
		foreach my $bag ( @$parents, $this ) {

			# check the cache;
			unless ( my $t = $bag->{$_cache}{$role} ) {

	  # no cached entry this may be due cache flush
	  # go through own entries and find better entry than inherited from parents
				foreach my $entry ( @{ $bag->{$_entries} } ) {
					my $level = $entry->{isa}{$role};
					if ( $level and ( not($prev) or $level <= $prev ) ) {
						$d    = $entry;
						$prev = $level;
					}
				}

				#cache it
				$bag->{$_cache}{$role} = $d if $d;
			}
			else {
				$d    = $t;
				$prev = $d->{isa}{$role};
			}
		}
	}

	return $d;
}

sub ResolveAll {
	my ( $this, $role ) = @_;

	return [
		map $_->{value},
		grep $_->{isa}{$role},
		map $_->{$_entries},
		@{ $this->{$_parents} || [] },
		$this
	];

}

sub Register {
	my ( $this, $isa, $value ) = @_;

	$isa = { $isa, 1 } unless isHash($isa);

	push @{ $this->{$_entries} },
	  { owner => $this, isa => $isa, value => $value };
	$this->{$_timestamp}++;

	delete $this->{$_cache}{$_} foreach keys %$isa;

	return $this;
}

1;