diff Lib/IMPL/DOM/Node.pm @ 18:818c74b038ae

DOM Schema + tests
author Sergey
date Thu, 10 Sep 2009 17:42:47 +0400
parents 75d55f4ee263
children fafe56cfcd69
line wrap: on
line diff
--- a/Lib/IMPL/DOM/Node.pm	Wed Sep 09 17:43:31 2009 +0400
+++ b/Lib/IMPL/DOM/Node.pm	Thu Sep 10 17:42:47 2009 +0400
@@ -17,13 +17,15 @@
     public _direct property nodeValue => prop_all;
     public _direct property childNodes => { get => \&_getChildNodes };
     public _direct property parentNode => prop_get ;
-    private _direct property _propertyMap => prop_get ;
+    public _direct property rootNode => { get => \&_getRootNode};
+    private _direct property _propertyMap => prop_all ;
 }
 
 sub CTOR {
     my ($this,%args) = @_;
     
     $this->nodeName($args{nodeName}) or die new IMPL::InvalidArgumentException("A name is required");
+    $this->nodeValue($args{nodeValue});
 }
 
 sub insertNode {
@@ -47,7 +49,8 @@
     
     $node->{$parentNode}->removeNode($node) if ($node->{$parentNode});
     
-    $this->childNodes->Append($node);
+    my $children = $this->childNodes;
+    $children->Append($node);
     
     $node->_setParent( $this );
     
@@ -73,7 +76,7 @@
     my ($this) = @_;
     
     $this->{$childNodes} = new IMPL::Object::List() unless $this->{$childNodes};
-    $this->{$childNodes};
+    return $this->{$childNodes};
 }
 
 sub removeNode {
@@ -81,8 +84,8 @@
     
     if ($this == $node->{$parentNode}) {
         $this->childNodes->RemoveItem($node);
-        $node->{$parentNode} = undef;
-        return $this;
+        $node->_setParent(undef);
+        return $node;
     } else {
         die new IMPL::InvalidOperationException("The specified node isn't belong to this node");
     }
@@ -101,11 +104,11 @@
     # replace (or set) old node
     $this->childNodes->[$index] = $node;
         
-    # save new parent
+    # set new parent
     $node->_setParent( $this );
         
     # unlink old node if we have one
-    $nodeOld->{$parentNode} = undef if $nodeOld;
+    $nodeOld->_setParent(undef) if $nodeOld;
         
     # return old node
     return $nodeOld;
@@ -115,21 +118,64 @@
     my ($this,$pos) = @_;
     
     if ( my $node = $this->childNodes->RemoveAt($pos) ) {
-        $node->{$parentNode} = undef;
+        $node->_setParent(undef);
+        return $node;
+    } else {
+        return undef;
+    }
+}
+
+sub removeLast {
+    my ($this) = @_;
+    
+    if ( my $node = $this->{$childNodes} ? $this->{$childNodes}->RemoveLast() : undef) {
+        $node->_setParent(undef);
         return $node;
     } else {
         return undef;
     }
 }
 
+sub removeSelected {
+    my ($this,$query) = @_;
+    
+    my @newSet;
+    my @result;
+    
+    if (ref $query eq 'CODE') {
+        &$query($_) ? push @result, $_ : push @newSet, $_ foreach @{$this->childNodes};
+    } elsif (defined $query) {
+        $_->nodeName eq $query ? push @result, $_ : push @newSet, $_ foreach @{$this->childNodes};
+    } else {
+        my $children = $this->childNodes;
+        $_->_setParent(undef) foreach @$children;
+        delete $this->{$childNodes};
+        return wantarray ? @$children : $children;
+    }
+    
+    $_->_setParent(undef) foreach @result;
+    
+    $this->{$childNodes} = @newSet ? bless \@newSet ,'IMPL::Object::List' : undef;
+    
+    return wantarray ? @result : \@result;
+}
+
 sub selectNodes {
     my ($this,$query) = @_;
+    
     my @result;
     
     if (ref $query eq 'CODE') {
         @result = grep &$query($_), @{$this->childNodes};
+    } elsif (defined $query) {
+        @result = grep $_->nodeName eq $query, @{$this->childNodes};
     } else {
-        @result = grep $_->nodeName eq $query, @{$this->childNodes};
+        if (wantarray) {
+            return @{$this->childNodes};
+        } else {
+            @result = $this->childNodes;
+            return \@result;
+        }
     }
     
     return wantarray ? @result : \@result;
@@ -143,18 +189,49 @@
     $_[0]->childNodes->Count ? 1 : 0;
 }
 
+sub _getRootNode {
+    $_[0]->{$rootNode} || $_[0];
+}
+
+sub _updateRootRefs {
+    my ($this) = @_;
+    
+    if ( my $newRoot = $this->{$parentNode} ? $this->{$parentNode}->rootNode : undef) {
+        if ($this->{$rootNode} ? $this->{$rootNode} != $newRoot : 1 ) {
+            $this->{$rootNode} = $newRoot;
+            weaken($this->{$rootNode});
+            if ($this->{$childNodes}) {
+                $_->_updateRootRefs foreach @{$this->{$childNodes}};
+            }
+        }
+    } elsif($this->{$rootNode}) {
+        delete $this->{$rootNode};
+        if ($this->{$childNodes}) {
+            $_->_updateRootRefs foreach @{$this->{$childNodes}};
+        }
+    }
+}
+
 sub _setParent {
     my ($this,$node) = @_;
     
-    $this->{$parentNode} = $node;
-    # prevent from creating cyclicreferences
-    weaken($this->{$parentNode});
+   
+    if (($node || 0) != ($this->{$parentNode} || 0)) {
+        if ($node) {
+            $this->{$parentNode} = $node;
+            # prevent from creating cyclicreferences
+            weaken($this->{$parentNode});
+        } else {
+            delete $this->{$parentNode};
+        }
+        $this->_updateRootRefs;
+    }
 }
 
 sub text {
     my ($this) = @_;
     
-    join '', $this->nodeValue, map $_->nodeValue, @{$this->childNodes};
+    join '', $this->nodeValue || '', map $_->nodeValue || '', @{$this->childNodes};
 }
 
 sub Property {