/usr/local/CPAN/DSlib/DS/TypeSpec.pm
#!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?!?)