changeset 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 c2aa10fbb396
children b04e978d6d5a
files Lib/IMPL/DOM/Document.pm Lib/IMPL/DOM/Navigator/Builder.pm Lib/IMPL/DOM/Node.pm Lib/IMPL/DOM/Schema/Node.pm Lib/IMPL/DOM/Schema/Validator/RegExp.pm Lib/IMPL/DOM/Transform.pm Lib/IMPL/DOM/Transform/PostToDOM.pm Lib/IMPL/Object.pm Lib/IMPL/Object/Autofill.pm Lib/IMPL/Resources/Format.pm Lib/IMPL/Transform.pm Lib/IMPL/Web/Application/ControllerUnit.pm Lib/IMPL/Web/DOM/FileNode.pm Lib/IMPL/Web/QueryHandler/UrlController.pm Lib/IMPL/Web/TT/Form.pm Lib/IMPL/base.pm _test/Test/DOM/Node.pm
diffstat 17 files changed, 482 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/Lib/IMPL/DOM/Document.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/DOM/Document.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -13,6 +13,12 @@
 sub Create {
     my ($this,$nodeName,$class,$refProps) = @_;
     
+    if ( ref $class eq 'HASH' ) {
+    	$refProps = $class;
+    	$class = undef;
+    }
+    
+    $class ||= typeof IMPL::DOM::Node;
     $refProps ||= {};
     
     delete $refProps->{nodeName};
--- a/Lib/IMPL/DOM/Navigator/Builder.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/DOM/Navigator/Builder.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -33,7 +33,11 @@
     if (my $schemaNode = $this->{$_schemaNavi}->NavigateName($nodeName)) {
         my $class = $schemaNode->can('nativeType') ? $schemaNode->nativeType || 'IMPL::DOM::Node' : 'IMPL::DOM::Node';
         
+        my $schemaSource = $this->{$_schemaNavi}->SourceSchemaNode;
+        
         my @errors = $this->inflateProperties($schemaNode,\%props);
+        $props{schema} = $schemaNode;
+        $props{schemaSource} = $schemaSource;
         
         my $node;
         if (! $this->{$Document}) {
@@ -51,7 +55,7 @@
         		map {
 					IMPL::DOM::Schema::ValidationError->new(
 						Node => $node,
-						Source => $this->{$_schemaNavi}->SourceSchemaNode,
+						Source => $schemaSource,
 						Schema => $schemaNode,
 						Message => $schemaNode->messageInflateError,
 						Error => $_
@@ -171,12 +175,12 @@
 создания экземпляра и созданный узел доавляется в документ. При создании
 нового узла используется метод документа C<< IMPL::DOM::Document->Create >>
 
-Свойства узла передаются при создании через параметр C<props>, но имя создаваемого
+Свойства узла передаются при создании через параметр C<%props>, но имя создаваемого
 узла НЕ может быть переопределено свойством C<nodeName>, оно будет проигнорировано.
 
 =item C< Document >
 
-Свойство, которое содержит документ по окончании процедурв построения.
+Свойство, которое содержит документ по окончании процедуры построения.
 
 =back
 
--- a/Lib/IMPL/DOM/Node.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/DOM/Node.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -17,7 +17,9 @@
     public _direct property isComplex => { get => \&_getIsComplex } ;
     public _direct property nodeValue => prop_all;
     public _direct property childNodes => { get => \&_getChildNodes }; # prop_list
-    public _direct property parentNode => prop_get ;
+    public _direct property parentNode => prop_get | owner_set;
+    public _direct property schema => prop_get | owner_set;
+    public _direct property schemaSource => prop_get | owner_set;
     private _direct property _propertyMap => prop_all ;
     
     __PACKAGE__->class_data(property_bind => {});
@@ -27,7 +29,9 @@
 	parent => \&selectParent,
 	siblings => \&selectSiblings,
 	child => \&childNodes,
-	document => \&selectDocument
+	document => \&selectDocument,
+	ancestor => \&selectAncestors,
+	descendant => \&selectDescendant
 );
 
 sub CTOR {
@@ -290,6 +294,24 @@
 	}
 }
 
+sub selectDescendant {
+	wantarray ?
+		map $_->selectAll(), $_[0]->childNodes :
+		[map $_->selectAll(), $_[0]->childNodes]
+}
+
+sub selectAll {
+    map(selectAll($_),@{$_[0]->childNodes}) , $_[0]
+}
+
+sub selectAncestors {
+	my $parent = $_[0]->parentNode;
+	
+	wantarray ?
+		($parent ? ($parent->selectAncestors,$parent) : ()) :
+		[$parent ? ($parent->selectAncestors,$parent) : ()]
+}
+
 sub firstChild {
     @_ >=2 ? $_[0]->replaceNodeAt(0,$_[1]) : $_[0]->childNodes->[0];
 }
@@ -349,9 +371,11 @@
     my $name = shift;
     
     if (my $method = $this->can($name)) {
-    	return &$method($this,@_);
+    	unshift @_,$this;
+    	# use goto to preserve calling context
+    	goto &$method;
     }
-    
+    # dynamic property
     if (@_) {
 		# set
 	    return $this->{$_propertyMap}{$name} = shift;
@@ -429,6 +453,20 @@
 
 Ссылка на родительский элемент, если таковой имеется.
 
+=item C<[get] schema>
+
+Ссылка на узел из C<IMPL::DOM::Schema>, представляющий схему данных текущего узла. Может быть C<undef>.
+
+=item C<[get] schema>
+
+Ссылка на узел из C<IMPL::DOM::Schema>, представляющий элемент схемы, объявляющий данный узел. Может быть C<undef>.
+
+Отличается от свойства C<schema> тем, что узел в случае ссылки на тип узла, данной свойство будет содержать
+описание ссылки C<IMPL::DOM::Schema::Node>, а свойство C<schema> например будет ссылаться на
+C<IMPL::DOM::Schema::ComplexType>.
+
+=back 
+
 =head2 METHODS
 
 =cut
\ No newline at end of file
--- a/Lib/IMPL/DOM/Schema/Node.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/DOM/Schema/Node.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -18,7 +18,20 @@
 }
 
 our %CTOR = (
-    'IMPL::DOM::Node' => sub {my %args = @_; $args{nodeName} ||= 'Node'; %args}
+    'IMPL::DOM::Node' => sub {
+    	my %args = @_;
+    	delete @args{qw(
+    		minOccur
+    		maxOccur
+    		type
+    		name
+    		display
+    		display_no
+    		display_blame
+    	)} ;
+    	$args{nodeName} ||= 'Node';
+    	%args
+    }
 );
 
 sub CTOR {
@@ -28,9 +41,9 @@
     $this->{$maxOccur} = defined $args{maxOccur} ? $args{maxOccur} : 1;
     $this->{$type} = $args{type};
     $this->{$name} = $args{name} or die new IMPL::InvalidArgumentException('Argument is required','name');
-    $this->{$display} = $args{display};
-    $this->{$display_no} = $args{display_no};
-    $this->{$display_blame} = $args{display_blame};
+    $this->{$display} = $args{display} if $args{display};
+    $this->{$display_no} = $args{display_no} if $args{display};
+    $this->{$display_blame} = $args{display_blame} if $args{display};
 }
 
 sub Validate {
@@ -70,6 +83,48 @@
 
 =head1 DESCRIPTION
 
-Базовый класс для элементов схемы.
+Базовый класс для элементов схемы. Также позволяет объявлять узлы определенного типа.
+
+=head1 MEMBERS
+
+=head2 PROPERTIES
+
+=over
+
+=item C<[get,set] minOccur>
+
+C<default: 1>.
+
+Минимальное количество повторений узла.
+
+=item C<[get,set] maxOccur>
+
+C<default: 1>.
+
+Максимальное количество повторений узла
+
+=item C<[get,set] type>
+
+C<default: undef>
+
+Имя типа из схемы.
+
+=item C<[get,set] name>
+
+Имя узла.
+
+=item C<[get,set] display>
+
+Имя узла для отображения.
+
+=item C<[get,set] display_no>
+
+Имя узла для отображения (родительный падеж).
+
+=item C<[get,set] display_blame>
+
+Имя узла для отображения (винительный падеж).
+
+=back
 
 =cut
--- a/Lib/IMPL/DOM/Schema/Validator/RegExp.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/DOM/Schema/Validator/RegExp.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -14,6 +14,8 @@
 
 BEGIN {
 	public property message => prop_all;
+	public property launder => prop_all;
+	private property _rx => prop_all;
 }
 
 sub CTOR {
@@ -25,13 +27,17 @@
 sub Validate {
 	my ($this,$node,$ctx) = @_;
 	
-	my $rx = $this->nodeValue;
+	my $rx = $this->_rx() || $this->_rx( map qr{$_}, $this->nodeValue );
+	
 	return new IMPL::DOM::Schema::ValidationError(
 		Node => $node,
 		Source => $ctx && $ctx->{Source} || $this->parentNode,
 		Schema => $this->parentNode,
 		Message => $this->message
-	) unless (not $node->isComplex) and $node->nodeValue =~ /$rx/;
+	) unless (not $node->isComplex) and $node->nodeValue =~ /($rx)/;
+	
+	$node->nodeValue($1) if $this->launder;
+	
 	return ();
 }
 
--- a/Lib/IMPL/DOM/Transform.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/DOM/Transform.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -28,6 +28,6 @@
 
 =head1 DESCRIPTION
 
-Преобразование для DOM документа, использует имя документа узла для применения подходящего преобразования.
+Преобразование для DOM документа, использует имя узла для применения подходящего преобразования.
 
 =cut
--- a/Lib/IMPL/DOM/Transform/PostToDOM.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/DOM/Transform/PostToDOM.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -114,11 +114,26 @@
 
 =begin code
 
-my $transform = new IMPL::DOM::Transform::PostToDOM(
-	'My::DOM::Document',
-	IMPL::DOM::Schema->LoadSchema('Data/user.add.schema.xml'),
-	'myForm'
-);
+	my $schema = IMPL::DOM::Schema->LoadSchema('Data/user.add.schema.xml');
+	
+	my $transform = IMPL::DOM::Transform::PostToDOM->new(
+		undef, # default class
+		$schema,
+		$schema->selectSingleNode('ComplexNode')->name
+	);
+	
+	my $doc = $transform->Transform(
+		CGI->new({
+			'user/login' => 'bob',
+			'user/fullName' => 'Bob Marley',
+			'user/password' => 'secret',
+			'user/password_retype' => 'secret',
+			'user/birthday' => '1978-12-17',
+			'user/email[1]' => 'bob@marley.com',
+			'user/email[2]' => 'bob.marley@google.com',
+			process => 1
+		})
+	);
 
 =end code
 
--- a/Lib/IMPL/Object.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/Object.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -25,7 +25,7 @@
 
 =pod
 
-=head1 SYNOPSIS
+=head1 SINOPSYS
 
 =begin code
 
--- a/Lib/IMPL/Object/Autofill.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/Object/Autofill.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -62,7 +62,7 @@
                 } else {
                     my $fld = $prop_info->Implementor->FieldName($prop_info);
                     if ($prop_info->Mutators & prop_list) {
-                        $text .= "\t\$this->{$fld} = ref \$fields->{$name} ? \$fields->{$name} : [\$fields->{$name}] if exists \$fields->{$name};\n";
+                        $text .= "\t\$this->{$fld} = IMPL::Object::List->new ( ref \$fields->{$name} ? \$fields->{$name} : [\$fields->{$name}] ) if exists \$fields->{$name};\n";
                     } else {
                         $text .= "\t\$this->{$fld} = \$fields->{$name} if exists \$fields->{$name};\n";
                     }
--- a/Lib/IMPL/Resources/Format.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/Resources/Format.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -9,6 +9,7 @@
 sub FormatMessage {
     my ($string,$args,$resolver) = @_;
     
+    $args ||= {};
     $resolver ||= \&_defaultResolver;
     
     $string =~ s/%(\w+(?:\.\w+)*)%/_getvalue($args,$1,"\[$1\]",$resolver)/ge;
--- a/Lib/IMPL/Transform.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/Transform.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -73,7 +73,7 @@
 my $obj = new AnyObject;
 
 my $t = new Transform (
-    AnyClass => sub {
+    SomeClass => sub {
         my ($this,$object) = @_;
         return new NewClass({ Name => $object->name, Document => $this->Transform($object->Data) })
     },
--- a/Lib/IMPL/Web/Application/ControllerUnit.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/Web/Application/ControllerUnit.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -72,11 +72,9 @@
 		} elsif (ref $info eq 'HASH') {
 			die new IMPL::Exception("A schema must be specified",$self,$method) unless $info->{schema};
 			
-			$self->class_data(CONTROLLER_METHODS)->{$method} = {
-				wrapper => 'FormWrapper',
-				schema => $info->{schema},
-				form => $info->{form} 
-			};
+			$info->{wrapper} = 'FormWrapper';
+			
+			$self->class_data(CONTROLLER_METHODS)->{$method} = $info;
 		} else {
 			die new IMPL::Exception("Unsupported method information",$self,$method);
 		}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/Web/DOM/FileNode.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -0,0 +1,193 @@
+package IMPL::Web::DOM::FileNode;
+use IMPL::base qw(IMPL::DOM::Node);
+
+__PACKAGE__->PassThroughArgs;
+
+use IMPL::Class::Property;
+use File::Temp qw(tempfile);
+
+BEGIN {
+	public property parameterName => {
+		get => sub {
+			my ($this) = @_;
+			$this->_parameterName() or
+			$this->_parameterName(
+				join '/', ( map {
+					$_->nodeProperty('instanceId') ?
+						$_->nodeName . '['.$_->nodeProperty('instanceId').']':
+						$_->nodeName
+				} $this->_selectParents, $this )
+			);
+		}
+	};
+	private property _parameterName => prop_all;
+	public property fileName => {
+		get => sub {
+			my ($this) = @_;
+			return $this->document->query->param($this->parameterName);
+		}
+	};
+	public property fileHandle => {
+		get => sub {
+			my ($this) = @_;
+			return $this->document->query->upload($this->parameterName);
+		}
+	};
+}
+
+sub invokeTempFile {
+	my ($this,$sub,$target) = @_;
+	
+	die new IMPL::InvalidArgumentException("A reference to a function should be specified") unless $sub && ref $sub eq 'CODE';
+	
+	$target ||= $this;
+	
+	my $query = $this->document->nodeProperty('query') or die new IMPL::InvalidOperationException("Failed to get a CGI query from the document");
+	my $hFile = $query->upload($this->parameterName) or $query->cgi_error ? die new IMPL::IOException("Failed to open the uploaded file",$query->cgi_error) : return;
+			
+	my ($hTemp,$tempFileName) = tempfile();
+	binmode($hTemp);
+	
+	print $hTemp $_ while <$hFile>;
+	
+	$hTemp->flush();
+	seek $hTemp, 0,0;
+	{
+		local $_ = $tempFileName;
+		&$sub($this,$tempFileName,$hTemp);
+	}
+}
+
+sub _selectParents {
+	my ($node) = @_;
+	
+	my @result;
+	
+	unshift @result, $node while $node = $node->parentNode;
+	
+	return @result;
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+C<IMPL::Web::DOM::FileNode> - узел, использующийся для представления параметра запроса в котором передан файл.
+
+=head1 SINOPSYS
+
+=begin code xml
+
+<!-- input.schema.xml -->
+<schema>
+	<SimpleType type="file" nativeType="IMPL::Web::DOM::FileNode"/>
+	<ComplexNode name="user">
+		<Node type="file" name="avatar"/>
+	</ComplexNode>
+</schema>
+
+=end code xml
+
+=begin code
+
+# handle.pl
+use IMPL::DOM::Transform::PostToDOM ();
+use IMPL::DOM::Schema;
+use CGI;
+use File::Copy qw(copy);
+
+my $t = new IMPL::DOM::Transform::PostToDOM(
+	undef,
+	IMPL::DOM::Schema->LoadSchema('input.schema.xml'),
+	'user'	
+);
+
+my $doc = $t->Transform(CGI->new());
+
+if ($t->Errors->Count) {
+	# handle errors	
+}
+
+$doc->selectSingleNode('avatar')->invokeTempFile(
+	sub {
+		my($node,$fname,$fhandle) = @_;
+		
+		# do smth with file
+		copy($_,'avatar.jpg');
+		
+		# same thing
+		# copy($fname,'avatar.jpg');
+	}
+);
+
+=end code
+
+=head1 DESCRIPTION
+
+Данный класс используется для представлении параметров C<CGI> запросов при преобзаовании
+запроса в ДОМ документ преобразованием C<IMPL::DOM::Transform::PostToDOM>.
+
+Узлы данного типа расширяют стандатрный C<IMPL::DOM::Node> несколькими свойствами и
+методами для доступа к файлу, переданному в виде параметра запроса.
+
+=head1 MEMBERS
+
+=head2 PROPERTIES
+
+=over
+
+=item C<[get] parameterName>
+
+Имя параметра C<CGI> запроса соответствующего данному узлу.
+
+=item C<[get] fileName>
+
+Имя файла из параметра запроса
+
+=item C<[get] fileHandle>
+
+Указатель на файл из параметра запроса
+
+=back
+
+=head2 METHODS
+
+=over
+
+=item C<invokeTempFile($callback,$target)>
+
+Сохраняет файл, переданный в запросе во временный, вызывает C<$callback> для обработки временного файла.
+
+=over
+
+=item C<$callback>
+
+Ссылка на функцию которая будет вызвана для обработки временного файла. C<callback($target,$fname,$fhandle)>
+
+=over
+
+=item C<$fname>
+
+Имя временного файла
+
+=item C<$fhandle>
+
+Указатель на временный файл
+
+=back
+	
+Также пременная C<$_> содержит имя временного файла.
+
+=item C<$target>
+
+Значение этого параметра будет передано первым параметром функции C<$callback>.
+
+=back
+
+=back
+
+=cut
\ No newline at end of file
--- a/Lib/IMPL/Web/QueryHandler/UrlController.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/Web/QueryHandler/UrlController.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -5,6 +5,7 @@
 use IMPL::Class::Property;
 use IMPL::Exception;
 use Carp qw(croak);
+use Scalar::Util qw(tainted);
 
 BEGIN {
 	public property namespace => prop_all;
@@ -20,10 +21,18 @@
 	my @target = grep $_, split /\//, ($ENV{PATH_INFO} || '') or die new IMPL::Exception("No target specified");
 	
 	my $method = pop @target;
-	$method =~ s/\.\w+$//;
+	if ( $method =~ /^(\w+)/ ) {
+		$method = $1;
+	} else {
+		die new IMPL::Exception("Invalid method name",$method);
+	}
+	
+	(/^(\w+)$/ or die new IMPL::Exception("Invalid module name part", $_)) and $_=$1 foreach @target;
 	
 	my $module = join '::',$namespace,@target;
 	
+	die new IMPL::Exception("A module name is untrusted", $module) if tainted($module);
+	
 	eval "require $module; 1;" unless eval{ $module->can('InvokeAction'); };
 	if (my $err = $@ ) {
 		die new IMPL::Exception("Failed to load module",$module,$err);
--- a/Lib/IMPL/Web/TT/Form.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/Web/TT/Form.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -4,7 +4,8 @@
 use base qw(IMPL::Web::TT::Control);
 
 use IMPL::Class::Property;
-use IMPL::DOM::Navigator::SchemaNavigator;
+use IMPL::DOM::Navigator::SchemaNavigator();
+
 __PACKAGE__->PassThroughArgs;
 
 BEGIN {
@@ -39,6 +40,97 @@
 	$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) = @_;
 	
@@ -107,9 +199,7 @@
 	}
 }
 
-
 1;
-
 __END__
 
 =pod
--- a/Lib/IMPL/base.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/Lib/IMPL/base.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -14,7 +14,7 @@
 			undef $!;
 			undef $@;
 			$loaded{$baseClass} = 1;
-			eval "require $baseClass;";
+			eval "require $baseClass; 1;";
 			
 			die $@ if $@ and not $!;
 		}
--- a/_test/Test/DOM/Node.pm	Mon Aug 09 08:45:36 2010 +0400
+++ b/_test/Test/DOM/Node.pm	Mon Aug 16 08:26:44 2010 +0400
@@ -45,6 +45,15 @@
     failed "document property returned incorrect value" unless $child->document == $this->Root;
 };
 
+test DocumentCreateNode => sub {
+	my ($this) = @_;
+	
+	my $child = $this->Root->firstChild->appendNode($this->Root->Create(Info => { uuid => '77f9-9a-6d58' } )) or failed "Failed to append a child node";
+    
+    failed "document property is undef" unless $child->document;
+    failed "document property returned incorrect value" unless $child->document == $this->Root;
+};
+
 test MoveNode => sub {
     my ($this) = @_;
     
@@ -94,6 +103,30 @@
     unless @result == 2;
 };
 
+test SelectNodesPath => sub {
+	my ($this) = @_;
+	
+	my @result = $this->Root->selectNodes('Child','Info');
+	
+	failed "Failed to select a node by path 'Child/Info'" unless @result;
+};
+
+test SelectByAxisDescendant => sub {
+	my ($this) = @_;
+	
+	my @result = $this->Root->selectNodes( { descendant => ['GrandChild','Info']} );
+	
+	failed "Failed to select a node by path '//(GrandChild|Info)/'" unless @result == 2;
+};
+
+test SelectByAxisAncestor => sub {
+	my ($this) = @_;
+	
+	my @result = $this->Root->selectSingleNode( { descendant => 'Info'} )->selectNodes( { ancestor => undef } ) ;
+	
+	failed "Failed to select a node by path '//Info/ancestor:*'" unless @result == 2;
+};
+
 test CheckNodesValues => sub {
     my ($this) = @_;
     
@@ -118,7 +151,7 @@
     my ($this) = @_;
     
     failed "property isComplex returned false for the root node" unless $this->Root->isComplex;
-    failed "property isComplex returned true for a simple node", $this->Root->firstChild->nodeName if $this->Root->firstChild->isComplex;
+    failed "property isComplex returned true for a simple node", $this->Root->selectSingleNode('Item')->childNodes->Count if $this->Root->selectSingleNode('Item')->isComplex;
 };
 
 test setObjectProperty => sub {