view Lib/IMPL/Web/TT/Form.pm @ 148:e6447ad85cb4

DOM objects now have a schema and schemaSource properties RegExp now can launder data Improved post to DOM transformation (multiple values a now supported) Added new axes to navigation queries: ancestor and descendant minor changes and bug fixes
author wizard
date Mon, 16 Aug 2010 08:26:44 +0400
parents bd10093bb122
children a9f4ba4783eb
line wrap: on
line source

use strict;
package IMPL::Web::TT::Form;

use base qw(IMPL::Web::TT::Control);

use IMPL::Class::Property;
use IMPL::DOM::Navigator::SchemaNavigator();

__PACKAGE__->PassThroughArgs;

BEGIN {
	public property base => prop_all;
	public property schema => prop_all;
	public property errors => prop_all;
	public property data => prop_all;
	public property state => prop_all;
	public property formResult => prop_all;
}

sub CTOR {
	my ($this) = @_;
	
	if (my $form = $this->formResult) {
		$this->base($form->{formName});
		$this->errors($form->{formErrors});
		$this->data($form->{formData});
		$this->schema($form->{formSchema});
		$this->state($form->{state});
	} else {
	
		$this->base($this->nodeName) unless $this->base;
		
		die new IMPL::InvalidArgumentException('A schema is required for a form',$this->nodeName)
			unless eval { $this->schema->isa( typeof IMPL::DOM::Schema ) };
		
		die new IMPL::InvalidOperationException('Can\'t find a form definition in a schema',$this->nodeName,$this->base)
			unless $this->schema->selectNodes(sub { $_->nodeName eq 'ComplexNode' and $_->name eq $this->base });
	}
			
	$this->errors([]) unless $this->errors;
}

sub fillContents {
	my ($this) = @_;
	
	my $schema = $this->schema->selectSingleNode(sub { $_->nodeName eq 'ComplexNode' and $_->name eq $this->base });
	
	$this->buildContainer(
		$schema,
		$schema,
		$this->data->isComplex ? $this->data : undef,
		$this
	);
}

sub buildContainer {
	my ($this,$schemaSource,$schema,$domNode,$container,$path) = @_;
	
	$path = [@{$path || []},{node => $domNode, schemaSource => $schemaSource}];
	
	$container ||= $this->document->Create($schemaSource->name,'IMPL::Web::TT::Collection');
	
	foreach my $schemaItem ( $schema->content->childNodes ) {
		my $schemaItemSource = $schemaItem;
		
		$schemaItem = $this->schema->resolveType($schemaItem->type)
			if typeof $schemaItem eq typeof IMPL::DOM::Schema::Node;
			
		my @nodesData = $domNode->selectNodes(sub { $_->schemaSource == $schemaItemSource } ) if $domNode;
		
		push @nodesData, undef unless @nodesData;
			
		if ($schemaItem->isa(typeof IMPL::DOM::Schema::ComplexNode) ) {
			$this->appendChild( $this->buildContainer($schemaItemSource,$schemaItem,$_,undef,$path) ) foreach @nodesData;
		} elsif ($schemaItem->isa(typeof IMPL::DOM::Schema::SimpleNode)) {
			$this->appendChild( $this->buildControl($schemaItemSource,$schemaItem,$_,$path) ) foreach @nodesData;
		}
	}
	
	return $container;
}

sub buildControl {
	my ($this,$schemaSource,$schema,$node,$path) = @_;
	
	my @errors;
	
	if ($node) {
		@errors = grep { ($_->Node || $_->Parent) == $node } @{$this->errors}; 
	} else {
		@errors = grep $_->Schema == $schemaSource, @{$this->errors};
	}
	
	return $this->document->CreateControl(
		$schemaSource->name,
		$this->mapType($schemaSource),
		{
			schema => $schema,
			sourceSchema => $schemaSource,
			errors => \@errors,
			data => $node,
			nodeValue => $node && $node->nodeValue, # small hack set a non dom class property through
			queryParameter => $this->makeParameterName([@$path,{ node => $node, schemaSource => $schemaSource}])
		}
	);
}

sub mapType {
	my ($this,$schema) = @_;
	
	$schema->nodeProperty('control') ||
	( $schema->type && $this->schema->resolveType($schema->type)->nodeProperty('control') )
		or die new IMPL::Exception("Unable to get control class for the form element",$schema->path);
}

sub makeParameterName {
	my ($this,$path) = @_;
	
	join '/', map {
		$_->{node} ?
			(
				$_->{node}->nodeProperty('instanceId') ?
					$_->{node}->nodeName . '['. ']' :
					$_->{node}->nodeName
			) :
			(
				$_->{schemaSource}->maxOccur eq 'unbounded' || $_->{schemaSource}->maxOccur > 1 ?
					$_->{schemaSource}->name . '[0]' :
					$_->{schemaSource}->name
			) 
	} @$path;
}

sub makeControlArgs{
	my ($this,$path) = @_;
	
	my $navi = new IMPL::DOM::Navigator::SchemaNavigator($this->schema);
	my @path = ($this->base, split /\./,$path); 
	
	$navi->NavigateName($_) or die new IMPL::InvalidArgumentException(
		"Can't find a definition for an element",
		$_,
		$path,
		$this->element,
	) foreach @path;
	
	my $schema = $navi->Current;
	my $sourceSchema = $navi->SourceSchemaNode;
	my $queryParameter = join '/', @path;
	shift @path;
	my $node = $this->data ? $this->data->selectSingleNode(@path) : undef;
	
	my @errors;
	
	if ($node) {
		@errors = grep { ($_->Node || $_->Parent) == $node } @{$this->errors}; 
	} else {
		@errors = grep $_->Schema == $sourceSchema, @{$this->errors};
	}
	
	return {
		schema => $schema,
		sourceSchema => $sourceSchema,
		errors => \@errors,
		data => $node,
		nodeValue => $node && $node->nodeValue, # small hack set a non dom class property through
		queryParameter => $queryParameter
	};
}

sub makeContent {
	my ($this,$mappings) = @_;
	
	my $formSchema = $this->schema->selectSingleNode(sub { $_->nodeName eq 'ComplexNode' and $_->name eq $this->base } )
		or die new Exception("Cant find a schema element for the specified form", $this->base);

	my $doc = $this->document;		
	foreach my $itemSchema ( $formSchema->content->childNodes ) {
		my $itemName = $itemSchema->name; 
		if (my $controlClass = $mappings->{$itemName} ) {
			my $contorl = $doc->CreateControl($itemName,$controlClass,$this->makeControlArgs($itemName));
			$this->appendChild($contorl);
		}
	}
	return;
}

sub formErrors {
	my ($this) = @_;
	
	if (my $node = $this->data ) {
		return [
			grep {
				( $_->Node || $_->Parent) == $node
			} @{$this->errors}
		];
	} else {
		return [];
	}
}

1;
__END__

=pod

=head1 NAME

C<IMPL::Web::TT::Form> - Форма с элементами

=head1 DESCRIPTION

Является элементом управления, тоесть для своего преобразования ипользует
шаблон 

=cut