#!perl # ########################################################################## # # Title: Type specification # Creation date: 2007-03-05 # Author: Michael Zedeler # Description: Class holding type specifications for data streams # File: $Source: /data/cvs/lib/DSlib/lib/DS/TypeSpec.pm,v $ # Repository: kronhjorten # State: $State: Exp $ # Documentation: inline # Recepient: - # ########################################################################## # package DS::TypeSpec; use base qw{ Clone }; use strict; use Carp; use Carp::Assert; use List::MoreUtils qw{ any all }; use DS::TypeSpec::Field; our ($VERSION) = $DS::VERSION; our ($REVISION) = '$Revision: 1.2 $' =~ /(\d+\.\d+)/; sub new { my( $class, $arg1, $arg2 ) = @_; my $name; my $fields; if( $arg1 ) { if( ref( $arg1 ) eq '' ) { $name = $arg1; if( $arg2 ) { $fields = $arg2; } } else { $fields = $arg1; } if( $fields ) { should(ref($fields) , 'ARRAY'); } } my $self = bless { name => $name || '', fields => {} }, $class; if( $fields ) { $self->add_fields( $fields ); } return $self; } sub add_fields { my( $self, $fields ) = @_; foreach my $field (@$fields) { $self->add_field( $field ); } } sub add_field { my( $self, $field ) = @_; if( ref( $field ) eq '' ) { $field = new DS::TypeSpec::Field( $field ); } assert($field->isa('DS::TypeSpec::Field')); if( $self->{fields}->{ $field->{name} } ) { croak("Can't add field to data stream type spec, since another field with the same name already exists"); } else { $self->{fields}->{ $field->{name} } = $field; } } sub remove_fields { my( $self, $fields ) = @_; foreach my $field (@$fields) { $self->remove_field( $field ); } } sub remove_field { my( $self, $field ) = @_; my $field_name; if( not ref($field) eq '' ) { should($field->isa, 'DS::TypeSpec::Field'); $field_name = $field->{name}; } else { $field_name = $field; } if( not $self->{fields}->{ $field->{name} } ) { croak("Can't remove field from data stream type spec - name not recognized. The name is $field_name, but I only have " . join(", ", keys %{$self->{fields}})); } else { delete $self->{fields}->{ $field->{name} }; } } sub fields { my( $self, $fields ) = @_; my $result = 1; if( $fields ) { should(ref($fields), 'ARRAY'); my %remove_fields = ( %{$self->{fields}} ); foreach my $field ( @$fields ) { if( $self->{fields}->{$field} ) { $self->add_field( $field ); delete $remove_fields{ $field }; } } $self->{fields} = $fields; } else { $result = $self->{fields}; } return $result; } sub field_names { my( $self, $fields ) = @_; return keys %{$self->{fields}}; } sub keys_locked { my( $self, $keys_locked ) = @_; my $result = 1; if( $keys_locked ) { $self->{keys_locked} = $keys_locked ? 1 : 0; } else { $keys_locked = $self->{keys_locked}; } return $result; } sub values_readonly { my( $self, $values_readonly ) = @_; my $result = 1; if( $values_readonly ) { $self->{values_readonly} = $values_readonly ? 1 : 0; } else { $values_readonly = $self->{values_readonly}; } return $result; } sub contains { my( $self, $other ) = @_; my $result; if( $other->isa('DS::TypeSpec::Any') ) { $result = 1; } else { # This is equivalent to the subset operator in mathematics # For all of the $other fields $result = all { my $other = $_; # There must be one key with the same name any { $_ eq $other } keys %{$self->{fields}}; } keys %{$other->{fields}}; } return $result; } sub project { my( $self, $arg1, $arg2 ) = @_; my $name = ''; my $new_fields; if( $arg1 ) { if( ref( $arg1 ) eq '' ) { $name = $arg1; if( $arg2 ) { $new_fields = $arg2; } } else { $new_fields = $arg1; } } # if( ref( $fields ) eq 'ARRAY' ) { # my $new_fields = {}; # foreach my $field ( @$fields ) { # $new_fields->{$field} = 1; # } # $fields = $new_fields; # } should(ref($new_fields), 'HASH'); my $new_spec = new DS::TypeSpec( $name ); foreach my $new_field (keys %$new_fields) { if( my $field = $self->{fields}->{ $new_fields->{$new_field} } ) { my $new_field_obj = $field->clone(); $new_field_obj->{name} = $new_field; $new_spec->add_field( $new_field_obj ); } else { croak("Can't limit to field $new_field since it is not in the original type"); } } return $new_spec; } 1; #TODO Add sorting and unique constraints. Possibly also field order (or maybe not?!?)