+ return $errors;
+}
+
+=head2 reset
+
+Clean all accumulated errors for this input and remember delimiter templates
+for L<save_delimiters_templates>
+
+ $validate->reset;
+
+This function B<must> be called after each input to provide accurate statistics.
+
+=cut
+
+sub reset {
+ my $self = shift;
+
+ my $log = $self->_get_logger;
+
+ delete ($self->{errors});
+
+ if ( ! $self->{_delimiters_templates} ) {
+ $log->debug("called without _delimiters_templates?");
+ return;
+ }
+
+ foreach my $f ( keys %{ $self->{_delimiters_templates} } ) {
+ foreach my $t ( keys %{ $self->{_delimiters_templates}->{$f} } ) {
+ $self->{_accumulated_delimiters_templates}->{$f}->{$t} +=
+ $self->{_delimiters_templates}->{$f}->{$t};
+ }
+ }
+ $log->debug("_accumulated_delimiters_templates = ", sub { dump( $self->{_accumulated_delimiter_templates} ) } );
+ delete ($self->{_delimiters_templates});
+}
+
+=head2 all_errors
+
+Return hash with all errors
+
+ print dump( $validate->all_errors );
+
+=cut
+
+sub all_errors {
+ my $self = shift;
+ return $self->{errors};
+}
+
+=head2 report_error
+
+Produce nice humanly readable report of single error
+
+ print $validate->report_error( $error_hash );
+
+=cut
+
+sub report_error {
+ my $self = shift;
+
+ my $h = shift || die "no hash?";
+
+ sub _unroll {
+ my ($self, $tree, $accumulated) = @_;
+
+ my $log = $self->_get_logger();
+
+ $log->debug("# ",
+ ( $tree ? "tree: $tree " : '' ),
+ ( $accumulated ? "accumulated: $accumulated " : '' ),
+ );
+
+ my $results;
+
+ if (ref($tree) ne 'HASH') {
+ return ("$accumulated\t($tree)", undef);
+ }
+
+ my $dump;
+
+ foreach my $k (sort keys %{ $tree }) {
+
+ if ($k eq 'dump') {
+ $dump = $tree->{dump};
+ #warn "## dump ",dump($dump),"\n";
+ next;
+ }
+
+ $log->debug("current: $k");
+
+ my ($new_results, $new_dump) = $self->_unroll($tree->{$k},
+ $accumulated ? "$accumulated\t$k" : $k
+ );
+
+ $log->debug( "new_results: ", sub { dump($new_results) } ) if ( $new_results );
+
+ push @$results, $new_results if ($new_results);
+ $dump = $new_dump if ($new_dump);
+
+ }
+
+ $log->debug( "results: ", sub { dump($results) } ) if ( $results );
+
+ if ($#$results == 0) {
+ return ($results->[0], $dump);
+ } else {
+ return ($results, $dump);
+ }
+ }
+
+
+ sub _reformat {
+ my $l = shift;
+ $l =~ s/\t/ /g;
+ $l =~ s/_/ /g;
+ return $l;
+ }
+
+ my $out = '';
+
+ for my $f (sort keys %{ $h }) {
+ $out .= "$f: ";
+
+ my ($r, $d) = $self->_unroll( $h->{$f} );
+ my $e;
+ if (ref($r) eq 'ARRAY') {
+ $e .= join(", ", map { _reformat( $_ ) } @$r);
+ } else {
+ $e .= _reformat( $r );
+ }
+ $e .= "\n\t$d" if ($d);
+
+ $out .= $e . "\n";
+ }
+ return $out;
+}
+
+
+=head2 report
+
+Produce nice humanly readable report of errors
+
+ print $validate->report;
+
+=cut
+
+sub report {
+ my $self = shift;
+ my $e = $self->{errors} || return;
+
+ my $out;
+ foreach my $mfn (sort { $a <=> $b } keys %$e) {
+ $out .= "MFN $mfn\n" . $self->report_error( $e->{$mfn} ) . "\n";
+ }
+
+ return $out;
+
+}
+
+=head2 delimiters_templates
+
+Generate report of delimiter tamplates
+
+ my $report = $validate->delimiter_teplates(
+ report => 1,
+ current_input => 1,
+ );
+
+Options:
+
+=over 4
+
+=item report
+
+Generate humanly readable report with single fields
+
+=item current_input
+
+Report just current_input and not accumulated data
+
+=back
+
+=cut
+
+sub delimiters_templates {
+ my $self = shift;
+
+ my $args = {@_};
+
+ my $t = $self->{_accumulated_delimiters_templates};
+ $t = $self->{_delimiters_templates} if ( $args->{current_input} );
+
+ my $log = $self->_get_logger;
+
+ unless ($t) {
+ $log->error("called without delimiters");
+ return;
+ }
+
+ my $out;
+
+ foreach my $f (sort { $a <=> $b } keys %$t) {
+ $out .= "$f\n" if ( $args->{report} );
+ foreach my $template (sort { $a cmp $b } keys %{ $t->{$f} }) {
+ my $count = $t->{$f}->{$template};
+ $out .=
+ ( $count ? "" : "# " ) .
+ ( $args->{report} ? "" : "$f" ) .
+ "\t$count\t$template\n";
+ }
+ }
+
+ return $out;
+}
+
+=head2 save_delimiters_templates
+
+Save accumulated delimiter templates
+
+ $validator->save_delimiters_template( '/path/to/validate/delimiters' );
+
+=cut
+
+sub save_delimiters_templates {
+ my $self = shift;
+
+ my $path = $self->{delimiters_path};
+
+ return unless ( $path );
+
+ my $log = $self->_get_logger;
+
+ if ( ! $self->{_accumulated_delimiters_templates} ) {
+ $log->error('no _accumulated_delimiters_templates found, reset');
+ $self->reset;
+ }
+
+ if ( ! $self->{_delimiters_templates} ) {
+ $log->error('found _delimiters_templates, calling reset');
+ $self->reset;
+ }
+
+ $path .= '.new' if ( -e $path );
+
+ open(my $d, '>', $path) || $log->fatal("can't open $path: $!");
+ print $d $self->delimiters_templates;
+ close($d);