]> wagnertech.de Git - mfinanz.git/blobdiff - SL/Helper/Csv/Dispatcher.pm
SL::Helper::Csv kann mit Multiplex-Daten (mehreren Profilen) umgehen.
[mfinanz.git] / SL / Helper / Csv / Dispatcher.pm
index 31c5eba587b66fc585dcfb7a29d74a32b4c67c33..17d52d75103215641551b6f1df608b431b3e2a30 100644 (file)
@@ -9,6 +9,8 @@ use Rose::Object::MakeMethods::Generic scalar => [ qw(
   _specs _errors
 ) ];
 
+use SL::Helper::Csv::Error;
+
 sub new {
   my ($class, $parent) = @_;
   my $self = bless { }, $class;
@@ -20,13 +22,67 @@ sub new {
 }
 
 sub dispatch {
-  my ($self, $obj, $line) = @_;
+  my ($self, $line) = @_;
+
+  my $class = $self->_class_by_line($line);
+  croak 'no class given' unless $class;
+
+  eval "require " . $class;
+  my $obj = $class->new;
 
-  for my $spec (@{ $self->_specs }) {
+  my $specs = $self->_specs_by_line($line);
+  for my $spec (@{ $specs }) {
     $self->apply($obj, $spec, $line->{$spec->{key}});
   }
+
+  return $obj;
+}
+
+# return class for given line
+# if only one profile is given, return this profiles class
+# if more than one profile is given, identify class by first
+# column???
+sub _class_by_line {
+  my ($self, $line) = @_;
+
+  my $class;
+  if ($self->_csv->is_multiplexed) {
+    foreach my $p (@{ $self->_csv->profile }) {
+      my $row_ident = $p->{row_ident};
+      if ($line->{datatype} eq $row_ident) {
+        $class = $p->{class};
+        last;
+      }
+    }
+  } else {
+    $class = @{ $self->_csv->profile }[0]->{class};
+  }
+
+  return $class;
+}
+
+sub _specs_by_line {
+  my ($self, $line) = @_;
+
+  my $spec;
+  my $i = 0;
+  if ($self->_csv->is_multiplexed) {
+    foreach my $p (@{ $self->_csv->profile }) {
+      my $row_ident = $p->{row_ident};
+      if ($line->{datatype} eq $row_ident) {
+        $spec = @{ $self->_specs }[$i];
+        last;
+      }
+      $i++;
+    }
+  } else {
+    $spec = @{ $self->_specs }[0];
+  }
+
+  return $spec;
 }
 
+
 sub apply {
   my ($self, $obj, $spec, $value) = @_;
   return unless $value;
@@ -36,7 +92,6 @@ sub apply {
     if ($class) {
 
       # autovifify
-      eval "require $class; 1" or die "could not load class '$class'";
       if (defined $index) {
         if (! $obj->$acc || !$obj->$acc->[$index]) {
           my @objects = $obj->$acc;
@@ -64,33 +119,90 @@ sub is_known {
 sub parse_profile {
   my ($self, %params) = @_;
 
-  my $header  = $self->_csv->header;
-  my $profile = $self->_csv->profile;
+  my $profile;
+  my $class;
+  my $header;
+  my @specs;
+
+  my $i = 0;
+  foreach my $h (@{ $self->_csv->header }) {
+    $header = $h;
+    if ($self->_csv->profile) {
+      $profile = @{ $self->_csv->profile }[$i]->{profile};
+      $class   = @{ $self->_csv->profile }[$i]->{class};
+    }
+
+    my $spec = $self->_parse_profile(profile => $profile,
+                                     class   => $class,
+                                     header  => $header);
+    push @specs, $spec;
+    $i++;
+  }
+
+  $self->_specs(\@specs);
+
+  return ! $self->errors;
+}
+
+sub _parse_profile {
+  my ($self, %params) = @_;
+
+  my $profile = $params{profile};
+  my $class   = $params{class};
+  my $header  = $params{header};
+
   my @specs;
 
   for my $col (@$header) {
     next unless $col;
-    push @specs, $self->make_spec($col, $profile->{$col} || $col);
+    if ($self->_csv->strict_profile) {
+      if (exists $profile->{$col}) {
+        push @specs, $self->make_spec($col, $profile->{$col}, $class);
+      } else {
+        $self->unknown_column($col, undef);
+      }
+    } else {
+      if (exists $profile->{$col}) {
+        push @specs, $self->make_spec($col, $profile->{$col}, $class);
+      } else {
+        push @specs, $self->make_spec($col, $col, $class);
+      }
+    }
   }
 
-  $self->_specs(\@specs);
   $self->_csv->_push_error($self->errors);
-  return ! $self->errors;
+
+  return \@specs;
 }
 
 sub make_spec {
-  my ($self, $col, $path) = @_;
+  my ($self, $col, $path, $cur_class) = @_;
 
   my $spec = { key => $col, steps => [] };
-  my $cur_class = $self->_csv->class;
+
+  return unless $path;
+
+  return unless $cur_class;
 
   for my $step_index ( split /\.(?!\d)/, $path ) {
     my ($step, $index) = split /\./, $step_index;
     if ($cur_class->can($step)) {
-      if ($cur_class->meta->relationship($step)) { #a
-        my $next_class = $cur_class->meta->relationship($step)->class;
-        push @{ $spec->{steps} }, [ $step, $next_class, $index ];
-        $cur_class = $next_class;
+      if (my $rel = $cur_class->meta->relationship($step)) { #a
+        if ($index && ! $rel->isa('Rose::DB::Object::Metadata::Relationship::OneToMany')) {
+          $self->_push_error([
+            $path,
+            undef,
+            "Profile path error. Indexed relationship is not OneToMany around here: '$step_index'",
+            undef,
+            0,
+          ]);
+          return;
+        } else {
+          my $next_class = $cur_class->meta->relationship($step)->class;
+          push @{ $spec->{steps} }, [ $step, $next_class, $index ];
+          $cur_class = $next_class;
+          eval "require $cur_class; 1" or die "could not load class '$cur_class'";
+        }
       } else { # simple dispatch
         push @{ $spec->{steps} }, [ $step ];
         last;
@@ -126,7 +238,7 @@ sub errors {
 
 sub _push_error {
   my ($self, @errors) = @_;
-  my @new_errors = ($self->errors, @errors);
+  my @new_errors = ($self->errors, map { SL::Helper::Csv::Error->new(@$_) } @errors);
   $self->_errors(\@new_errors);
 }