view Lib/IMPL/DOM/Transform/ObjectToDOM.pm @ 261:93963ec449c5

sync
author sergey
date Fri, 28 Dec 2012 16:43:03 +0400
parents 299af584c05f
children 0f59b2de72af
line wrap: on
line source

package IMPL::DOM::Transform::ObjectToDOM;
use strict;

use IMPL::Const qw(:prop :access);
use IMPL::declare {
    require => {
        PropertyInfo => 'IMPL::Class::PropertyInfo',
        Builder => 'IMPL::DOM::Navigator::Builder',
        Exception => 'IMPL::Exception',
        ArgumentException => '-IMPL::InvalidArgumentException',
        OperationException => '-IMPL::InvalidOperationException'
    },
    base => [
        'IMPL::Transform' => sub {
            -plain => 'TransformPlain',
            HASH => 'TransformHash',
            -default => 'TransformDefault'
        }
    ],
    props => [
        documentSchema => PROP_RO,
        _schema => PROP_RW,
        _navi => PROP_RW
    ]
};

use constant {
    SchemaNode => 'IMPL::DOM::Schema::Node',
    ComplexNode => 'IMPL::DOM::Schema::ComplexNode'
};

sub CTOR {
    my ($this,$docName,$docSchema,$transforms) = @_;
    
    my $docNodeSchema = $docSchema->selectSingleNode(sub { $_->isa(SchemaNode) and $_->name eq $docName } )
        or die OperationException->new("Can't find a node schema for the document '$docName'");
       
    my $docClass = ($docNodeSchema->can('nativeType') ? $docNodeSchema->nativeType : undef) || 'IMPL::DOM::Document';
    
    $this->documentSchema($docNodeSchema);
    
    $this->_navi(
        Builder->new(
            $docClass,
            $docSchema,
            ignoreUndefined => 1
        )
    );
    $this->_schema($docSchema);
    
    $this->_navi->NavigateCreate($docName);
}

sub TransformPlain {
    my ($this,$data) = @_;
    
    $this->_navi->Current->nodeValue( $data );
    return $this->_navi->Current;
}

sub currentNode {
    shift->_navi->Current;
}

sub inflateNodeValue {
    shift->_navi->inflateValue(shift);
}

sub TransformHash {
    my ($this,$data) = @_;
    
    die ArgumentException->new(data => 'A HASH reference is required')
        unless ref $data eq 'HASH';
        
    return $this->StoreObject($this->currentNode,$data)
        if !$this->currentNode->schema->isa(ComplexNode);

    KEYLOOP: foreach my $key (keys %$data) {
        my $value = $data->{$key};
        
        if (ref $value eq 'ARRAY') {
            #TODO: collapse empty values only if needed
            foreach my $subval (grep $_, @$value) {
                
                $this->_navi->saveState();
                
                my $node = $this->_navi->NavigateCreate($key);
                
                unless(defined $node) {
                    #$this->_navi->Back();
                    $this->_navi->restoreState();
                    next KEYLOOP;
                }
                
                $this->_navi->applyState();
                
                $this->Transform($subval);
                
                $this->_navi->Back();
            }
        } else {
            $this->_navi->saveState();
            my $node = $this->_navi->NavigateCreate($key);

            unless(defined $node) {
                #$this->_navi->Back();
                $this->_navi->restoreState();
                next KEYLOOP;
            }
            
            $this->_navi->applyState();
            
            $this->Transform($value);
            
            $this->_navi->Back();            
        }
    }
    return $this->_navi->Current;
}

# this method handles situatuions when a complex object must be stored in a
# simple node.
sub StoreObject {
    my ($this,$node,$data) = @_;
    
    $node->nodeValue($data);
    
    return $node;
}

sub TransformDefault {
    my ($this,$data) = @_;
    
    return $this->StoreObject($this->currentNode,$data)
        if !$this->currentNode->schema->isa(ComplexNode);
    
    if ( ref $data and eval { $data->can('GetMeta') } ) {
        my %props = map {
            $_->name, 1
        } $data->GetMeta(PropertyInfo, sub { $_->access == ACCESS_PUBLIC }, 1 );
        
        my %values = map {
            $_,
            $data->$_();
        } keys %props;
        
        return $this->Transform(\%values);
    } else {
        die OperationException->new("Don't know how to transform $data");
    }
    
    return $this->_navi->Current;
}

sub buildErrors {
    my ($this) = @_;
    
    return $this->_navi->buildErrors;
}

1;

__END__

=pod

=head1 NAME

C<IMPL::DOM::Transform::ObjectToDOM> -преобразование объекта  

=head1 SYNOPSIS 

=cut