package Inline::Java::Object ; @Inline::Java::Object::ISA = qw(Inline::Java::Object::Tie) ; use strict ; use Inline::Java::Protocol ; use Carp ; $Inline::Java::Object::VERSION = '0.53_90' ; # Here we store as keys the knots and as values our blessed private objects my $PRIVATES = {} ; # Bogus constructor. We fall here if no public constructor is defined # in the Java class. sub new { my $class = shift ; croak "No public constructor defined for class $class" ; } # Constructor. Here we create a new object that will be linked # to a real Java object. sub __new { my $class = shift ; my $java_class = shift ; my $inline = shift ; my $objid = shift ; my $proto = shift ; my $args = shift ; my %this = () ; my $knot = tie %this, $class ; my $this = bless(\%this, $class) ; my $pkg = $inline->get_api('pkg') ; if ($class ne "Inline::Java::Object"){ $class = Inline::Java::java2perl($pkg, $java_class) ; } my $priv = Inline::Java::Object::Private->new($class, $java_class, $inline) ; $PRIVATES->{$knot} = $priv ; if ($objid <= -1){ my $obj = undef ; eval { $obj = $this->__get_private()->{proto}->CreateJavaObject($java_class, $proto, $args) ; } ; croak $@ if $@ ; if (! defined($this->__get_private()->{id})){ # Use created a java::lang::String or something... return $obj ; } } else{ $this->__get_private()->{id} = $objid ; Inline::Java::debug(2, "creating object in java ($class):") ; } Inline::Java::debug_obj($this) ; return $this ; } sub __get_private { my $this = shift ; my $knot = tied(%{$this}) || $this ; my $priv = $PRIVATES->{$knot} ; if (! defined($priv)){ croak "Unknown Java object reference $knot" ; } return $priv ; } # Checks to make sure all the arguments can be "cast" to prototype # types. sub __validate_prototype { my $this = shift ; my $method = shift ; my $args = shift ; my $protos = shift ; my $inline = shift ; my @matched = () ; my @proto_values = values %{$protos} ; my @errors = () ; foreach my $s (@proto_values){ my $proto = $s->{SIGNATURE} ; my $stat = $s->{STATIC} ; my $idx = $s->{IDX} ; my $new_args = undef ; my $score = undef ; my $sig = Inline::Java::Protocol->CreateSignature($proto) ; Inline::Java::debug(3, "matching arguments to $method$sig") ; eval { ($new_args, $score) = Inline::Java::Class::CastArguments($args, $proto, $inline) ; } ; if ($@){ if (scalar(@proto_values) == 1){ # Here we have only 1 prototype, so we return the error. croak $@ ; } push @errors, $@ ; Inline::Java::debug(3, "error trying to fit args to prototype: $@") ; next ; } # We passed! Inline::Java::debug(3, "match successful: score is $score") ; my $h = { PROTO => $proto, NEW_ARGS => $new_args, NB_ARGS => scalar(@{$new_args}), SCORE => $score, STATIC => $stat, IDX => $idx, } ; # Tiny optimization: abort if type coerce was used and matched for # every parameter if (Inline::Java::Class::IsMaxArgumentsScore($new_args, $score)){ Inline::Java::debug(3, "perfect match found, aborting search") ; @matched = () ; push @matched, $h ; last ; } else{ push @matched, $h ; } } my $nb_matched = scalar(@matched) ; if (! $nb_matched){ my $name = (ref($this) ? $this->__get_private()->{class} : $this) ; my $sa = Inline::Java::Protocol->CreateSignature($args) ; my $msg = "In method $method of class $name: Can't find any signature that matches " . "the arguments passed $sa.\nAvailable signatures are:\n" ; my $i = 0 ; foreach my $s (@proto_values){ my $proto = $s->{SIGNATURE} ; my $static = ($s->{STATIC} ? "static " : "") ; my $sig = Inline::Java::Protocol->CreateSignature($proto) ; $msg .= "\t$static$method$sig\n" ; $msg .= "\t\terror was: $errors[$i]" ; $i++ ; } chomp $msg ; croak $msg ; } my $chosen = undef ; foreach my $h (@matched){ my $idx = ($chosen ? $chosen->{IDX} : 0) ; my $max = ($chosen ? $chosen->{SCORE} : 0) ; my $s = $h->{SCORE} ; my $i = $h->{IDX} ; if ($s > $max){ $chosen = $h ; } elsif ($s == $max){ # Here if the scores are equal we take the last one since # we start with inherited methods and move to class mothods if ($i > $idx){ $chosen = $h ; } } } if ((! $chosen->{STATIC})&&(! ref($this))){ # We are trying to call an instance method without an object # reference croak "Method $method of class $this must be called from an object reference" ; } # Here we will be polite and warn the user if we had to choose a # method by ourselves. if ($inline->get_java_config('WARN_METHOD_SELECT')){ if (($nb_matched > 1)&& ($chosen->{SCORE} < ($chosen->{NB_ARGS} * 10))){ my $msg = "Based on the arguments passed, I had to choose between " . "the following method signatures:\n" ; foreach my $m (@matched){ my $s = Inline::Java::Protocol->CreateSignature($m->{PROTO}) ; my $c = ($m eq $chosen ? "*" : " ") ; $msg .= " $c $method$s\n" ; } $msg .= "I chose the one indicated by a star (*). To force " . "the use of another signature or to disable this warning, use " . "the casting functionality described in the documentation." ; carp $msg ; } } return ( $chosen->{PROTO}, $chosen->{NEW_ARGS}, $chosen->{STATIC}, ) ; } sub __isa { my $this = shift ; my $proto = shift ; my $ret = undef ; eval { $ret = $this->__get_private()->{proto}->ISA($proto) ; } ; if ($@){ return ($@, 0) ; } if ($ret == -1){ my $c = $this->__get_private()->{java_class} ; return ("$c is not a kind of $proto", 0) ; } return ('', $ret) ; } sub __cast { my $this = shift ; my $class = shift ; my $ret = $this->__get_private()->{proto}->Cast($class) ; return $ret ; } sub __get_member { my $this = shift ; my $key = shift ; if ($this->__get_private()->{class} eq "Inline::Java::Object"){ croak "Can't get member '$key' for an object that is not bound to Perl" ; } Inline::Java::debug(3, "fetching member variable '$key'") ; my $inline = $this->__get_private()->{inline} ; my $fields = $inline->get_fields($this->__get_private()->{class}) ; my $types = $fields->{$key} ; if (!$types){ my $name = $this->__get_private()->{class} ; croak "No public member variable '$key' defined for class '$name'" ; } my $sign = _get_class($types); my $proto = $sign->{TYPE} ; my $ret = $this->__get_private()->{proto}->GetJavaMember($key, [$proto], [undef]) ; Inline::Java::debug(3, "returning member (" . ($ret || '') . ")") ; return $ret ; } sub _get_class { my ($types) = @_; # We take the last one, which is more specific. Eventually # we should use a scoring method just like for the methods my $sign = undef ; foreach my $s (values %{$types}){ if ((! defined($sign))||($s->{IDX} > $sign->{IDX})){ $sign = $s ; } } return $sign; } sub __set_member { my $this = shift ; my $key = shift ; my $value = shift ; if ($this->__get_private()->{class} eq "Inline::Java::Object"){ croak "Can't set member '$key' for an object that is not bound to Perl" ; } my $inline = $this->__get_private()->{inline} ; my $fields = $inline->get_fields($this->__get_private()->{class}) ; my $types = $fields->{$key} ; if (!$types){ my $name = $this->__get_private()->{class} ; croak "No public member variable '$key' defined for class '$name'" ; } my $sign = _get_class($types); my $proto = $sign->{TYPE} ; my $new_args = undef ; my $score = undef ; ($new_args, $score) = Inline::Java::Class::CastArguments([$value], [$proto], $this->__get_private()->{inline}) ; $this->__get_private()->{proto}->SetJavaMember($key, [$proto], $new_args) ; } sub AUTOLOAD { my $this = shift ; my @args = @_ ; use vars qw($AUTOLOAD) ; my $func_name = $AUTOLOAD ; # Strip package from $func_name, Java will take of finding the correct # method. $func_name =~ s/^(.*)::// ; Inline::Java::debug(5, "$func_name") ; my $name = (ref($this) ? $this->__get_private()->{class} : $this) ; if ($name eq "Inline::Java::Object"){ croak "Can't call method '$func_name' on an object ($name) that is not bound to Perl" ; } croak "No public method '$func_name' defined for class '$name'" ; } sub DESTROY { my $this = shift ; my $knot = tied %{$this} ; if (! $knot){ Inline::Java::debug(4, "destroying Inline::Java::Object::Tie") ; if (! Inline::Java::get_DONE()){ my $class = $this->__get_private()->{class} ; Inline::Java::debug(2, "destroying object in java ($class):") ; { local $@ ; eval { $this->__get_private()->{proto}->DeleteJavaObject($this) ; } ; if ($@){ # We croaked here. Was there already a pending $@? my $name = $this->__get_private()->{class} ; croak "In method DESTROY of class $name: $@" ; } } # Here we have a circular reference so we need to break it # so that the memory is collected. my $priv = $this->__get_private() ; my $proto = $priv->{proto} ; $priv->{proto} = undef ; $proto->{obj_priv} = undef ; delete $PRIVATES->{$this} ; } else{ Inline::Java::debug(4, "script marked as DONE, object destruction not propagated to Java") ; } } else{ Inline::Java::debug(4, "destroying Inline::Java::Object") ; } } ######################## Hash Methods ######################## package Inline::Java::Object::Tie ; @Inline::Java::Object::Tie::ISA = qw(Tie::StdHash) ; use Tie::Hash ; use Carp ; sub TIEHASH { my $class = shift ; return $class->SUPER::TIEHASH(@_) ; } sub STORE { my $this = shift ; my $key = shift ; my $value = shift ; return $this->__set_member($key, $value) ; } sub FETCH { my $this = shift ; my $key = shift ; return $this->__get_member($key) ; } sub FIRSTKEY { my $this = shift ; return $this->SUPER::FIRSTKEY() ; } sub NEXTKEY { my $this = shift ; return $this->SUPER::NEXTKEY() ; } sub EXISTS { my $this = shift ; my $key = shift ; my $inline = $this->__get_private()->{inline} ; my $fields = $inline->get_fields($this->__get_private()->{class}) ; if ($fields->{$key}){ return 1 ; } return 0 ; } sub DELETE { my $this = shift ; my $key = shift ; croak "Operation DELETE not supported on Java object" ; } sub CLEAR { my $this = shift ; croak "Operation CLEAR not supported on Java object" ; } sub DESTROY { my $this = shift ; } ######################## Static Member Methods ######################## package Inline::Java::Object::StaticMember ; @Inline::Java::Object::StaticMember::ISA = qw(Tie::StdScalar) ; use Tie::Scalar ; use Carp ; my $DUMMIES = {} ; sub TIESCALAR { my $class = shift ; my $dummy = shift ; my $name = shift ; my $this = $class->SUPER::TIESCALAR(@_) ; $DUMMIES->{$this} = [$dummy, $name] ; return $this ; } sub STORE { my $this = shift ; my $value = shift ; my ($obj, $key) = @{$DUMMIES->{$this}} ; return $obj->__set_member($key, $value) ; } sub FETCH { my $this = shift ; my ($obj, $key) = @{$DUMMIES->{$this}} ; return $obj->__get_member($key) ; } sub DESTROY { my $this = shift ; } ######################## Private Object ######################## package Inline::Java::Object::Private ; sub new { my $class = shift ; my $obj_class = shift ; my $java_class = shift ; my $inline = shift ; my $this = {} ; $this->{class} = $obj_class ; $this->{java_class} = $java_class ; $this->{inline} = $inline ; $this->{proto} = new Inline::Java::Protocol($this, $inline) ; bless($this, $class) ; return $this ; } sub DESTROY { my $this = shift ; Inline::Java::debug(4, "destroying Inline::Java::Object::Private") ; } 1 ;