The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Template::Flute::Iterator::JSON;

use strict;
use warnings;

use JSON;

use base 'Template::Flute::Iterator';

=head1 NAME

Template::Flute::Iterator::JSON - Iterator class for JSON strings and files

=head1 SYNOPSIS

    $json = q{[
        {"sku": "orange", "image": "orange.jpg"},
        {"sku": "pomelo", "image": "pomelo.jpg"}
    ]};

    $json_iter = Template::Flute::Iterator::JSON->new($json);

    $json_iter->next();

    $json_iter_file = Template::Flute::Iterator::JSON->new(file => 'fruits.json');

=head1 DESCRIPTION

Template::Flute::Iterator::JSON is a subclass of L<Template::Flute::Iterator>.

=head1 CONSTRUCTOR

=head2 new

Creates an Template::Flute::Iterator::JSON object from a JSON string.

The JSON string can be either passed as such or as scalar reference.

=cut

sub new {
	my ($class, @args) = @_;
	my ($json, $json_struct, $self, $key, $value);

	$self = {};
	
	bless ($self, $class);

	if (@args == 1) {
		# single parameter => JSON is passed as string or scalar reference
		if (ref($args[0]) eq 'SCALAR') {
			$json = ${$args[0]};
		}
		else {
			$json = $args[0];
		}

		$json_struct = from_json($json);
		$self->_seed_iterator($json_struct);
		
		return $self;
	}
	
	while (@args) {
		$key = shift(@args);
		$value = shift(@args);
		
		$self->{$key} = $value;
	}

	if ($self->{file}) {
		$json_struct = $self->_parse_json_from_file($self->{file});
		$self->_seed_iterator($json_struct);
	}
	else {
		die "Missing JSON file.";
	}
	
	return $self;
}

sub _seed_iterator {
    my ($self, $json_struct) = @_;

    if (exists $self->{selector}) {
        if (ref($self->{selector}) eq 'HASH') {
            my (@k, $key, $value);

            # loop through top level elements and locate selector
            if ((@k = keys %{$self->{selector}})) {
                $key = $k[0];
                $value = $self->{selector}->{$key};

                for my $record (@$json_struct) {
                    if (exists $record->{$key} 
                        && $record->{$key} eq $value) {
                        $self->seed($record->{$self->{children}});
                        return;
                    }
                }
            }

            return;
        }
        elsif ($self->{selector} eq '*') {
            # find all elements
            $self->seed($self->_tree($json_struct, $self->{children}, $self->{sort}));

            if ($self->{sort}) {
                $self->sort($self->{sort}, $self->{unique});
            }

            return;
        }

        # no matches for selector, seed iterator with empty list
        $self->seed();
        return;
    }
    
    $self->seed($json_struct);
}

sub _tree {
    my ($self, $json_struct, $children, $sort) = @_;
    my (@leaves);

    for my $record (@$json_struct) {
        if (exists $record->{$children}) {
            for my $child (@{$record->{$children}}) {
                push (@leaves, $child);
            }
        }
    }

    return \@leaves;
}

sub _parse_json_from_file {
	my ($self, $file) = @_;
	my ($json_fh, $json_struct, $json_txt);
	
	# read from JSON file
	unless (open $json_fh, '<', $file) {
		die "$0: failed to open JSON file $file: $!\n";
	}

	while (<$json_fh>) {
		$json_txt .= $_;
	}

	close $json_fh;

	# parse JSON
	$json_struct = from_json($json_txt);

	return $json_struct;
}

=head1 AUTHOR

Stefan Hornburg (Racke), <racke@linuxia.de>

=head1 LICENSE AND COPYRIGHT

Copyright 2011 Stefan Hornburg (Racke) <racke@linuxia.de>.

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.

=cut

1;