2 use OpenSRF::Utils::JSON;
4 use base 'OpenSRF::Application';
5 use OpenSRF::Utils::Logger;
6 use OpenSRF::Utils::SettingsClient;
10 my $log = 'OpenSRF::Utils::Logger';
12 use vars qw/$fieldmap $VERSION/;
14 sub publish_fieldmapper {
15 my ($self,$client,$class) = @_;
17 return $fieldmap unless (defined $class);
18 return undef unless (exists($$fieldmap{$class}));
19 return {$class => $$fieldmap{$class}};
21 __PACKAGE__->register_method(
22 api_name => 'opensrf.open-ils.system.fieldmapper',
24 method => 'publish_fieldmapper',
28 # To dump the Javascript version of the fieldmapper struct use the command:
30 # PERL5LIB=~/cvs/ILS/OpenSRF/src/perlmods/:~/cvs/ILS/Open-ILS/src/perlmods/ GEN_JS=1 perl -MOpenILS::Utils::Fieldmapper -e 'print "\n";'
32 # ... adjusted for your CVS sandbox, of course.
36 return () unless (defined $fieldmap);
37 return keys %$fieldmap;
41 my $attr_list = shift;
42 my $attr_name = shift;
44 my $attr = $attr_list->getNamedItem( $attr_name );
45 if( defined( $attr ) ) {
46 return $attr->getValue();
52 my $field_list = shift;
55 # Get attributes of the field list. Since there is only one
56 # <field> per class, these attributes logically belong to the
57 # enclosing class, and that's where we load them.
59 my $field_attr_list = $field_list->attributes();
61 my $sequence = get_attribute( $field_attr_list, 'oils_persist:sequence' );
62 if( ! defined( $sequence ) ) {
65 my $primary = get_attribute( $field_attr_list, 'oils_persist:primary' );
67 # Load attributes into the Fieldmapper ----------------------
69 $$fieldmap{$fm}{ sequence } = $sequence;
70 $$fieldmap{$fm}{ identity } = $primary;
72 # Load each field -------------------------------------------
74 my $array_position = 0;
75 for my $field ( $field_list->childNodes() ) { # For each <field>
76 if( $field->nodeName eq 'field' ) {
78 my $attribute_list = $field->attributes();
80 my $name = get_attribute( $attribute_list, 'name' );
81 next if( $name eq 'isnew' || $name eq 'ischanged' || $name eq 'isdeleted' );
82 my $virtual = get_attribute( $attribute_list, 'oils_persist:virtual' );
83 if( ! defined( $virtual ) ) {
86 my $selector = get_attribute( $attribute_list, 'reporter:selector' );
88 $$fieldmap{$fm}{fields}{ $name } =
89 { virtual => ( $virtual eq 'true' ) ? 1 : 0,
90 position => $array_position,
93 # The selector attribute, if present at all, attaches to only one
94 # of the fields in a given class. So if we see it, we store it at
95 # the level of the enclosing class.
97 if( defined( $selector ) ) {
98 $$fieldmap{$fm}{selector} = $selector;
105 # Load the standard 3 virtual fields ------------------------
107 for my $vfield ( qw/isnew ischanged isdeleted/ ) {
108 $$fieldmap{$fm}{fields}{ $vfield } =
109 { position => $array_position,
117 my $link_list = shift;
120 for my $link ( $link_list->childNodes() ) { # For each <link>
121 if( $link->nodeName eq 'link' ) {
122 my $attribute_list = $link->attributes();
124 my $field = get_attribute( $attribute_list, 'field' );
125 my $reltype = get_attribute( $attribute_list, 'reltype' );
126 my $key = get_attribute( $attribute_list, 'key' );
127 my $class = get_attribute( $attribute_list, 'class' );
129 $$fieldmap{$fm}{links}{ $field } =
139 my $class_node = shift;
141 # Get attributes ---------------------------------------------
143 my $attribute_list = $class_node->attributes();
145 my $fm = get_attribute( $attribute_list, 'oils_obj:fieldmapper' );
146 $fm = 'Fieldmapper::' . $fm;
147 my $id = get_attribute( $attribute_list, 'id' );
148 my $controller = get_attribute( $attribute_list, 'controller' );
149 my $virtual = get_attribute( $attribute_list, 'virtual' );
150 if( ! defined( $virtual ) ) {
153 my $tablename = get_attribute( $attribute_list, 'oils_persist:tablename' );
154 if( ! defined( $tablename ) ) {
157 my $restrict_primary = get_attribute( $attribute_list, 'oils_persist:restrict_primary' );
159 # Load the attributes into the Fieldmapper --------------------
161 $log->debug("Building Fieldmapper class for [$fm] from IDL");
163 $$fieldmap{$fm}{ hint } = $id;
164 $$fieldmap{$fm}{ virtual } = ( $virtual eq 'true' ) ? 1 : 0;
165 $$fieldmap{$fm}{ table } = $tablename;
166 $$fieldmap{$fm}{ controller } = [ split ' ', $controller ];
167 $$fieldmap{$fm}{ restrict_primary } = $restrict_primary;
169 # Load fields and links
171 for my $child ( $class_node->childNodes() ) {
172 my $nodeName = $child->nodeName;
173 if( $nodeName eq 'fields' ) {
174 load_fields( $child, $fm );
175 } elsif( $nodeName eq 'links' ) {
176 load_links( $child, $fm );
186 return if (keys %$fieldmap);
187 return if (!OpenSRF::System->connected && !$args{IDL});
190 my $parser = XML::LibXML->new();
191 my $file = $args{IDL} || OpenSRF::Utils::SettingsClient->new->config_value( 'IDL' );
192 my $fmdoc = $parser->parse_file( $file );
193 my $rootnode = $fmdoc->documentElement();
195 for my $child ( $rootnode->childNodes() ) { # For each <class>
196 my $nodeName = $child->nodeName;
197 if( $nodeName eq 'class' ) {
198 load_class( $child );
202 #-------------------------------------------------------------------------------
203 # Now comes the evil! Generate classes
205 for my $pkg ( __PACKAGE__->classes ) {
206 (my $cdbi = $pkg) =~ s/^Fieldmapper:://o;
210 use base 'Fieldmapper';
213 if (exists $$fieldmap{$pkg}{proto_fields}) {
214 for my $pfield ( sort keys %{ $$fieldmap{$pkg}{proto_fields} } ) {
215 $$fieldmap{$pkg}{fields}{$pfield} = { position => $pos, virtual => $$fieldmap{$pkg}{proto_fields}{$pfield} };
220 OpenSRF::Utils::JSON->register_class_hint(
221 hint => $pkg->json_hint,
232 $value = [] unless (defined $value);
233 return bless $value => $self->class_name;
246 (my $field = $AUTOLOAD) =~ s/^.*://o;
247 my $class_name = $obj->class_name;
250 $fpos =~ s/^clear_//og ;
252 my $pos = $$fieldmap{$class_name}{fields}{$fpos}{position};
254 if ($field =~ /^clear_/o) {
256 *{$obj->class_name."::$field"} = sub {
258 $self->[$pos] = undef;
262 return $obj->$field();
265 die "No field by the name $field in $class_name!"
266 unless (exists $$fieldmap{$class_name}{fields}{$field} && defined($pos));
270 *{$obj->class_name."::$field"} = sub {
273 $self->[$pos] = $new_val if (defined $new_val);
274 return $self->[$pos];
277 return $obj->$field($value);
282 return $$fieldmap{$self->class_name}{selector};
287 return $$fieldmap{$self->class_name}{identity};
290 sub RestrictPrimary {
292 return $$fieldmap{$self->class_name}{restrict_primary};
297 return $$fieldmap{$self->class_name}{sequence};
302 return $$fieldmap{$self->class_name}{table};
307 return $$fieldmap{$self->class_name}{controller};
311 my $class_name = shift;
312 return ref($class_name) || $class_name;
317 my $class_name = $self->class_name;
318 my $fields = $$fieldmap{$class_name}{fields};
321 !$$fields{$_}{virtual}
322 } sort {$$fields{$a}{position} <=> $$fields{$b}{position}} keys %$fields;
330 my $class_name = $self->class_name;
331 return 1 if grep { $_ eq $field } keys %{$$fieldmap{$class_name}{fields}};
337 my $class_name = $self->class_name;
338 return keys %{$$fieldmap{$class_name}{fields}};
345 for my $f ($self->properties) {
355 return $self->new( [@$self] );
360 return $fieldmap->{$self->class_name}->{api_level};
365 return $fieldmap->{$self->class_name}->{cdbi};
371 return $fieldmap->{$self->class_name}->{proto_fields}->{$field} if ($field);
372 return $fieldmap->{$self->class_name}->{virtual};
378 return $fieldmap->{$self->class_name}->{readonly};
383 return $fieldmap->{$self->class_name}->{hint};