package UML::Export::Java::Hibernate;

use 5.6.1;
use strict;
use warnings;

use base qw(UML::Export::Java);

our $AUTHOR = q{ ks.perl@kurtstephens.com 2004/04/08 };
our $VERSION = do { my @r = (q$Revision: 1.1 $ =~ /\d+/g); sprintf "%d." . "%03d" x $#r, @r };

=head1 NAME

UML::Export::Java::Hibernate - A Hibernate .hbn.xml code generator for XMI

=head1 SYNOPSIS

  use base qw(UML::Export::Java::Hibernate);
  my $code = '';
  my $gen = new UML::Export::Java::Hibernate('out' => sub { $code .= join('', @_) });
  $gen->xmi_doc($xmi_doc);
  print $code;

=head1 DESCRIPTION

This package allow XMI documents generate XML mapping documents for Hibernate.

=head1 USAGE

=head1 PATTERNS

=over 4

=item * a design pattern


=back

=head1 EXPORT

None exported.

=head1 AUTHOR

Kurt Stephens, ks.perl@kurtstephens.com 2003/04/08

=head1 SEE ALSO

L<UML::XMI|UML::XMI>, L<XML::DOM|XML::DOM>, L<http://hibernate.bluemars.net|http://hibernate.bluemars.net>

=head1 VERSION

$Revision: 1.1 $

=head1 METHODS

=cut

####################################################################################

use XML::Writer;

####################################################################################


sub xmi_hbn_isEnabled
{
  my ($self, $node) = @_;

  $node->xmi_taggedValue_inherited_true('uml2code')
  && $node->xmi_taggedValue_inherited_true('uml22code.Java.Hibernate');
}


sub xmi_hbn_hasStoredSuperclass
{
  my ($self, $cls) = @_;

  for my $supercls ( $cls->xmi_generalization_parent ) {
    return 1 if $self->xmi_hbn_isEnabled($supercls);
    return 1 if $self->xmi_hbn_hasStoredSuperclass($supercls);
  }

  0;
}


sub xmi_hbn_hasStoredSubclass
{
  my ($self, $cls) = @_;

  for my $supercls ( $cls->xmi_generalization_child ) {
    return 1 if $self->xmi_hbn_isEnabled($supercls);
    return 1 if $self->xmi_hbn_hasStoredSubclass($supercls);
  }

  0;
}


sub xmi_hbn_rootClasses
{
  my ($self, $model) = @_;

  my @root_classes;

  for my $cls ( $model->xmi_classes ) {
    next if $self->xmi_hbn_hasStoredSuperclass($cls);
    push(@root_classes, $cls);
  }

  wantarray ? @root_classes : \@root_classes;
}


####################################################################################


sub xmi_hbn_attribute
{
  my ($self, $attr, $cls) = @_;

  return unless $self->xmi_hbn_isEnabled($attr);
  
  my $attr_name = $attr->xmi_name;
  my $attr_type = $attr->xmi_type;
  my $attr_multi = $attr->xmi_multiplicity;
  my $attr_multi_range = $attr_multi->xmi_ranges;
  my $java_type = $self->xmi_java_type($attr_type);

  my $needsCollection = $self->xmi_java_needsCollection($attr, $attr_multi);

  my $xml = $self->{'xml'};
  
  my $type = $attr->xmi_taggedValue('uml2code.Java.Hibernate.type');
  my $column = $attr->xmi_taggedValue('uml2code.Java.Hibernate.column');
  my $cascade = $attr->xmi_taggedValue_inherited('uml2code.Java.Hibernate.cascade');
  my $outer_join = $attr->xmi_taggedValue_inherited('uml2code.Java.Hibernate.outer-join');

  if ( $needsCollection ) {
    $xml->startTag('set',
		   'name' => $attr_name,
		   'not-null' => 'false', #??#?#?#
		   $type ? ( 'type' => $type ) : (),
		   );
    $xml->endTag('set');
  } else {
    if ( $attr_type->isa('UML::Export::DataType') ) {
      $xml->emptyTag('property',
		     'name' => $attr_name,
		     $column ? ( 'column' => $column ) : (),
		     $type ? ( 'type' => $type ) : (),
		     );
    } else {
      my $component = $attr->xmi_taggedValue_true('uml2code.Java.Hibernate.component');
      if ( $component ) {
	$self->xmi_hbn_class($attr_type, 'component', 
			     'name' => $attr_name,
			     'class' => $java_type,
			     );
      } else {
	$xml->emptyTag('many-to-one',
		       'name' => $attr_name,
		       $column ? ( 'column' => $column ) : (),
		       'class' => $java_type,
		       $cascade ? ( 'cascade' => $cascade ) : (),
		       $outer_join ? ( 'outer-join' => $outer_join ) : (),
		       );
      }
    }
  }

  $self;
}


sub xmi_hbn_operation
{
  my ($self, $op, $cls) = @_;

}


sub xmi_hbn_association_end
{
  my ($self, $end, $cls) = @_;

}


sub xmi_hbn_class
{
  my ($self, $cls, $hbn_type, %opts) = @_;
  
  $hbn_type ||= 'class';

  # $DB::single = 1;
  
  my $xml = $self->{'xml'};
  
  my $cls_name = $self->xmi_java_type($cls);

  # print STDERR "\nxmi_hbn_class $cls_name $hbn_type\n";

  my $hbn_isEnabled = $self->xmi_hbn_isEnabled($cls);
  if ( $hbn_isEnabled ) {
    # $DB::single = 1;
    
    my $table_name = $cls->xmi_taggedValue_inherited('uml2code.Java.Hibernate.table');
    unless ( defined $table_name ) {
      $table_name = $cls_name;
      $table_name =~ s/\./_/sg;
    }
    
    # True if the class needs to discriminate subclass instances.
    my $discriminator = $cls->xmi_taggedValue_inherited_true('uml2code.Java.Hibernate.discriminator', $self->xmi_hbn_hasStoredSubclass($cls));

    # The value that should be used to discriminate this class.
    my $discriminator_value = $cls->xmi_taggedValue('uml2code.Java.Hibernate.discriminator-value', $cls_name);

    # The name for the id field.
    my $id_name = $cls->xmi_taggedValue_inherited('uml2code.Java.Hibernate.id.name', 'id');
    my $id_generator_class = $cls->xmi_taggedValue_inherited
    (
     'uml2code.Java.Hibernate.id.generator.class' =>
     'sequence'
     );
    my $id_generator_param = $cls->xmi_taggedValue_inherited
    ('uml2code.Java.Hibernate.id.generator.param' => 
     join('_', $table_name, $id_name, 'seq')
     );
    
    $xml->startTag($hbn_type, 
		   $hbn_type eq 'component' ?
		   ( %opts )
		   :
		   (
		    'name' => $cls_name,
		    'table' => $table_name,
		    'discriminator-value' => $discriminator_value,
		    )
		    );
    
    if ( $hbn_type eq 'class' ) {
      $xml->startTag('id',
		     'name' => $id_name,
		     );
      $xml->startTag('generator',
		     'class' => $id_generator_class,
		     );
      $xml->startTag('param');
      $xml->characters($id_generator_param);
      $xml->endTag('param');
      $xml->endTag('generator');
      $xml->endTag('id');
    }
    if ( $hbn_type eq 'subclass' ) {
    }
    
    # Discriminator column?
    if ( $hbn_type ne 'component' ) {
      if ( $discriminator ) {
	my $discriminator_type = $cls->xmi_taggedValue_inherited('uml2code.Java.Hibernate.discriminator.type');
	my $discriminator_column = $cls->xmi_taggedValue_inherited('uml2code.Java.Hibernate.discriminator.type');

	$xml->emptyTag('discriminator',
		       $discriminator_column ? ( 'column' => $discriminator_column ) : (),
		       $discriminator_type   ? ( 'type' => $discriminator_type ) : (),
		       );
      }
    }

    # Attributes
    for my $attr ( $cls->xmi_attributes ) {
      $self->xmi_hbn_attribute($attr, $cls);
    }
    
    # Operations
    for my $op ( $cls->xmi_operations ) {
      $self->xmi_hbn_operation($op, $cls);
    }
    
    # Associations.
    for my $end ( $cls->xmi_association_ends_attached_to ) {
      $self->xmi_hbn_association_end($end, $cls);
    }
  }
  
  for my $subcls ( $cls->xmi_generalization_child ) {
    $self->xmi_hbn_class($subcls, 'subclass');
  }

  if ( $hbn_isEnabled ) {
    $xml->endTag($hbn_type);
  }
}



####################################################################################


sub xmi_doc
{
    my ($self, $doc) = @_;

    my $model = $doc->xmi_model;

    $DB::single = 1;
    
    my $file_suffix = $model->xmi_taggedValue_inherited('uml2code.Java.Hibernate.suffix', '.hbn.xml');
    my $file_name = $model->xmi_taggedValue_inherited('uml2code.Java.Hibernate.file', "uml2code.$file_suffix");

    my $out = $self->{'out'};

    my $xml = new XML::Writer(
			      OUTPUT => $out,
			      NEWLINES => 1,
			      DATA_INDENT => 2,
			      );
    $self->{'xml'} = $xml;

    print $out (qq@
//-// FILE BEGIN $file_name
//-// 
@);

    $xml->startTag('hibernate-mapping');
    
    # Do all the root classes and subsequent subclasses.
    for my $cls ( $self->xmi_hbn_rootClasses($model) ) {
      $self->xmi_hbn_class($cls);
    }

    $xml->endTag('hibernate-mapping');

    print $out (qq@
}
//-// FILE END $file_name
@);

}



####################################################################################

1;

####################################################################################


### Keep these comments at end of file: ks.perl@kurtstephens.com 2003/04/06 ###
### Local Variables: ###
### mode:perl ###
### perl-indent-level:2 ###
### perl-continued-statement-offset:0 ###
### perl-brace-offset:0 ###
### perl-label-offset:0 ###
### End: ###

