perfSONAR_PS::DataModels::APIBuilder - builder utils to build binding perl objects collection


perfSONAR_PS-Services-PingER documentation Contained in the perfSONAR_PS-Services-PingER distribution.

Index


Code Index:

NAME

Top

 perfSONAR_PS::DataModels::APIBuilder - builder utils to build binding perl objects collection

DESCRIPTION

Top

      single call is here with several private ones
      the public call is:
      buildAPI(<top object name>, <top object>, <path>,<API root name>);

SYNOPSIS

Top

      ###
      use   perfSONAR_PS::DataModels::DataModel  qw($message); 
      use  perfSONAR_PS::DataModels::APIBuilderqw(&buildAPI  $API_ROOT $TOP_DIR $DATATYPES_ROOT) ;

      $API_ROOT = 'perfSONAR_PS';
      $TOP_DIR = "/tmp/API/" .$API_ROOT;   
      $DATATYPES_ROOT = 'Datatypes';

      buildAPI('message', $message, '','' );
      ####

API

Top

Exported variables

  $API_ROOT  - name of the  API  ( empty string by default)
  $TOP_DIR  - top dirname of the API location( /tmp/API by default)
  $DATATYPES_ROOT - dirname for schema datamodel files

buildAPI

   builds the whole API recursively

   accepts  four parameters
   - name of the root element - message by default
   - top hashref ( object to be built) 
   -   path ( empty by default )
   -   root API name ( empty by default )  

    


buildClass

   builds  single class on the filesystem and corresponded test file

   accepts  four parameters
     -   full  path to the class ( except for .pm extension)
     -   full  package  name  
     -   name of the   element 
     -   hashref with the element definition  
     -   hashref with parent definition if its not the root element


perfSONAR_PS-Services-PingER documentation Contained in the perfSONAR_PS-Services-PingER distribution.
package  perfSONAR_PS::DataModels::APIBuilder;
 


 use strict;
 use warnings;
  
 use IO::File;
 use File::Path;
 use Data::Dumper;
 use Log::Log4perl qw(get_logger);  
BEGIN {
 use Exporter ();
 our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS);
 use version; our $VERSION = qv('2.0');
        %EXPORT_TAGS = ();
        use base qw(Exporter);
        @EXPORT_OK     = qw( );
        @EXPORT_OK  =qw(  &buildAPI  &buildClass  $DATATYPES_ROOT $API_ROOT  $TOP_DIR $SCHEMA_VERSION $TEST_DIR);
}
our @EXPORT_OK;
our ( $API_ROOT,  $TOP_DIR, $DATATYPES_ROOT, $SCHEMA_VERSION, $TEST_DIR) = ('', '/tmp/API', 'Datatypes',  APIBuilder->VERSION, "$TOP_DIR/../");  
my %known_classes = ();
my %existed = (); 
my $logger = get_logger(  "APIBuilder" ); 
 
#
# prints second parameter ( string)  to multiple filehandles passed as arrayref 
#

sub printMulti {
     my ($fharr, $msg) = @_;
     foreach my $fh (@{$fharr}) {
        print $fh $msg;
     }
}
#
#  for new classname, path, root and ns will check if this package already exists and
#  then update path and root with appended classname and return root and path
#  where root is the API modules tree path and path is the directory pathname
#  without top dir name 
#
sub  _makeAPIPath {
     my ($classname, $path, $root,  $ns) = @_;
     my $classnameUP = ucfirst($classname);
     print "ROOT=   $API_ROOT\:\:$DATATYPES_ROOT\:\:$SCHEMA_VERSION\:\:$ns$root\:\:$classnameUP\n";
     unless ( $existed{"$API_ROOT\:\:$DATATYPES_ROOT\:\:$SCHEMA_VERSION\:\:$ns$root\:\:$classnameUP"} ) {
       $path  .= "/$classnameUP";
       $root  .=   "\:\:$classnameUP";
       $existed{ "$API_ROOT\:\:$DATATYPES_ROOT\:\:$SCHEMA_VERSION\:\:$ns$root" } =  $classname;
       $known_classes{$classname}{$ns} =   "$API_ROOT\:\:$DATATYPES_ROOT\:\:$SCHEMA_VERSION\:\:$ns$root"  ;
      }
      return ($root,  $path);  
   
}

 
sub buildAPI {
       my ($name, $element, $path, $root, $parent ) = @_;
       my $ns = $element->{attrs}->{xmlns};
      
       ($root, $path) = _makeAPIPath($name, $path, $root,    $ns );
       
       if( $element && ref($element) eq 'HASH' &&  $element->{attrs} )  {
         
       if (ref($element->{elements}) eq 'ARRAY') {
         
          mkpath   ([ "$TOP_DIR/$DATATYPES_ROOT/$SCHEMA_VERSION/$ns/$path"  ], 1, 0755) ;
       }
       foreach my $el (@{$element->{elements}}) {
           if(ref($el) eq 'ARRAY') {
           
              if(ref($el->[1]) eq 'HASH' && $el->[1]->{attrs}) {
             
             buildAPI($el->[0],  $el->[1],  $path, $root,   $element ); 
         
          } elsif(ref($el->[1]) eq 'ARRAY') {
           
             foreach my $sub_el (@{$el->[1]}) {
                 if(ref($sub_el) eq 'HASH' && $sub_el->{attrs}) {  
                    buildAPI($el->[0],   $sub_el,   $path, $root, $element );
            } elsif(ref($sub_el) eq 'ARRAY' && scalar @{$sub_el} ==  1) {
                buildAPI($el->[0],   $sub_el->[0],   $path, $root,   $element  );  
                } else {
                $logger->error(" Malformed definition: name=" . $el->[0] . " Dump=" .  Dumper $sub_el);
            }
             } 
              }   
        }
            }     
       buildClass( "$TOP_DIR/$DATATYPES_ROOT/$SCHEMA_VERSION/$ns/$path", "$API_ROOT\:\:$DATATYPES_ROOT\:\:$SCHEMA_VERSION\:\:$ns$root" , $name, $element, $parent);      
      } 
      return;         
}
 
 sub  buildClass {
     my ($path, $root,   $name, $element, $parent ) = @_;
     my $className =   $root;
    
     my $fh = IO::File->new(  $path . ".pm","w+");
     $logger->error(" Failed to open file :" . $path . ".pm")  unless $fh;
    
#------------------------------------------------------------------------------
   my @elements =     grep(ref($_) eq 'ARRAY' && $_->[0] && $_->[1],   @{$element->{elements}});
  
   my @elementnodes = grep(ref($_->[1]),   @elements);
   my @textnodes =    grep($_->[1] eq 'text'  && !ref($_->[1]),  @elements);
 
   my $elements_names = @elementnodes?join (" " ,  map { $_->[0] }  @elementnodes):'';
   my $texts_names =  @textnodes?join (" " , map { $_->[0] } @textnodes):'';
   
   my @attributes =  grep(!/xmlns/, keys %{$element->{attrs}});
   my $attributes_names = @attributes?join " " , @attributes:'';
#--------------------------------------------------------------------------------  
   my %parent_sql = ();
   if($parent &&  ref($parent) eq 'HASH' && $parent->{sql}) {
       foreach my $table (keys %{$parent->{sql}}) {
           foreach my $field (keys %{$parent->{sql}->{$table}}) {
               my $value =  $parent->{sql}->{$table}->{$field}->{value};
           $value = [$value]  if ref($value) ne 'ARRAY';
           foreach my   $possible (@{$value}) {
              $parent_sql{$table}{$field}{$possible}++;
           }
       }
       }
   }  
   my %sql_pass =(); ### hash with pass through 
   my %sql_here =(); ### hash with sql to get here
  # preprocessing sql config    
   if($element->{sql}) {
       foreach my $table (keys %{$element->{sql}}) {
           foreach my $field (keys %{$element->{sql}->{$table}}) {
               my $value =  $element->{sql}->{$table}->{$field}->{value};
           unless($value) {
               $logger->error(" SQL config malformed for element=$name table=$table field=$field, but value is missied");
           return;
           } 
           my $condition = $element->{sql}->{$table}->{$field}->{if};
           my ($attr_name, $set) = $condition?$condition  =~ m/^(\w+):?(\w+)?$/:('','');
           my $cond_string = $condition && $set?" (\$self->$attr_name eq '$set') ":$condition?" (\$self->$attr_name)":'';
           
           $value = [$value]  if ref($value) ne 'ARRAY';
           
           foreach my   $possible (@{$value}) {
                next if %parent_sql && $parent_sql{$table}{$field} && !$parent_sql{$table}{$field}{$name};
           if($elements_names =~ /\b$possible\b/) {  #### if name of the possible element is among the members of this object the pass  it there
               $sql_pass{$possible}{$table}{$field} = $cond_string;
           } else {                                     ######  otherwise set it with some value ( text or attribute )
               $sql_here{$possible}{$table}{$field} = $cond_string;
           } 
               }
           }
       }
   }

#-------------------------------------------- build tests 

   buildTest(\@elementnodes,  \@attributes, $className, $name, $element);
   $logger->debug("\n...... List of Attributes:$attributes_names \n Texts: $texts_names \n Elements: $elements_names\n");
#----------------------------------------------
( my $version = $SCHEMA_VERSION ) =~ tr/_/./;
#--------------------------------------------
print  $fh <<EOA;
package  $className;
use strict;
use warnings;
use English qw( -no_match_vars);
use version; our \$VERSION = qv('$version');
=head1 NAME

 $className  - A base class, implements  '$name'  element from the perfSONAR_PS RelaxNG schema
  
=head1 DESCRIPTION

   Object representation of the $name element.
   Object fields are:
EOA
#------------------------------------------------------------------------------
map { print $fh   "    Scalar:     $_, \n"  }   @attributes ;
map { print $fh   "    Object reference:   " . $_->[0]  . " => type " . ref($_->[1]) . ",\n" }  @elements ;
   
  
   print $fh <<EOB;
   
   The constructor accepts only single parameter, it could be a hashref to parameters hash  or DOM with  '$name' element 
    
    
=head1 SYNOPSIS

              use $className;
          
          my \$el =  $className->new(\$DOM_Obj);
 
=head1   METHODS

=cut
 

use XML::LibXML;
use Scalar::Util qw(blessed);
use Log::Log4perl qw(get_logger); 
use perfSONAR_PS::Datatypes::Element qw(getElement);
use perfSONAR_PS::Datatypes::Namespace;
use perfSONAR_PS::Datatypes::NSMap;
use Readonly;
EOB
foreach my $el (@elementnodes) {
   foreach my $ns (keys %{$known_classes{$el->[0]}}) {
       print $fh  "use " . $known_classes{$el->[0]}{$ns} . ";\n"  if $known_classes{$el->[0]}{$ns}; 
   }
}


print  $fh <<EOC;
use Class::Accessor::Fast;
use Class::Fields;
use base qw(Class::Accessor::Fast Class::Fields);
EOC


 
print  $fh  "use fields qw(nsmap idmap refidmap $attributes_names $elements_names $texts_names ";
print $fh " text " if $element->{text}; 
print  $fh  ");\n";  
 
print  $fh  <<EOD;

$className->mk_accessors($className->show_fields('Public'));
  
=head2 new( )
   
      creates   object, accepts DOM with  element tree or hashref to the list of
      keyd parameters
EOD
map { print $fh   "         $_   => undef, \n"  }  @attributes ;
map { print $fh   "         " . $_->[0]  . " => " . ref($_->[1]) . ",\n"   }  @elementnodes;
print $fh "text => 'text'\n" if $element->{text};              
 
print  $fh  <<EOF;

=cut
Readonly::Scalar our \$COLUMN_SEPARATOR => ':';
Readonly::Scalar our \$CLASSPATH =>  '$className';
Readonly::Scalar our \$LOCALNAME => '$name';
            
sub new { 
    my \$that = shift;
    my \$param = shift;
 
    my \$logger  = get_logger( \$CLASSPATH ); 
    my \$class = ref(\$that) || \$that;
    my \$self =  fields::new(\$class );
    \$self->nsmap(perfSONAR_PS::Datatypes::NSMap->new()); 
EOF
   
print $fh "    \$self->nsmap->mapname( \$LOCALNAME, '" .   $element->{attrs}->{xmlns} . "');\n";
 
print $fh <<EOG;
    
    if(\$param) {
        if(blessed \$param && \$param->can('getName')  && (\$param->getName =~ m/\$LOCALNAME\$/xm) ) {
            return  \$self->fromDOM(\$param);  
          
        } elsif(ref(\$param) ne 'HASH')   {
            \$logger->error("ONLY hash ref accepted as param " . \$param ); 
            return;
        }
    if(\$param->{xml}) {
         my \$parser = XML::LibXML->new();
             my \$dom;
             eval {
                  my \$doc = \$parser->parse_string( \$param->{xml});
          \$dom = \$doc->getDocumentElement;
             };
             if(\$EVAL_ERROR) {
                 \$logger->error(" Failed to parse XML :" . \$param->{xml} . " \\n ERROR: \\n" . \$EVAL_ERROR);
                return;
             }
             return  \$self->fromDOM( \$dom );  
    } 
        \$logger->debug("Parsing parameters: " . (join " : ", keys \%{\$param}));
     
        no strict 'refs';
        foreach my \$param_key (keys \%{\$param}) {
            \$self->\$param_key( \$param->{\$param_key} ) if \$self->can(\$param_key);
        }  
        use strict;     
   
       \$logger->debug("Done ");     
    }  
    return \$self;
}

 
sub DESTROY {
    my \$self = shift;
    \$self->SUPER::DESTROY  if \$self->can("SUPER::DESTROY");
    return;
}
 
=head2   getDOM (\$) 
      
       accept parent DOM
       return $name object DOM, generated from object contents 
  
=cut 
 
sub getDOM {
    my \$self = shift;
    my \$parent = shift; 
    my \$logger  = get_logger( \$CLASSPATH ); 
    my \$$name = getElement({name =>   \$LOCALNAME, parent => \$parent , ns => [\$self->nsmap->mapname( \$LOCALNAME )],
                             attributes => [

EOG
#-------------------------------
 
 foreach my $attr (@attributes) {
      $logger->debug("_printConditional:: $attr = " . $element->{attrs}->{$attr});
      print $fh  _printConditional( $attr, $element->{attrs}->{$attr}, 'get');
 }
 print $fh   "                                           ],\n"; # end for attributes
 print $fh  _printConditional(  'text', $element->{text} , 'get') if ($element->{text} );
 
print $fh   "                         }); \n";
### deal with subelements 
###
###   each subel defined as [ name => obj  ] or [name => [obj]] or [name => [obj1,obj2]]  or    [name => [[obj1],[obj2]]]
### 
###                             just object    arrayref of objects  choice between two obj        chiice between two obj arrayref
###
foreach my $els (@elementnodes) {
    $logger->fatal(" What the heck: name=$name els=$els ") unless ref($els) eq 'ARRAY';
 
    my  $condition =  conditionParser($els->[2]);
    my $subname =  $els->[0];
    $condition->{logic} .= " && " if  $condition->{logic};
    if(ref($els->[1])    eq  'ARRAY') { 
        if(scalar @{$els->[1]} >  1 ) {     
            if(ref( $els->[1]->[0]) ne 'ARRAY') {
                printGetDOM($fh, $subname, $name,  $condition->{logic}); 
        } else {
            printGetArrayDom($fh, $subname, $name,  $condition->{logic});    
        }  
        } else {
            printGetArrayDom($fh, $subname, $name,  $condition->{logic});
        }
    } elsif(ref($els->[1])   eq  'HASH') {
        printGetDOM($fh, $subname, $name,  $condition->{logic});
    }  
}
if( $texts_names ) {
       print $fh  "   foreach my \$textnode (qw/$texts_names /) {\n";
       print $fh  "       if(\$self->{\$textnode}) { \n";
       print $fh  "            my  \$domtext  =  getElement({name =>   \$textnode, parent => \$$name , ns => [\$self->nsmap->mapname(\$LOCALNAME)],\n";
       print $fh  "                                          text => \$self->{\$textnode},\n";
       print $fh  "                                 });\n";
       print $fh  "            \$domtext?\$$name->appendChild(\$domtext):\$logger->error(\"Failed to append new text element \$textnode  to  $name   \");\n";
       print $fh  "        } \n";
       print $fh  "   } \n";
 }
 print $fh "    return \$$name;\n}\n";
  
foreach my  $el (@elementnodes) {
    my $subname = $el->[0];
   if(ref($el->[1]) eq 'ARRAY') { 
  
    print $fh <<EOH5;
  
=head2  add$subname()

    if any of subelements can be an arrray then this method will provide
    facility to add another element to the  array and will return ref to such array
    or just set the element to a new one
=cut

sub add\u$subname {
    my \$self = shift;
    my \$new = shift;
    my \$logger  = get_logger( \$CLASSPATH ); 
   
    \$self->$subname && ref(\$self->$subname) eq 'ARRAY'?push \@{\$self->$subname}, \$new:\$self->$subname([\$new]); 
    \$logger->debug("Added new to $subname"); 
    \$self->buildIdMap; ## rebuild index map 
    \$self->buildRefIdMap; ## rebuild ref index map  
    return \$self->$subname;
}

=head2  remove\u${subname}ById()

     remove specific element from the array of ${subname} elements by id ( if id is supported by this element )
     accepts single param - id - which is id attribute of the element
     if there is no array then it will return undef and warninig
     if it removed some id then \$id will be returned
     
=cut

sub remove\u${subname}ById {
    my \$self = shift;
    my \$id = shift;
    my \$logger  = get_logger( \$CLASSPATH ); 
    if(ref(\$self->$subname) eq 'ARRAY' && \$self->idmap->{$subname} &&  exists \$self->idmap->{$subname}{\$id}) { 
        \$self->$subname->\[\$self->idmap->{$subname}{\$id}\]->DESTROY; 
    my \@tmp =  grep { defined \$_ } \@{\$self->$subname};  
    \$self->$subname([\@tmp]);
    \$self->buildRefIdMap; ## rebuild ref index map  
    \$self->buildIdMap; ## rebuild index map 
    return \$id;
    } elsif(!ref(\$self->$subname)  || ref(\$self->$subname) ne 'ARRAY')  {
        \$logger->warn("Failed to remove  element because ${subname} not an array for non-existent id:\$id");  
    } else {
        \$logger->warn("Failed to remove element for non-existant id:\$id");  
    } 
    return;
}   
=head2  get\u${subname}ByMetadataIdRef()

     get specific object from the array of ${subname} elements by  MetadataIdRef( if  MetadataIdRef is supported by this element )
     accepts single param -  MetadataIdRef
     if there is no array then it will return just an object
     
=cut

sub get\u${subname}ByMetadataIdRef {
    my \$self = shift;
    my \$id = shift;
    my \$logger  = get_logger( \$CLASSPATH ); 
    if(ref(\$self->$subname) eq 'ARRAY' && \$self->refidmap->{$subname} && exists \$self->refidmap->{$subname}{\$id}) {
        my \$$subname = \$self->$subname->\[\$self->refidmap->{$subname}{\$id}\];
    return (\$$subname->can("metadataIdRef") &&   \$$subname->metadataIdRef eq  \$id)?\$$subname:undef; 
    } elsif(\$self->$subname && (!ref(\$self->$subname) || 
                                    (ref(\$self->$subname) ne 'ARRAY' &&
                                     blessed \$self->$subname && \$self->$subname->can("metadataIdRef") &&
                     \$self->$subname->metadataIdRef eq  \$id)))  {
        return \$self->$subname;
    }  
    \$logger->warn("Requested element for non-existent metadataIdRef:\$id"); 
    return;
    
}

=head2  get\u${subname}ById()

     get specific element from the array of ${subname} elements by id ( if id is supported by this element )
     accepts single param - id
     if there is no array then it will return just an object
     
=cut

sub get\u${subname}ById {
    my \$self = shift;
    my \$id = shift;
    my \$logger  = get_logger( \$CLASSPATH ); 
    if(ref(\$self->$subname) eq 'ARRAY' && \$self->idmap->{$subname} &&  exists \$self->idmap->{$subname}{\$id} ) {
        return \$self->$subname->\[\$self->idmap->{$subname}{\$id}\];
    } elsif(!ref(\$self->$subname) || ref(\$self->$subname) ne 'ARRAY')  {
        return \$self->$subname;
    }  
    \$logger->warn("Requested element for non-existent id:\$id"); 
    return;   
}
EOH5
}

}

print $fh <<EOH56;

=head2  querySQL ()

      depending on config  it will return some hash ref  to the initialized fields
    for example querySQL ()
    accepts one optional prameter - query hashref
    will return:
    { ip_name_src =>  'hepnrc1.hep.net' },}
    
=cut

sub  querySQL {
    my \$self = shift;
    my \$query = shift; ### undef at first and then will be hash ref
    my \$logger  = get_logger( \$CLASSPATH );
     
EOH56
    if($element->{sql}) {
      print $fh "    my \%defined_table = (";
      
      foreach my $table (keys %{$element->{sql}}) { 
           print $fh "  '$table' => [";
       foreach my $field (keys %{$element->{sql}->{$table}}) {   
         print $fh "  '$field', ";
       }
       print $fh " ], ";
      }
      print $fh " );\n";
    }
 
    foreach my  $subname (keys %sql_pass) {  
        foreach my $table (keys %{$sql_pass{$subname}}) {
        foreach my $entry (keys %{$sql_pass{$subname}{$table}}) { 
                print $fh "    \$query->{$table}{$entry}= [";
            foreach my $nss (keys %{ $known_classes{$subname}}) { 
                    print $fh "    '$known_classes{$subname}{$nss}',";
               }
           print $fh "    ];\n";
        }    
    }
    }
    foreach my  $subname (keys %sql_here) {  
        foreach my $table (keys %{$sql_here{$subname}}) {
        foreach my $entry (keys %{$sql_here{$subname}{$table}}) {
           
           print $fh "    \$query->{$table}{$entry}= [ '$className' ] if!(defined \$query->{$table}{$entry}) || ref(\$query->{$table}{$entry});\n";
        }    
    }
    }
if($elements_names)  {   
print $fh <<EOH78; 
    foreach my \$subname (qw/$elements_names/) {
        if(\$self->{\$subname} && (ref(\$self->{\$subname}) eq 'ARRAY' ||  blessed \$self->{\$subname}))   {
            my \@array = ref(\$self->{\$subname}) eq 'ARRAY'?\@{\$self->{\$subname}}:(\$self->{\$subname});
        foreach my \$el  (\@array) {
            if(blessed  \$el  &&  \$el->can("querySQL"))  {
                    \$el->querySQL(\$query);         
                    \$logger->debug("Quering $name  for subclass \$subname");
            } else {
                \$logger->error(" Failed for $name Unblessed member or querySQL is not implemented by subclass \$subname");
            }
        }  
        }
    }    
EOH78
}
    if(%sql_here)  {
        print $fh   "    eval { \n";
        print $fh   "        foreach my \$table  ( keys \%defined_table) {  \n";
        print $fh   "            foreach my \$entry (\@{\$defined_table{\$table}}) {  \n";
    print $fh   "                if(ref(\$query->{\$table}{\$entry}) eq 'ARRAY') {\n";
        print $fh   "                    foreach my \$classes (\@{\$query->{\$table}{\$entry}}) {  \n";
         print $fh  "                        if(\$classes && \$classes eq '$className' ) { \n";
        my $if_sub_cond = ' if    ';
        foreach my  $subname  (@attributes, 'text') {       
             if($sql_here{$subname})  {
                print $fh   getSQLSub($sql_here{$subname},   $subname, $if_sub_cond  ); 
            $if_sub_cond =  ' elsif ';
         }
        } 
    print $fh  "                         }\n";
        print $fh  "                     }\n";
        print $fh  "                 }\n";    
        print $fh  "             }\n";
    print $fh  "        }\n";
    print $fh  "    }; \n    if (\$EVAL_ERROR) { \$logger->logcroak(\" SQL query building is failed  here \" . \$EVAL_ERROR)};\n";
    }
    print $fh  "    return \$query;\n";
    print $fh  "}\n";
   

print $fh <<EOHH;

=head2 merge

      merge with another $name ( append + overwrite if exists )
      we can do it differently
      method #1:
         convert to dom both objects and then get resulted object from combined dom 
      method #2 default:
         through the introspection of the object

=cut


sub merge {
    my \$self = shift;
    my \$new_${name} = shift;
    my \$logger  = get_logger( \$CLASSPATH );  
    unless(\$new_${name} && blessed \$new_${name} && \$new_${name}->can("getDOM")) {
        \$logger->error(" Please supply defined object of $name  ");
        return;
    } 
    ### for each field ( element or attribute )
    ### merge elements, add if its arrayref and overwrite attribtues for the same elements
    ### merge only if namespace is the same  
    foreach my \$member_name (\$new_${name}->show_fields) {
        ### double check if   objects are the same
    if(\$self->can(\$member_name)) {
        my \$current_member  = \$self->{\$member_name};
        my \$new_member      =  \$new_${name}->{\$member_name};
        ###  check if both objects are defined
        if(\$current_member && \$new_member) {
            ### if  one of them array then just add another one
            if(blessed \$current_member && blessed \$new_member  && \$current_member->can("merge") 
               && ( \$current_member->nsmap->mapname(\$member_name) 
                eq  \$new_member->nsmap->mapname(\$member_name) ) ) {
               \$current_member->merge(\$new_member);
            \$self->{\$member_name} =  \$current_member;
            \$logger->debug("  Merged \$member_name , got" . \$current_member->asString);
            ### if its array then just push
            } elsif(ref(\$current_member) eq 'ARRAY'){
                 
           \$self->{\$member_name}=[\$current_member, \$new_member];
              
            \$logger->debug("  Pushed extra to \$member_name ");
            }  
        ## thats it, dont merge if new member is just a scalar
        } elsif( \$new_member) {
           \$self->{\$member_name} = \$new_member;
        }   
    } else {
        \$logger->error(" This field \$member_name,  found in supplied  $name  is not supported by $name class");
        return;
        }
    }
    return \$self;
} 
 
=head2  buildIdMap()

    if any of subelements has id then get a map of it in form of
    hashref to { element}{id} = index in array and store in the idmap field

=cut

sub  buildIdMap {
    my \$self = shift;
    my \$map = (); 
    my \$logger  = get_logger( \$CLASSPATH );
EOHH
   if( @elementnodes ) {
        print $fh  "    foreach my \$field (qw/$elements_names/) {\n";
    print $fh  "        my \@array = ref(\$self->{\$field}) eq 'ARRAY'?\@{\$self->{\$field}}:(\$self->{\$field});\n";
        print $fh  "        my \$i = 0;\n";
        print $fh  "        foreach my \$el ( \@array)  {\n";
    print $fh  "            if(\$el && blessed \$el && \$el->can(\"id\") &&  \$el->id)  { \n";
        print $fh  "                \$map->{\$field}{\$el->id} = \$i;   \n"; 
        print $fh  "            }\n"; 
    print $fh  "            \$i++;\n"; 
    print $fh  "        }\n";
    print $fh  "    }\n";
        print $fh  "    return \$self->idmap(\$map);\n";
     } else {
        print $fh  "    return;\n";
    }  
     print $fh  "}\n";
     
print $fh <<EOHH23;
=head2 buildrefIdMap ()

    if any of subelements has  metadataIdRef  then get a map of it in form of
    hashref to { element}{ metadataIdRef } = index in array and store in the idmap field

=cut

sub  buildRefIdMap {
    my \$self = shift;
    my \%map = (); 
    my \$logger  = get_logger( \$CLASSPATH );
EOHH23

   if( @elementnodes ) {
        print $fh  "    foreach my \$field (qw/$elements_names/) {\n";
    print $fh  "        my \@array = ref(\$self->{\$field}) eq 'ARRAY'?\@{\$self->{\$field}}:(\$self->{\$field});\n"; 
    print $fh  "        my \$i = 0;\n";
        print $fh  "        foreach my \$el ( \@array)  {\n";  
    print $fh  "            if(\$el && blessed \$el  && \$el->can(\"metadataIdRef\") &&  \$el->metadataIdRef )  { \n";
        print $fh  "                \$map{\$field}{\$el->metadataIdRef} = \$i;   \n";
        print $fh  "            }\n"; 
    print $fh  "            \$i++;\n"; 
    print $fh  "        }\n";
    print $fh  "    }\n";
        print $fh  "    return \$self->refidmap(\\\%map);\n";
     } else {
        print $fh  "    return;\n";
    }  
     print $fh  "}\n";
     
print $fh <<EOH1; 
=head2  asString()

   shortcut to get DOM and convert into the XML string
   returns XML string  representation of the  $name object

=cut

sub asString {
    my \$self = shift;
    my \$dom = \$self->getDOM();
    return \$dom->toString('1');
}

=head2 registerNamespaces ()

   will parse all subelements and register all namepspaces within the $name namespace

=cut

sub registerNamespaces {
    my \$self = shift;
    my \$logger  = get_logger( \$CLASSPATH );
    my \$nsids = shift;
    my \$local_nss = {reverse \%{\$self->nsmap->mapname}};
    unless(\$nsids) {
        \$nsids =  \$local_nss;
    }  else {
        \%{\$nsids} = ( \%{\$local_nss},  \%{\$nsids});
    }
EOH1
     if( @elementnodes ) {
        print $fh  "    foreach my \$field (qw/$elements_names/) {\n";
    
    print $fh  "        my \@array = ref(\$self->{\$field}) eq 'ARRAY'?\@{\$self->{\$field}}:(\$self->{\$field});\n";
        print $fh  "        foreach my \$el ( \@array)  {\n";
    print $fh  "            if(blessed \$el &&   \$el->can(\"registerNamespaces\") )  { \n";
    print $fh  "                my \$fromNSmap =  \$el->registerNamespaces(\$nsids); \n";
    print $fh  "                my \%ns_idmap =   \%{\$fromNSmap};  \n";
    print $fh  "                foreach my \$ns ( keys \%ns_idmap)  {\n";
    print $fh  "                      \$nsids->{\$ns}++\n";
    print $fh  "                }\n";
    print $fh  "            }\n";
        print $fh  "        }\n";  
    print $fh  "    }\n";
     }
        print $fh  "    return     \$nsids;\n";
    
       print $fh   "}\n"; 
       
       
print $fh <<EOH2;  
=head2  fromDOM (\$)
   
   accepts parent XML DOM   element   tree as parameter 
   returns $name  object

=cut

sub fromDOM {
    my \$self = shift;
    my \$logger  = get_logger( \$CLASSPATH ); 
    my \$dom = shift;
     
EOH2
  $logger->debug("  fromDOM for: name=$name ");
 foreach my $attr (@attributes) {
     print $fh  _printConditional($attr, $element->{attrs}->{$attr}, 'from');  
     print $fh "    \$logger->debug(\" Attribute $attr= \". \$self->$attr) if \$self->$attr; \n";
 }
 print $fh  _printConditional('text', $element->{text}, 'from') if ($element->{text}) ;
 if(@elements) {
    print $fh "    foreach my \$childnode (\$dom->childNodes) { \n";
    print $fh "        my  \$getname  = \$childnode->getName;\n";
    print $fh "        my (\$nsid, \$tagname) = split \$COLUMN_SEPARATOR,  \$getname; \n";
    print $fh "        unless(\$nsid && \$tagname) {   \n"; 
 ##   print $fh "           \$logger->warn(\" Undefined  tag=\$getname\");        \n";
    print $fh "            next;\n";         
    print $fh "        }\n";
    my $conditon_head =  '        if';
    foreach my $els (@elementnodes) {
       $logger->fatal("   What the heck: name=$name els=$els ") unless ref($els) eq 'ARRAY';
       my $subname =  $els->[0];
       my  $condition  =  conditionParser($els->[2]);
       $condition->{logic} .= " && " if  $condition->{logic};
       if(ref($els->[1])    eq  'ARRAY') { 
          if(scalar @{$els->[1]} >  1 )  {
         foreach my $choice (@{$els->[1]}) {
             if(ref($choice) ne  'ARRAY') { 
                 printFromDOM($fh, $subname, $choice,   'CHOICE', $conditon_head,   $condition->{logic});
                 $conditon_head = ' elsif';
          } elsif(scalar @{$choice} ==  1 ) {
             printFromDOM($fh, $subname, $choice->[0],   'ARRAY', $conditon_head,   $condition->{logic});
                 $conditon_head = ' elsif';
          } else {
            $logger->logdie(" Malformed element definition: name=$name subelement=$subname  ");
          } 
        }
      } else {
          printFromDOM($fh, $subname, $els->[1]->[0] ,   'ARRAY',$conditon_head,    $condition->{logic});
      }
        } elsif (ref($els->[1])    eq  'HASH') {
            printFromDOM($fh, $subname,$els->[1],   'HASH',$conditon_head,   $condition->{logic});
    }  
     $conditon_head = ' elsif';
    }
    if( @textnodes) {
        print $fh "$conditon_head (\$childnode->textContent && \$self->can(\"\$tagname\")) { \n";
        print $fh "           \$self->{\$tagname} =  \$childnode->textContent; ## text node \n";
        print $fh "        }  "; 
     
    }
    if(@elementnodes || @textnodes) {
       print $fh "     ###  \$dom->removeChild(\$childnode); ##remove processed element from the current DOM so subclass can deal with remaining elements\n";
    }
  
    print $fh "    }\n"; 
    print $fh "  \$self->buildIdMap;\n \$self->buildRefIdMap;\n \$self->registerNamespaces;\n  "; 
 }
 
    
 print $fh "\n return \$self;\n}\n"; 
 
print $fh <<EOJ;

 
 
=head1 AUTHORS

   Maxim Grigoriev (FNAL)  2007-2008, maxim\@fnal.gov

=cut 

1;
 
EOJ

close $fh; 
return; 
} 
#    auxiliary private function 
#    build test file for the class  
#
sub buildTest {
  my ($elementnodes, $attributes, $className, $name, $element) = @_; 
   mkpath   ([ "$TEST_DIR" ], 1, 0755);
   my $fhtest = IO::File->new( "$TEST_DIR$className.t" ,"w+");
    $logger->error(" Failed to open test suite file : $TEST_DIR$className.t")  unless $fhtest;
    
    print  $fhtest <<EOTA;
use warnings;
use strict;    
use Test::More 'no_plan';
use Data::Dumper;
use FreezeThaw qw(cmpStr);
use Log::Log4perl;
use_ok('$className');
use    $className;
EOTA

foreach my $el (@{$elementnodes}) {
   foreach my $ns (keys %{$known_classes{$el->[0]}}) {
       print $fhtest  "use " . $known_classes{$el->[0]}{$ns} . ";\n"  if $known_classes{$el->[0]}{$ns}; 
   }
}

    print $fhtest <<EOTB;
Log::Log4perl->init("$TOP_DIR/logger.conf"); 

my \$obj1 = undef;
#2
eval {
\$obj1 = $className->new({
EOTB

    map { print $fhtest   "  '$_' =>  'value_$_'," }   @{$attributes};
    print $fhtest "})\n};\n  ok( \$obj1  && \!\$EVAL_ERROR , \"Create object $className...\" . \$EVAL_ERROR);\n  \$EVAL_ERROR = undef; \n";

    print $fhtest "#3\n";
    print $fhtest " my \$ns  =  \$obj1->nsmap->mapname('$name');\n"; 
    print $fhtest " ok(\$ns  eq '". $element->{attrs}->{xmlns} . "', \"  mapname('$name')...  \");\n";
    my $testn = '4';
    foreach my $att (@{$attributes}) {
        print $fhtest "#$testn\n";
        print $fhtest " my \$$att  =  \$obj1->$att;\n"; 
        print $fhtest " ok(\$$att  eq 'value_$att', \" checking accessor  obj1->$att ...  \");\n";
        $testn++;
    }
    foreach my $subel (@{$elementnodes}) { 
        my $subel1 = (ref($subel->[1]) eq 'ARRAY')?
                      ((ref($subel->[1]->[0]) eq 'ARRAY')?$subel->[1]->[0]->[0]:$subel->[1]->[0]):
                                      ((ref($subel->[1]) eq 'HASH')?$subel->[1]:undef);
        next unless $subel1;
        print $fhtest "#$testn\n"; 
        my $subel_name = $subel->[0];
        print $fhtest " my  \$obj_$subel_name  = undef;\n";
        print $fhtest " eval {\n";
        print $fhtest "      \$obj_$subel_name  =  " . $known_classes{$subel_name}{$subel1->{attrs}->{xmlns}} ."->new({";
        map { print $fhtest   "  '$_' =>  'value$_'," if   $_ ne 'xmlns' &&   $subel1->{attrs}->{$_}}  keys %{$subel1->{attrs}};
        print $fhtest "});\n";
       (ref($subel->[1]) eq 'ARRAY' && $#{$subel->[1]} == 0)?print $fhtest "    \$obj1->add\u$subel_name(\$obj_$subel_name);\n":
                                                             print $fhtest "    \$obj1->$subel_name(\$obj_$subel_name);\n ";
    print $fhtest "  }; \n";
        print $fhtest " ok( \$obj_$subel_name && \!\$EVAL_ERROR , \"Create subelement object $subel_name and set it  ...\" . \$EVAL_ERROR);\n  \$EVAL_ERROR = undef; \n";
        $testn++; 
     
    }
    print $fhtest "#$testn\n"; 
    print $fhtest " my \$string = undef;\n";
    print $fhtest " eval {\n";
    print $fhtest "      \$string =  \$obj1->asString \n";
    print $fhtest " };\n";
    print $fhtest " ok(\$string   && \!\$EVAL_ERROR  , \"  Converting to string XML:   \$string \" . \$EVAL_ERROR);\n";
    print $fhtest " \$EVAL_ERROR = undef;\n"; 
    $testn++;
    print $fhtest "#$testn\n";  
    
    print $fhtest " my \$obj22 = undef; \n";
    print $fhtest " eval {\n";
    print $fhtest "    \$obj22   =   $className->new({xml => \$string});\n";
    print $fhtest " };\n";
    print $fhtest " ok( \$obj22  && \!\$EVAL_ERROR , \"  re-create object from XML string:  \".   \$EVAL_ERROR);\n";
    print $fhtest " \$EVAL_ERROR = undef;\n"; 
    $testn++;
    print $fhtest "#$testn\n";  
    print $fhtest " my \$dom1 = \$obj1->getDOM();\n";
    print $fhtest " my \$obj2 = undef; \n";
    print $fhtest " eval {\n";
    print $fhtest "    \$obj2   =   $className->new(\$dom1);\n";
    print $fhtest " };\n";
    print $fhtest " ok( \$obj2  && \!\$EVAL_ERROR , \"  re-create object from DOM XML:  \".   \$EVAL_ERROR);\n";
    print $fhtest " \$EVAL_ERROR = undef;\n"; 
    close $fhtest;
}


#
#   auxiliary private function 
#   prints part of getSQL which maps available entries on sql request hash 
#
sub getSQLSub {
    my ($sql_fields,   $subname,  $if_cond ) = @_;
    my $head_string = "                           $if_cond(\$self->$subname && ("; 
    my $add = ' ';
    foreach my $table (keys %{$sql_fields}) { 
       
        $head_string .= "$add( ";
        my @cond_string = ();
        foreach my $field (keys %{$sql_fields->{$table}}) { 
            my $cond = $sql_fields->{$table}{$field};
        $cond .= ' && ' if $cond;  
        push @cond_string, " ($cond\$entry eq '$field')";
        }
        $head_string .=  (join " or ", @cond_string) . ")"; 
         $add = ' || ';
      } 
      $head_string .=  " )) {\n";
        
     $head_string .=   "                                \$query->{\$table}{\$entry} =  \$self->$subname;\n"; 
     $head_string .=   "                                \$logger->debug(\" Got value for SQL query \$table.\$entry: \" . \$self->$subname);\n"; 
     $head_string .=   "                                last;  \n"; 
     $head_string .=   "                            }\n";
     return $head_string;
}
#
#   auxiliary private function 
#   printing fromDOM part
#
#
sub printFromDOM {
    my ($fh, $subname, $el,   $type, $conditon_head, $cond_string ) = @_;
    my  $subnameUP =  ucfirst($subname);
    $logger->debug("Building fromDOM: type=$type subname=$subname");
    print $fh "$conditon_head ($cond_string\$tagname eq  '$subname' && \$nsid eq '".  $el->{'attrs'}{'xmlns'}  . "' && \$self->can(\$tagname)) { \n";
    print $fh "           my \$element = undef;\n";
    print $fh "           eval {\n";
    print $fh "               \$element = " . $known_classes{$subname}{$el->{'attrs'}{'xmlns'}} . "->new(\$childnode) \n";
    print $fh "           };\n";
    print $fh "           if(\$EVAL_ERROR || !(\$element  && blessed \$element)) {\n";
    print $fh "               \$logger->error(\" Failed to load and add  $subnameUP : \" . \$dom->toString . \" error: \" . \$EVAL_ERROR);\n";
    print $fh "               return;\n";
    print $fh "           }\n";
    print $fh   (($type eq 'ARRAY')?"           (\$self->$subname && ref(\$self->$subname) eq 'ARRAY')?push \@{\$self->$subname}, \$element:\$self->$subname([\$element]);":
                       "           \$self->$subname(\$element)") . "; ### add another $subname  \n";
    print $fh "        } ";
}
#
#   auxiliary private function 
#   printing  getDom  part for arrayref members ( when its more then single instance of the sublelement )
#
sub printGetArrayDom { 
    my ($fh, $subname, $name, $logic) = @_;
       print $fh  "    if($logic\$self->$subname && ref(\$self->$subname) eq 'ARRAY' ) {\n";
       print $fh  "        foreach my \$subel (\@{\$self->$subname}) { \n";
       print $fh  "            if(blessed  \$subel  &&  \$subel->can(\"getDOM\")) { \n";
       print $fh  "                 my  \$subDOM =  \$subel->getDOM(\$$name);\n";
       print $fh  "                \$subDOM?\$$name->appendChild(\$subDOM):\$logger->error(\"Failed to append  $subname elements  with value: \" .  \$subDOM->toString ); \n";    
       print $fh  "            }\n";
       print $fh  "         }\n";
       print $fh  "    }\n";
}
#
#   auxiliary private function 
#   printing  getDom  part for singular  object members 
#
sub printGetDOM {
    my ($fh, $subname, $name, $cond_string) = @_;
    print $fh  "   if($cond_string\$self->$subname  && blessed \$self->$subname  && \$self->$subname->can(\"getDOM\")) {\n";
    print $fh  "        my  \$${subname}DOM = \$self->$subname->getDOM(\$$name);\n";
    print $fh  "       \$${subname}DOM?\$$name->appendChild(\$${subname}DOM):\$logger->error(\"Failed to append  $subname  with value: \" .  \$${subname}DOM->toString ); \n";    
    print $fh  "   }\n";
} 
#
#    auxiliary private function 
#    will parse conditional string and return  regexp and  logical condition
#    accepted parameter: $value is string to parse  
#    will return hashref to the resulted hash with keys: {condition  , logic =>  , regexp  }
#
sub conditionParser {
    my $value = shift;
    my $result = { condition => '', logic => '', regexp => ''};
    return $result  unless $value;
    $value  =~ s/^(scalar|enum|set|if|unless|exclude)\:?//;
    $result->{condition} = $1;
    my    @list  = split ",", $value   unless  $result->{condition} eq 'scalar';
    if(@list) {
        $result->{logic}  =  "(\$self->" . (join " && \$self->", @list) . ")";
        $result->{regexp} = " =~ m/(" . (join "|", @list) . ")\$/";
        if($result->{condition}  eq 'unless') {
            $result->{logic}  = "!".  $result->{logic};
    } elsif($result->{condition}  eq 'exclude') {
        $result->{regexp} =~ s/\=\~/\!\~/;
        }
    }
    return  $result;      
}
#
#   auxiliary private function 
#   analyze condition and return conditional string to be used in getDOM|fromDOM
#   accepted parameters: $key - [attribute | 'text'],  $value - condition to parse, $what -  ['get' | 'from']
#
#
sub _printConditional {
    my ($key, $value,$what) = @_;
    my $string = '';
  
    my $arrayref_signleft = ($key ne 'text')?"[":'';
    my $arrayref_signright = ($key ne 'text')?"]":''; 
   
    my $fromDomArg = ($key ne 'text')?"\$dom->getAttribute('$key')":"\$dom->textContent";
   
    my  $condition  = conditionParser($value);
    $logger->debug("$value Enum List:: " . ( join ":", map { " $_= " . $condition->{$_}} keys  %{$condition})) unless $condition->{condition}  eq 'scalar';
    
    if($condition->{condition}   eq 'scalar') { 
        $string =   $what eq 'get'?"                                               $arrayref_signleft'$key' =>  \$self->$key$arrayref_signright,\n":
                                 "    \$self->$key($fromDomArg) if($fromDomArg);\n";
    } elsif($condition->{condition}  =~ /^if|unless$/ &&  $condition->{logic}) {
        $string =  $what eq 'get'?"                                     $arrayref_signleft '$key' => (".$condition->{logic}."?\$self->$key:undef)$arrayref_signright,\n":
                                 "    \$self->$key($fromDomArg) if(" . $condition->{logic}. " && $fromDomArg);\n";   
    } elsif($condition->{condition} =~ /enum|set|exclude/ &&  $condition->{regexp}) {
          
        my $regexp  =  $what eq 'get'?"(\$self->$key   " . $condition->{regexp} . ")":"($fromDomArg  " . $condition->{regexp}  .")";
          
        $string     =   $what eq 'get'?"                                     $arrayref_signleft'$key' =>  ($regexp?\$self->$key:undef)$arrayref_signright,\n":
                                  "    \$self->$key($fromDomArg) if($fromDomArg && $regexp);\n";   
  
    } else {
        $logger->fatal("Malfromed , uknown condition=" . $condition->{condition} );
    }
    
    return $string;
}
  
 
1;