comparison Lib/IMPL/Class/Property/Base.pm @ 59:0f3e369553bd

Rewritten property implementation (probably become slower but more flexible) Configuration infrastructure in progress (in the aspect of the lazy activation) Initial concept for the code generator
author wizard
date Tue, 09 Mar 2010 02:50:45 +0300
parents
children b0c068da93ac
comparison
equal deleted inserted replaced
58:a35b60b16a99 59:0f3e369553bd
1 package IMPL::Class::Property::Base;
2 use strict;
3
4 use IMPL::Class::Property;
5
6 require IMPL::Class::Member;
7
8 our @factoryParams = qw($class $name $set $get $validator);
9
10 my %factoryCache;
11
12 my $accessor_get_no = 'die new IMPL::Exception(\'The property is write only\',$name,$class) unless $get;';
13 my $accessor_set_no = 'die new IMPL::Exception(\'The property is read only\',$name,$class) unless $set;';
14
15 my $custom_accessor_get = 'unshift @_, $this and goto &$get;';
16 my $custom_accessor_set = 'unshift @_, $this and goto &$set;';
17
18 my $validator_code = '$this->$validator(@_);';
19
20 my %access_code = (
21 IMPL::Class::Member::MOD_PUBLIC , "",
22 IMPL::Class::Member::MOD_PROTECTED, "die new IMPL::Exception('Can\\'t access the protected member',\$name,\$class,scalar caller) unless UNIVERSAL::isa(scalar caller,\$class);",
23 IMPL::Class::Member::MOD_PRIVATE, "die new IMPL::Exception('Can\\'t access the private member',\$name,\$class,scalar caller) unless caller eq \$class;"
24 );
25
26 my $virtual_call = q(
27 my $method = $this->can($name);
28 return $this->$method(@_) unless $method == $accessor or caller->isa($class);
29 );
30
31 my $owner_check = "die new IMPL::Exception('Set accessor is restricted to the owner',\$name,\$class,scalar caller) unless caller eq \$class;";
32
33 sub GenerateAccessors {
34 my ($self,$param,@params) = @_;
35
36 my %accessors;
37
38 if (not ref $param) {
39 if ($param & prop_list) {
40 $accessors{get} = ($param & prop_get) ? $self->GenerateGetList(@params) : $accessor_get_no;
41 $accessors{set} = ($param & prop_set) ? $self->GenerateSetList(@params) : $accessor_set_no;
42 } else {
43 $accessors{get} = ($param & prop_get) ? $self->GenerateGet(@params) : $accessor_get_no;
44 $accessors{set} = ($param & prop_set) ? $self->GenerateSet(@params) : $accessor_set_no;
45 }
46 $accessors{owner} = (($param & owner_set) == owner_set) ? $owner_check : "";
47 } elsif (UNIVERSAL::isa($param,'HASH')) {
48 $accessors{get} = $param->{get} ? $custom_accessor_get : $accessor_get_no;
49 $accessors{set} = $param->{set} ? $custom_accessor_set : $accessor_set_no;
50 $accessors{owner} = "";
51 } else {
52 die new IMPL::Exception('The unsupported accessor/mutators supplied',$param);
53 }
54
55 return \%accessors;
56 }
57
58 sub GenerateSet {
59 die new IMPL::Exception("Standard accessors not supported");
60 }
61
62 sub GenerateGet {
63 die new IMPL::Exception("Standard accessors not supported");
64 }
65
66 sub GenerateGetList {
67 die new IMPL::Exception("Standard accessors not supported");
68 }
69
70 sub GenerateSetList {
71 my ($self) = @_;
72 die new IMPL::Exception("Standard accessors not supported");
73 }
74
75 sub Make {
76 my ($self,$propInfo) = @_;
77
78 my $key = $self->MakeFactoryKey($propInfo);
79
80 my $factory = $factoryCache{$key};
81
82 unless ($factory) {
83 my $mutators = $self->GenerateAccessors($propInfo->Mutators);
84 $factory = $self->CreateFactory(
85 $access_code{ $propInfo->Access },
86 $propInfo->Attributes->{validator} ? $validator_code : "",
87 $mutators->{owner},
88 $mutators->{get},
89 $mutators->{set}
90 );
91 $factoryCache{$key} = $factory;
92 }
93
94 {
95 no strict 'refs';
96 *{ $propInfo->Class.'::'.$propInfo->Name } = &$factory($self->RemapFactoryParams($propInfo));
97 }
98
99 my $mutators = $propInfo->Mutators;
100
101 if (ref $mutators) {
102 $propInfo->canGet( $mutators->{get} ? 1 : 0 );
103 $propInfo->canSet( $mutators->{set} ? 1 : 0 );
104 } else {
105 $propInfo->canGet( $mutators & prop_get );
106 $propInfo->canSet( $mutators & prop_set );
107 }
108 }
109
110 # extract from property info: class, name, get_accessor, set_accessor, validator
111 sub RemapFactoryParams {
112 my ($self,$propInfo) = @_;
113
114 my $mutators = $propInfo->Mutators;
115 my $class = $propInfo->Class;
116 my $validator = $propInfo->Attributes->{validator};
117
118 die new IMPL::Exception('Can\'t find the specified validator',$class,$validator) if $validator and ref $validator ne 'CODE' and not $class->can($validator);
119
120 return (
121 $propInfo->get(qw(Class Name)),
122 (ref $mutators?
123 ($mutators->{set},$mutators->{get})
124 :
125 (undef,undef)
126 ),
127 $validator
128 );
129 }
130
131 sub MakeFactoryKey {
132 my ($self,$propInfo) = @_;
133
134 my ($access,$mutators,$validator) = ($propInfo->get(qw(Access Mutators)),$propInfo->Attributes->{validator});
135
136 return join ('',
137 $access,
138 $validator ? 'v' : 'n',
139 ref $mutators ?
140 ('c' , $mutators->{get} ? 1 : 0, $mutators->{set} ? 1 : 0)
141 :
142 (($mutators & prop_list) ? 'l' : 's' , ($mutators & prop_get) ? 1 : 0, ($mutators & prop_set) ? ((($mutators & owner_set) == owner_set) ? 2 : 1 ) : 0 )
143 );
144 }
145
146 sub CreateFactory {
147 my ($self,$codeAccessCheck,$codeValidator,$codeOwnerCheck,$codeGet,$codeSet) = @_;
148
149 my $strParams = join(',',@factoryParams);
150
151 my $factory = <<FACTORY;
152
153 sub {
154 my ($strParams) = \@_;
155 my \$accessor;
156 \$accessor = sub {
157 my \$this = shift;
158 $codeAccessCheck
159 $codeValidator
160 if (\@_) {
161 $codeOwnerCheck
162 $codeSet
163 } else {
164 $codeGet
165 }
166 }
167 }
168 FACTORY
169
170 return ( eval $factory or die new IMPL::Exception("Syntax error due compiling the factory","$@") );
171 }
172
173 1;
174
175 __END__
176
177 =pod
178
179 =head1 DESCRIPTION
180
181 Базовый класс для реализации свойств.
182
183 По существу свойства состоят из двух методов для установки и получения значений. Также
184 существует несколько вариантов доступа к свойству, и метод верификации значения. Еще
185 свойства могут быть виртуальными.
186
187 Для создания реализатора свойств достаточно унаследовать от этого класса и описать
188 методы для генерации кода получения и установки значения.
189
190 =cut