| 3 | 
 #  $Id$ | 
 #  $Id$ | 
| 4 | 
 # | 
 # | 
| 5 | 
 #  $Log$ | 
 #  $Log$ | 
| 6 | 
  | 
 #  Revision 1.36  2003/05/10 17:31:18  jonen | 
| 7 | 
  | 
 #  + added new functions related to 'create' item | 
| 8 | 
  | 
 #   - createNode() | 
| 9 | 
  | 
 #     # creates non-persistent 'deep dummy filled' object | 
| 10 | 
  | 
 #   - | 
| 11 | 
  | 
 # | 
| 12 | 
  | 
 #  Revision 1.35  2003/04/19 16:09:48  jonen | 
| 13 | 
  | 
 #  + added operator dispatching (currently for getting ref-type) at 'getListFiltered' | 
| 14 | 
  | 
 # | 
| 15 | 
  | 
 #  Revision 1.34  2003/04/11 01:18:53  joko | 
| 16 | 
  | 
 #  sendQuery: | 
| 17 | 
  | 
 #  + introduced crud action 'DELETE' | 
| 18 | 
  | 
 # | 
| 19 | 
  | 
 #  Revision 1.33  2003/04/09 06:07:43  joko | 
| 20 | 
  | 
 #  revamped 'sub sendQuery' | 
| 21 | 
  | 
 # | 
| 22 | 
 #  Revision 1.32  2003/04/08 22:52:22  joko | 
 #  Revision 1.32  2003/04/08 22:52:22  joko | 
| 23 | 
 #  modified 'querySchema': better behaviour regarding filtering result | 
 #  modified 'querySchema': better behaviour regarding filtering result | 
| 24 | 
 # | 
 # | 
| 153 | 
 use DesignPattern::Object; | 
 use DesignPattern::Object; | 
| 154 | 
 use Data::Storage::Result::Tangram; | 
 use Data::Storage::Result::Tangram; | 
| 155 | 
 use Data::Mungle::Compare::Struct qw( isEmpty ); | 
 use Data::Mungle::Compare::Struct qw( isEmpty ); | 
| 156 | 
 use Data::Mungle::Transform::Deep qw( expand ); | 
 use Data::Mungle::Transform::Deep qw( expand deep_copy merge_to ); | 
| 157 | 
  | 
  | 
| 158 | 
 # get logger instance | 
 # get logger instance | 
| 159 | 
 my $logger = Log::Dispatch::Config->instance; | 
 my $logger = Log::Dispatch::Config->instance; | 
| 515 | 
   #    objects => $objects, | 
   #    objects => $objects, | 
| 516 | 
   #  ); | 
   #  ); | 
| 517 | 
    | 
    | 
| 518 | 
     # HACK: build eval-string (sorry) to get filtered list - please give advice here | 
     # TODO: is_op? | 
| 519 | 
     push @tfilters, '$remote->{' . $filter->{key} . '}' . " $filter->{op} '$filter->{val}'"; | 
     # dispatch un-common operators if exists | 
| 520 | 
  | 
     if($filter->{op} eq "ref") { | 
| 521 | 
  | 
       push @tfilters, 'ref($remote->{' . $filter->{key} . '})' . " eq '$filter->{val}'"; | 
| 522 | 
  | 
     } else { | 
| 523 | 
  | 
       # HACK: build eval-string (sorry) to get filtered list - please give advice here | 
| 524 | 
  | 
       push @tfilters, '$remote->{' . $filter->{key} . '}' . " $filter->{op} '$filter->{val}'"; | 
| 525 | 
  | 
     } | 
| 526 | 
  | 
  | 
| 527 | 
   } | 
   } | 
| 528 | 
  | 
  | 
| 575 | 
 sub sendQuery { | 
 sub sendQuery { | 
| 576 | 
   my $self = shift; | 
   my $self = shift; | 
| 577 | 
   my $query = shift; | 
   my $query = shift; | 
 | 
   #my $sql = "SELECT cs FROM $self->{metainfo}->{$descent}->{node} WHERE $self->{metainfo}->{$descent}->{IdentProvider}->{arg}='$self->{entry}->{source}->{ident}';"; | 
  | 
 | 
   #my $result = $self->{metainfo}->{$descent}->{storage}->sendCommand($sql); | 
  | 
| 578 | 
  | 
  | 
| 579 | 
   #print Dumper($query); | 
   #print Dumper($query); | 
| 580 | 
  | 
  | 
| 581 | 
   # HACK: special case: querying by id does not translate into a common tangram query | 
   # type = ITEM|LIST|TRANSPARENT | 
| 582 | 
   # just load the object by given id(ent) | 
   my $type = ''; | 
| 583 | 
   if ($query->{criterias} && ($query->{criterias}->[0]->{key} eq 'id' && $query->{criterias}->[0]->{op} eq 'eq')) { | 
   # mode = OID|SPECIAL | 
| 584 | 
     #print "LOAD!!!", "\n"; | 
   my $mode = ''; | 
| 585 | 
     #exit; | 
   my $ident = ''; | 
| 586 | 
     #return Set::Object->new( $self->{COREHANDLE}->load($query->{criterias}->[0]->{val}) ); | 
   my $crud = ''; | 
| 587 | 
     my $ident = $query->{criterias}->[0]->{val}; | 
    | 
| 588 | 
 #print "load obj", "\n"; | 
    | 
| 589 | 
     #return $self->createSet() if $ident == 5; | 
   # dispatch type and mode | 
| 590 | 
     $self->{_COREHANDLE}->unload($ident); | 
    | 
| 591 | 
     my $object = $self->{_COREHANDLE}->load($ident); | 
     # defaults - 1 | 
| 592 | 
 #print "get id", "\n"; | 
     if ($query->{options}) { | 
| 593 | 
     my $oid = $self->{_COREHANDLE}->id($object); | 
       $crud = $query->{options}->{crud}; | 
| 594 | 
 #print Dumper($object); | 
       $crud ||= $query->{options}->{action}; | 
| 595 | 
 #print "oid: $oid", "\n"; | 
     } | 
| 596 | 
     return $self->createSet($object); | 
  | 
| 597 | 
     #return $self->createSet( $self->{COREHANDLE}->load('300090018') ); | 
     # defaults - 2 | 
| 598 | 
   } | 
     $type ||= 'TRANSPARENT';  | 
| 599 | 
  | 
     $crud ||= 'RETRIEVE'; | 
| 600 | 
   my $list = $self->getListFiltered($query->{node}, $query->{criterias}); | 
  | 
| 601 | 
   #return $self->createSet($object); | 
     if ($query->{options}->{OID}) { | 
| 602 | 
   #return $self->createSet($list); | 
       $type = 'ITEM'; | 
| 603 | 
   return $self->createSet(@$list); | 
       $mode = 'OID'; | 
| 604 | 
  | 
       $ident = $query->{options}->{OID}; | 
| 605 | 
   #die("This should not be reached for now - redirect to \$self->getListFiltered() here!"); | 
      | 
| 606 | 
  | 
     } elsif (my $guid = $query->{options}->{GUID}) { | 
| 607 | 
  | 
       $type = 'TRANSPARENT'; | 
| 608 | 
  | 
       $query->{criterias} = [ { key => 'guid', op => 'eq', val => $guid } ];     | 
| 609 | 
  | 
  | 
| 610 | 
  | 
     # if operator is different (dispatcher for 'getListFiltered') | 
| 611 | 
  | 
     } elsif (my $op = $query->{options}->{op}) { | 
| 612 | 
  | 
       $type = 'TRANSPARENT'; | 
| 613 | 
  | 
       $query->{criterias} = [ { key => $query->{options}->{meta_label}, op => $op, val => $query->{options}->{meta_value} } ];     | 
| 614 | 
  | 
  | 
| 615 | 
  | 
     } | 
| 616 | 
  | 
    | 
| 617 | 
  | 
     # HACK: special case: querying by id does not translate into a common tangram query | 
| 618 | 
  | 
     # just load the object by given identifier (OID) named 'id' - this is required by Data::Transfer::Sync! | 
| 619 | 
  | 
     if ($query->{criterias} && ($query->{criterias}->[0]->{key} eq 'id' && $query->{criterias}->[0]->{op} eq 'eq')) { | 
| 620 | 
  | 
       $type = 'ITEM'; | 
| 621 | 
  | 
       $mode = 'SPECIAL.SYNC'; | 
| 622 | 
  | 
       $ident = $query->{criterias}->[0]->{val}; | 
| 623 | 
  | 
     } | 
| 624 | 
  | 
    | 
| 625 | 
  | 
  | 
| 626 | 
  | 
   # execute query | 
| 627 | 
  | 
   my $result; | 
| 628 | 
  | 
  | 
| 629 | 
  | 
   if ($type eq 'ITEM' && $ident) { | 
| 630 | 
  | 
  | 
| 631 | 
  | 
     if ($mode eq 'OID') {     | 
| 632 | 
  | 
       # TODO: review this case! | 
| 633 | 
  | 
       $result = $self->getObject($ident, $query->{options}); | 
| 634 | 
  | 
      | 
| 635 | 
  | 
     } elsif ($mode eq 'SPECIAL.SYNC') { | 
| 636 | 
  | 
  | 
| 637 | 
  | 
       # V1 - failed | 
| 638 | 
  | 
       #return Set::Object->new( $self->{COREHANDLE}->load($query->{criterias}->[0]->{val}) ); | 
| 639 | 
  | 
    | 
| 640 | 
  | 
       # hmm.... | 
| 641 | 
  | 
       #return $self->createSet() if $ident == 5; | 
| 642 | 
  | 
        | 
| 643 | 
  | 
       # Unload single object before doing any further operations to  | 
| 644 | 
  | 
       # expect a "fresh" object from orm when performing the next calls. | 
| 645 | 
  | 
       $self->{_COREHANDLE}->unload($ident); | 
| 646 | 
  | 
        | 
| 647 | 
  | 
       # Load object from orm. | 
| 648 | 
  | 
       my $object = $self->{_COREHANDLE}->load($ident); | 
| 649 | 
  | 
    | 
| 650 | 
  | 
       # determine object identifier (OID) | 
| 651 | 
  | 
       my $oid = $self->{_COREHANDLE}->id($object); | 
| 652 | 
  | 
        | 
| 653 | 
  | 
       # encapsulate into result/response container and return this one | 
| 654 | 
  | 
       $result = $self->createSet($object); | 
| 655 | 
  | 
        | 
| 656 | 
  | 
       # debugging | 
| 657 | 
  | 
       #$result = $self->createSet( $self->{COREHANDLE}->load('300090018') ); | 
| 658 | 
  | 
  | 
| 659 | 
  | 
     } | 
| 660 | 
  | 
      | 
| 661 | 
  | 
    | 
| 662 | 
  | 
   } elsif ($type eq 'TRANSPARENT') { | 
| 663 | 
  | 
  | 
| 664 | 
  | 
     if ($crud eq 'RETRIEVE') { | 
| 665 | 
  | 
  | 
| 666 | 
  | 
       my $list = $self->getListFiltered($query->{node}, $query->{criterias}); | 
| 667 | 
  | 
       #return $self->createSet($object); | 
| 668 | 
  | 
       #return $self->createSet($list); | 
| 669 | 
  | 
       return $self->createSet(@$list); | 
| 670 | 
  | 
      | 
| 671 | 
  | 
       #die("This should not be reached for now - redirect to \$self->getListFiltered() here!"); | 
| 672 | 
  | 
      | 
| 673 | 
  | 
       # try a generic tangram query here | 
| 674 | 
  | 
       # TODO: try to place an oql on top of that (search.cpan.org!) | 
| 675 | 
  | 
       my @crits; | 
| 676 | 
  | 
       foreach (@{$query->{criterias}}) { | 
| 677 | 
  | 
         my $op = ''; | 
| 678 | 
  | 
         $op = '=' if lc $_->{op} eq 'eq'; | 
| 679 | 
  | 
         push @crits, "$_->{key}$op'$_->{val}'"; | 
| 680 | 
  | 
       } | 
| 681 | 
  | 
       my $subnodes = {}; | 
| 682 | 
  | 
       map { $subnodes->{$_}++ } @{$query->{subnodes}}; | 
| 683 | 
  | 
       # HACK: this is hardcoded ;(    expand possibilities! | 
| 684 | 
  | 
       #my $crit = join(' AND ', @crits); | 
| 685 | 
  | 
       #my $sql = hash2Sql($query->{node}, $subnodes, 'SELECT', $crit); | 
| 686 | 
  | 
       #return $self->sendCommand($sql); | 
| 687 | 
  | 
       #my $h = $self->{COREHANDLE}->remote($query->{node}); | 
| 688 | 
  | 
       #my $res = $self->{COREHANDLE}->select($h, $h->{); | 
| 689 | 
  | 
       $result = $self->createCursor($query->{node}); | 
| 690 | 
  | 
  | 
| 691 | 
  | 
     } elsif ($crud eq 'UPDATE') { | 
| 692 | 
  | 
  | 
| 693 | 
  | 
       # Patch current query to be a loader (e.g. change action, remove payload) ... | 
| 694 | 
  | 
       my $childquery = deep_copy($query); | 
| 695 | 
  | 
       $childquery->{options}->{crud} = 'RETRIEVE'; | 
| 696 | 
  | 
       delete $childquery->{payload}; | 
| 697 | 
  | 
  | 
| 698 | 
  | 
       # ... to use it to fetch a fresh object using ourselves (sendQuery). | 
| 699 | 
  | 
       my $cursor = $self->sendQuery($childquery); | 
| 700 | 
  | 
       my $status = $cursor->getStatus(); | 
| 701 | 
  | 
       my $object = $cursor->getNextEntry(); | 
| 702 | 
  | 
  | 
| 703 | 
  | 
       # Merge values and apply value modifiers. | 
| 704 | 
  | 
       my $options = { utf8 => 1, php => 1 }; | 
| 705 | 
  | 
       merge_to($object, $query->{payload}, $options); | 
| 706 | 
  | 
  | 
| 707 | 
  | 
       # Execute update operation at orm. | 
| 708 | 
  | 
       $self->update($object); | 
| 709 | 
  | 
      | 
| 710 | 
  | 
     } elsif ($crud eq 'DELETE') { | 
| 711 | 
  | 
  | 
| 712 | 
  | 
       # Patch current query to be a loader (e.g. change action) ... | 
| 713 | 
  | 
       my $childquery = deep_copy($query); | 
| 714 | 
  | 
       $childquery->{options}->{crud} = 'RETRIEVE'; | 
| 715 | 
  | 
  | 
| 716 | 
  | 
       # ... to use it to fetch a fresh object using ourselves (sendQuery). | 
| 717 | 
  | 
       my $cursor = $self->sendQuery($childquery); | 
| 718 | 
  | 
       my $status = $cursor->getStatus(); | 
| 719 | 
  | 
       my $object = $cursor->getNextEntry(); | 
| 720 | 
  | 
  | 
| 721 | 
  | 
       $self->erase($object); | 
| 722 | 
  | 
  | 
| 723 | 
  | 
     } elsif ($crud eq 'CREATE') { | 
| 724 | 
  | 
        | 
| 725 | 
  | 
       my $nodename = $query->{node};       | 
| 726 | 
  | 
       my $newnode = $self->createNode($nodename); | 
| 727 | 
  | 
       my $id = $self->{_COREHANDLE}->insert($newnode); | 
| 728 | 
  | 
    | 
| 729 | 
  | 
       print "Saved new node $nodename with GUID $newnode->{guid}, OID '$id': " . Dumper($newnode) . "\n"; | 
| 730 | 
  | 
        | 
| 731 | 
  | 
       return $newnode; | 
| 732 | 
  | 
  | 
| 733 | 
  | 
     } | 
| 734 | 
  | 
  | 
| 735 | 
  | 
   } | 
| 736 | 
  | 
  | 
| 737 | 
  | 
   return $result; | 
| 738 | 
  | 
  | 
 | 
   # try a generic tangram query here | 
  | 
 | 
   # TODO: try to place an oql on top of that (search.cpan.org!) | 
  | 
 | 
   my @crits; | 
  | 
 | 
   foreach (@{$query->{criterias}}) { | 
  | 
 | 
     my $op = ''; | 
  | 
 | 
     $op = '=' if lc $_->{op} eq 'eq'; | 
  | 
 | 
     push @crits, "$_->{key}$op'$_->{val}'"; | 
  | 
 | 
   } | 
  | 
 | 
   my $subnodes = {}; | 
  | 
 | 
   map { $subnodes->{$_}++ } @{$query->{subnodes}}; | 
  | 
 | 
   # HACK: this is hardcoded ;(    expand possibilities! | 
  | 
 | 
   #my $crit = join(' AND ', @crits); | 
  | 
 | 
   #my $sql = hash2Sql($query->{node}, $subnodes, 'SELECT', $crit); | 
  | 
 | 
   #return $self->sendCommand($sql); | 
  | 
 | 
   #my $h = $self->{COREHANDLE}->remote($query->{node}); | 
  | 
 | 
   #my $res = $self->{COREHANDLE}->select($h, $h->{); | 
  | 
 | 
   return $self->createCursor($query->{node}); | 
  | 
| 739 | 
 } | 
 } | 
| 740 | 
  | 
  | 
| 741 | 
 sub eraseAll { | 
 sub eraseAll { | 
| 771 | 
   return $object if $object; | 
   return $object if $object; | 
| 772 | 
 } | 
 } | 
| 773 | 
  | 
  | 
| 774 | 
 sub getObjectByGuid { | 
 sub getObjectByGuid_old { | 
| 775 | 
   my $self = shift; | 
   my $self = shift; | 
| 776 | 
   my $guid = shift; | 
   my $guid = shift; | 
| 777 | 
   my $options = shift; | 
   my $options = shift; | 
| 801 | 
    | 
    | 
| 802 | 
 } | 
 } | 
| 803 | 
  | 
  | 
| 804 | 
 sub getObjectAsHash { | 
 sub getObjectAsHash_old { | 
| 805 | 
   my $self = shift; | 
   my $self = shift; | 
| 806 | 
   my $oid = shift; | 
   my $oid = shift; | 
| 807 | 
   my $options = shift;   | 
   my $options = shift;   | 
| 884 | 
   $self->{dataStorageLayer}->disconnect(); | 
   $self->{dataStorageLayer}->disconnect(); | 
| 885 | 
 } | 
 } | 
| 886 | 
  | 
  | 
| 887 | 
  | 
  | 
| 888 | 
  | 
 sub createNode { | 
| 889 | 
  | 
   my $self = shift; | 
| 890 | 
  | 
   my $classname = shift; | 
| 891 | 
  | 
  | 
| 892 | 
  | 
   my $obj = $classname->new(); | 
| 893 | 
  | 
    | 
| 894 | 
  | 
   my $attr_options = Class::Tangram::attribute_options($classname); | 
| 895 | 
  | 
   #print "Attribute Options: " . Dumper($attr_options); | 
| 896 | 
  | 
  | 
| 897 | 
  | 
   my $attr_types = Class::Tangram::attribute_types($classname); | 
| 898 | 
  | 
   #print "Attribute Types: " . Dumper($attr_types); | 
| 899 | 
  | 
    | 
| 900 | 
  | 
   foreach(keys %{$attr_types}) { | 
| 901 | 
  | 
     if($attr_types->{$_} eq 'string') { | 
| 902 | 
  | 
       $obj->{$_} = ''; | 
| 903 | 
  | 
     } elsif($attr_types->{$_} eq 'int') { | 
| 904 | 
  | 
       $obj->{$_} = 0; | 
| 905 | 
  | 
     } elsif($attr_types->{$_} eq 'real') { | 
| 906 | 
  | 
       $obj->{$_} = 0; | 
| 907 | 
  | 
     } elsif($attr_types->{$_} eq 'rawdatetime') { | 
| 908 | 
  | 
       $obj->{$_} = '0000-00-00 00:00:00'; | 
| 909 | 
  | 
     } elsif($attr_types->{$_} eq 'ref') { | 
| 910 | 
  | 
       if($attr_options->{$_}->{class}) { | 
| 911 | 
  | 
         $obj->{$_} = $self->createNode($attr_options->{$_}->{class}); | 
| 912 | 
  | 
       } else { | 
| 913 | 
  | 
         #$obj->{$_} = undef(); | 
| 914 | 
  | 
       } | 
| 915 | 
  | 
     } elsif($attr_types->{$_} eq 'iarray') { | 
| 916 | 
  | 
         $obj->{$_} = [ ]; | 
| 917 | 
  | 
     } elsif($attr_types->{$_} eq 'hash') { | 
| 918 | 
  | 
         $obj->{$_} = {  }; | 
| 919 | 
  | 
     } elsif($attr_types->{$_} eq 'flat_hash') { | 
| 920 | 
  | 
         $obj->{$_} = { }; | 
| 921 | 
  | 
     } | 
| 922 | 
  | 
   } | 
| 923 | 
  | 
    | 
| 924 | 
  | 
   #print "New Object: " . Dumper($obj); | 
| 925 | 
  | 
    | 
| 926 | 
  | 
   return $obj; | 
| 927 | 
  | 
 } | 
| 928 | 
  | 
  | 
| 929 | 
  | 
  | 
| 930 | 
  | 
   sub insertChildNode { | 
| 931 | 
  | 
     my $self = shift; | 
| 932 | 
  | 
     my $child_entry = shift; | 
| 933 | 
  | 
     my $query_args = shift; | 
| 934 | 
  | 
      | 
| 935 | 
  | 
     my $core = $self->{_COREHANDLE}; | 
| 936 | 
  | 
     my $nodename = $query_args->{nodename}; | 
| 937 | 
  | 
      | 
| 938 | 
  | 
     # get parent object | 
| 939 | 
  | 
     my $query = { | 
| 940 | 
  | 
           node => $query_args->{parent}->{nodename}, | 
| 941 | 
  | 
           options => { GUID => $query_args->{parent}->{guid}, }, | 
| 942 | 
  | 
           }; | 
| 943 | 
  | 
     my $cursor = $self->sendQuery($query); | 
| 944 | 
  | 
     my $parent = $cursor->getNextEntry(); | 
| 945 | 
  | 
  | 
| 946 | 
  | 
     # debug | 
| 947 | 
  | 
     #print "Parent_org: " . Dumper($parent); | 
| 948 | 
  | 
          | 
| 949 | 
  | 
     # Create child node object if isn't already done  | 
| 950 | 
  | 
     # ($child_entry have to be the class name then...) | 
| 951 | 
  | 
     if(!ref($child_entry)) { | 
| 952 | 
  | 
       $child_entry = $self->createNode($child_entry); | 
| 953 | 
  | 
       # it could be insert 'manually' or will be insert 'transparent' if parent will be updated | 
| 954 | 
  | 
       #$core->insert($child_entry); | 
| 955 | 
  | 
       #print "Create child object [$nodename]: " . Dumper($child_entry); | 
| 956 | 
  | 
     } | 
| 957 | 
  | 
  | 
| 958 | 
  | 
     # get reference of tied node | 
| 959 | 
  | 
     my $tied_node = tied $parent->{$nodename}; | 
| 960 | 
  | 
  | 
| 961 | 
  | 
     # insert/change child entry at parent | 
| 962 | 
  | 
     #print "reference: " . ref($parent->{$nodename}) . "\n"; | 
| 963 | 
  | 
     if(ref($parent->{$nodename}) eq 'ARRAY') { | 
| 964 | 
  | 
       # all tangram types are tied as 'SCALAR' with special 'FETCH', 'STORE' methods per type, | 
| 965 | 
  | 
       # so a 'PUSH' is not implemented (which could be then done transparently) | 
| 966 | 
  | 
       my $array = $tied_node->FETCH; | 
| 967 | 
  | 
       push @$array, $child_entry; | 
| 968 | 
  | 
       $tied_node->STORE($array); | 
| 969 | 
  | 
       # node will be normaly untied at 'STORE' | 
| 970 | 
  | 
       if(tied $parent->{$nodename}) { print "already tied !!\n"; } | 
| 971 | 
  | 
       else { undef $tied_node; } | 
| 972 | 
  | 
     } | 
| 973 | 
  | 
     elsif(ref($parent->{$nodename}) eq 'HASH') { | 
| 974 | 
  | 
       if(my $key = $query_args->{hash_key}) { | 
| 975 | 
  | 
         # same problem as with 'ARRAY': | 
| 976 | 
  | 
         # all tangram types are tied as 'SCALAR' with special 'FETCH', 'STORE' methods per type. | 
| 977 | 
  | 
         my $hash = $tied_node->FETCH; | 
| 978 | 
  | 
         $hash->{$key} = $child_entry; | 
| 979 | 
  | 
         $tied_node->STORE($hash);  | 
| 980 | 
  | 
         # node will be normaly untied at 'STORE' | 
| 981 | 
  | 
         if(tied $parent->{$nodename}) { print "already tied !!\n"; } | 
| 982 | 
  | 
         else { undef $tied_node; } | 
| 983 | 
  | 
       } else { | 
| 984 | 
  | 
        print "ERROR: No HASH KEY given, so not able to insert hash entry!"; | 
| 985 | 
  | 
       } | 
| 986 | 
  | 
     }  | 
| 987 | 
  | 
     else { | 
| 988 | 
  | 
       $parent->{$nodename} = $child_entry; | 
| 989 | 
  | 
     } | 
| 990 | 
  | 
  | 
| 991 | 
  | 
     # debug | 
| 992 | 
  | 
     #print "Parent_new: " . Dumper($parent); | 
| 993 | 
  | 
      | 
| 994 | 
  | 
     # save parent | 
| 995 | 
  | 
     $core->update($parent); | 
| 996 | 
  | 
      | 
| 997 | 
  | 
     # debug | 
| 998 | 
  | 
     #print "Saved Parent: ". Dumper($parent); | 
| 999 | 
  | 
      | 
| 1000 | 
  | 
     return $child_entry; | 
| 1001 | 
  | 
   } | 
| 1002 | 
  | 
  | 
| 1003 | 
  | 
  | 
| 1004 | 
 1; | 
 1; | 
| 1005 | 
 __END__ | 
 __END__ |