| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Java::Swing; | 
| 2 | 8 |  |  | 8 |  | 204905 | use strict; use warnings; | 
|  | 8 |  |  | 8 |  | 18 |  | 
|  | 8 |  |  |  |  | 274 |  | 
|  | 8 |  |  |  |  | 34 |  | 
|  | 8 |  |  |  |  | 18 |  | 
|  | 8 |  |  |  |  | 225 |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 | 8 |  |  | 8 |  | 40 | use Carp; | 
|  | 8 |  |  |  |  | 16 |  | 
|  | 8 |  |  |  |  | 789 |  | 
| 5 | 8 |  |  | 8 |  | 8355 | use Inline Java => 'DATA'; | 
|  | 8 |  |  |  |  | 194108 |  | 
|  | 8 |  |  |  |  | 62 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | our $VERSION = '0.14'; | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | my %callbacks; | 
| 10 |  |  |  |  |  |  | my %listeners; | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | sub import { | 
| 13 |  |  |  |  |  |  | # To add packages to the Java::Swing scheme, see PerlRealPackages.pm. | 
| 14 |  |  |  |  |  |  | # Make your additions in PerlRealLocalPackages.pm or | 
| 15 |  |  |  |  |  |  | # PerlFakeLocalPackages.pm.  Use Real for modules that have a .pm file | 
| 16 |  |  |  |  |  |  | # and Fake for modules this package should fabricate. | 
| 17 |  |  |  |  |  |  |  | 
| 18 | 8 |  |  | 8 |  | 5995 | use Java::Swing::PerlRealPackages; | 
|  | 8 |  |  |  |  | 23 |  | 
|  | 8 |  |  |  |  | 199 |  | 
| 19 | 8 |  |  | 8 |  | 3814 | use Java::Swing::PerlRealLocalPackages; | 
|  | 8 |  |  |  |  | 19 |  | 
|  | 8 |  |  |  |  | 305 |  | 
| 20 | 8 |  |  | 8 |  | 68 | foreach my $package ( | 
| 21 |  |  |  |  |  |  | @Java::Swing::PerlRealPackages::packages, | 
| 22 |  |  |  |  |  |  | @Java::Swing::PerlRealLocalPackages::local_packages, | 
| 23 |  |  |  |  |  |  | ) { | 
| 24 | 8 |  |  | 8 |  | 41 | no strict; | 
|  | 8 |  |  |  |  | 10 |  | 
|  | 8 |  |  |  |  | 420 |  | 
| 25 | 24 |  |  |  |  | 42 | *{"$package\::AUTOLOAD"} = \&module_autoload; | 
|  | 24 |  |  |  |  | 201 |  | 
| 26 |  |  |  |  |  |  | } | 
| 27 |  |  |  |  |  |  |  | 
| 28 | 8 |  |  | 8 |  | 4075 | use Java::Swing::PerlFakePackages; | 
|  | 8 |  |  |  |  | 19 |  | 
|  | 8 |  |  |  |  | 212 |  | 
| 29 | 8 |  |  | 8 |  | 3619 | use Java::Swing::PerlFakeLocalPackages; | 
|  | 8 |  |  |  |  | 21 |  | 
|  | 8 |  |  |  |  | 535 |  | 
| 30 | 8 |  |  |  |  | 174 | my %package_names = ( | 
| 31 |  |  |  |  |  |  | %Java::Swing::PerlFakePackages::names, | 
| 32 |  |  |  |  |  |  | %Java::Swing::PerlFakeLocalPackages::names, | 
| 33 |  |  |  |  |  |  | ); | 
| 34 | 8 |  |  |  |  | 96 | foreach my $package ( | 
| 35 |  |  |  |  |  |  | @Java::Swing::PerlFakePackages::packages, | 
| 36 |  |  |  |  |  |  | @Java::Swing::PerlFakeLocalPackages::local_packages, | 
| 37 |  |  |  |  |  |  | ) { | 
| 38 | 8 |  |  | 8 |  | 37 | no strict; | 
|  | 8 |  |  |  |  | 13 |  | 
|  | 8 |  |  |  |  | 1721 |  | 
| 39 | 2448 |  |  |  |  | 5571 | *{"$package\::AUTOLOAD"} = | 
|  | 2448 |  |  |  |  | 38421 |  | 
| 40 |  |  |  |  |  |  | gen_fake_module_autoload($package_names{$package}); | 
| 41 |  |  |  |  |  |  | } | 
| 42 |  |  |  |  |  |  | } | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | sub module_autoload { | 
| 45 | 0 |  |  | 0 | 0 | 0 | my $invocant = $_[0]; | 
| 46 | 0 |  |  |  |  | 0 | my $command  = our $AUTOLOAD; | 
| 47 | 0 |  |  |  |  | 0 | my $required = "Java/Swing/$invocant.pm"; | 
| 48 |  |  |  |  |  |  |  | 
| 49 | 0 |  |  |  |  | 0 | require $required; | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 0 |  |  |  |  | 0 | goto &$command; | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | sub gen_fake_module_autoload { | 
| 55 | 2448 |  | 100 | 2448 | 0 | 8081 | my $package_name = shift || 'javax.swing'; | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | return sub { | 
| 58 | 0 |  |  | 0 |  |  | my $invocant      = $_[0]; | 
| 59 | 0 |  |  |  |  |  | my $java_invocant = $invocant; | 
| 60 | 0 |  |  |  |  |  | $java_invocant    =~ s/::/./g; | 
| 61 | 0 |  |  |  |  |  | my $command       = our $AUTOLOAD; | 
| 62 | 0 |  |  |  |  |  | my $studied       = "$package_name.$java_invocant"; | 
| 63 |  |  |  |  |  |  |  | 
| 64 | 0 |  |  |  |  |  | Inline->bind( | 
| 65 |  |  |  |  |  |  | Java       => 'STUDY', | 
| 66 |  |  |  |  |  |  | SHARED_JVM => 1, | 
| 67 |  |  |  |  |  |  | AUTOSTUDY  => 1, | 
| 68 |  |  |  |  |  |  | STUDY      => [ $studied ], | 
| 69 |  |  |  |  |  |  | ); | 
| 70 |  |  |  |  |  |  |  | 
| 71 | 8 |  |  | 8 |  | 39 | no strict; | 
|  | 8 |  |  |  |  | 12 |  | 
|  | 8 |  |  |  |  | 6544 |  | 
| 72 | 0 |  |  |  |  |  | *{"$invocant\::new"} = gen_generic_new($package_name); | 
|  | 0 |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  |  | 
| 74 | 0 |  |  |  |  |  | goto &$command; | 
| 75 |  |  |  |  |  |  | } | 
| 76 | 2448 |  |  |  |  | 7593 | } | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | sub gen_generic_new { | 
| 79 | 0 |  |  | 0 | 0 |  | my $package_name = shift; | 
| 80 | 0 |  |  |  |  |  | $package_name    =~ s/\./::/g; | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | return sub { | 
| 83 | 0 |  |  | 0 |  |  | my $class        = shift; | 
| 84 | 0 |  |  |  |  |  | my $inline_class = "Java\::Swing\::$package_name\::$class"; | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | # if we were passed a hash reference, construct a default object, then | 
| 87 |  |  |  |  |  |  | # call set on each hash key with the value | 
| 88 | 0 | 0 |  |  |  |  | if (ref($_[0]) =~ /HASH/) { | 
| 89 | 0 |  |  |  |  |  | my $attributes = shift; | 
| 90 | 0 |  |  |  |  |  | my $retval; | 
| 91 |  |  |  |  |  |  |  | 
| 92 | 0 | 0 |  |  |  |  | if ( defined $attributes->{ Object } ) { | 
| 93 | 0 |  |  |  |  |  | my $init_object = delete $attributes->{ Object }; | 
| 94 | 0 |  |  |  |  |  | $retval = $inline_class->new( $init_object ); | 
| 95 |  |  |  |  |  |  | } | 
| 96 |  |  |  |  |  |  | else { | 
| 97 | 0 |  |  |  |  |  | $retval = $inline_class->new(); | 
| 98 |  |  |  |  |  |  | } | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 0 |  |  |  |  |  | foreach my $attribute (keys %$attributes) { | 
| 101 | 0 |  |  |  |  |  | my $setter = "set" . ucfirst($attribute); | 
| 102 | 0 |  |  |  |  |  | eval { $retval->$setter($attributes->{$attribute}); }; | 
|  | 0 |  |  |  |  |  |  | 
| 103 | 0 | 0 |  |  |  |  | if ($@) { | 
| 104 | 0 |  |  |  |  |  | croak "Error: '$attribute' is not an attribute of $class"; | 
| 105 |  |  |  |  |  |  | } | 
| 106 |  |  |  |  |  |  | } | 
| 107 | 0 |  |  |  |  |  | return $retval; | 
| 108 |  |  |  |  |  |  | } | 
| 109 |  |  |  |  |  |  | else { # pass args directly to constructor | 
| 110 | 0 |  |  |  |  |  | return $inline_class->new(@_); | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  | } | 
| 113 | 0 |  |  |  |  |  | } | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | # ------------------------------------------------------------------- | 
| 116 |  |  |  |  |  |  | # -----     Methods for Java::Swing objects                     ----- | 
| 117 |  |  |  |  |  |  | # ------------------------------------------------------------------- | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | sub new { | 
| 120 | 0 |  |  | 0 | 0 |  | my $class = shift; | 
| 121 | 0 |  |  |  |  |  | my $self  = {}; | 
| 122 |  |  |  |  |  |  |  | 
| 123 | 0 |  |  |  |  |  | $self->{OBJECT} = Java::Swing::Swinger->new(); | 
| 124 |  |  |  |  |  |  |  | 
| 125 | 0 |  |  |  |  |  | return bless $self, $class; | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | sub start { | 
| 129 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 130 |  |  |  |  |  |  |  | 
| 131 | 0 |  |  |  |  |  | $self->{OBJECT}->StartCallbackLoop(); | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | sub stop { | 
| 135 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 136 |  |  |  |  |  |  |  | 
| 137 | 0 |  |  |  |  |  | $self->{OBJECT}->StopCallbackLoop(); | 
| 138 |  |  |  |  |  |  | } | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | # See Timer.pm for an example of using the delayed approach.  To summarize, | 
| 141 |  |  |  |  |  |  | # if you need to pass your listener to the constructor of your sender, | 
| 142 |  |  |  |  |  |  | # call delayed_connect to get your listener.  Pass it to the constructor. | 
| 143 |  |  |  |  |  |  | # Then call finish_delayed_connect with all three pieces (listener, sender, | 
| 144 |  |  |  |  |  |  | # and callbacks). | 
| 145 |  |  |  |  |  |  | sub delayed_connect { | 
| 146 | 0 |  |  | 0 | 0 |  | my $invocant         = shift;  # not used | 
| 147 | 0 |  |  |  |  |  | my $listener_type    = shift; | 
| 148 | 0 |  |  |  |  |  | my $studied          = "Perl$listener_type"; | 
| 149 |  |  |  |  |  |  |  | 
| 150 | 0 |  |  |  |  |  | Inline->bind( | 
| 151 |  |  |  |  |  |  | Java       => 'STUDY', | 
| 152 |  |  |  |  |  |  | SHARED_JVM => 1, | 
| 153 |  |  |  |  |  |  | AUTOSTUDY  => 1, | 
| 154 |  |  |  |  |  |  | STUDY      => [ $studied ], | 
| 155 |  |  |  |  |  |  | ); | 
| 156 |  |  |  |  |  |  |  | 
| 157 | 0 |  |  |  |  |  | my $listening_class  = 'Java::Swing::' | 
| 158 |  |  |  |  |  |  | . "Perl$listener_type"; | 
| 159 |  |  |  |  |  |  |  | 
| 160 | 0 |  |  |  |  |  | my $listener         = $listening_class->new(); | 
| 161 |  |  |  |  |  |  |  | 
| 162 | 0 |  |  |  |  |  | return $listener; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | sub finish_delayed_connect { | 
| 166 | 0 |  |  | 0 | 0 |  | my $invocant         = shift;  # not used | 
| 167 | 0 |  |  |  |  |  | my $listener         = shift; | 
| 168 | 0 |  |  |  |  |  | my $sender           = shift; | 
| 169 | 0 |  |  |  |  |  | my $callbacks        = shift; | 
| 170 | 0 |  |  |  |  |  | my $send_name        = ref $sender;     # stringify these | 
| 171 | 0 |  |  |  |  |  | my $call_name        = ref $callbacks; | 
| 172 |  |  |  |  |  |  |  | 
| 173 | 0 |  |  |  |  |  | $callbacks{$send_name}{$call_name} = $callbacks; | 
| 174 | 0 |  |  |  |  |  | $listeners{$send_name}{$call_name} = $listener; | 
| 175 |  |  |  |  |  |  |  | 
| 176 | 0 |  |  |  |  |  | $listener->setSender($send_name); | 
| 177 | 0 |  |  |  |  |  | $listener->setCallbacks($call_name); | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | sub connect { | 
| 181 | 0 |  |  | 0 | 0 |  | my $invocant         = shift;  # not used | 
| 182 | 0 |  |  |  |  |  | my $listener_type    = shift; | 
| 183 | 0 |  |  |  |  |  | my $sender           = shift; | 
| 184 | 0 |  |  |  |  |  | my $callbacks        = shift; | 
| 185 | 0 |  |  |  |  |  | my $studied          = "Perl$listener_type"; | 
| 186 |  |  |  |  |  |  |  | 
| 187 | 0 |  |  |  |  |  | Inline->bind( | 
| 188 |  |  |  |  |  |  | Java       => 'STUDY', | 
| 189 |  |  |  |  |  |  | SHARED_JVM => 1, | 
| 190 |  |  |  |  |  |  | AUTOSTUDY  => 1, | 
| 191 |  |  |  |  |  |  | STUDY      => [ $studied ], | 
| 192 |  |  |  |  |  |  | ); | 
| 193 |  |  |  |  |  |  |  | 
| 194 | 0 |  |  |  |  |  | delegate_connection($listener_type, $sender, $callbacks); | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | sub delegate_connection { | 
| 198 | 0 |  |  | 0 | 0 |  | my $listener_type    = shift; | 
| 199 | 0 |  |  |  |  |  | my $sender           = shift; | 
| 200 | 0 |  |  |  |  |  | my $callbacks        = shift; | 
| 201 | 0 |  |  |  |  |  | my $send_name        = ref $sender;     # stringify these | 
| 202 | 0 |  |  |  |  |  | my $call_name        = ref $callbacks; | 
| 203 |  |  |  |  |  |  |  | 
| 204 | 0 |  |  |  |  |  | my $listening_class  = 'Java::Swing::' | 
| 205 |  |  |  |  |  |  | . "Perl$listener_type"; | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 0 |  |  |  |  |  | my $listener         = $listening_class->new($send_name, $call_name); | 
| 208 |  |  |  |  |  |  |  | 
| 209 | 0 |  |  |  |  |  | $callbacks{$send_name}{$call_name} = $callbacks; | 
| 210 | 0 |  |  |  |  |  | $listeners{$send_name}{$call_name} = $listener; | 
| 211 |  |  |  |  |  |  |  | 
| 212 | 0 |  |  |  |  |  | my $add_method       = "add$listener_type"; | 
| 213 | 0 |  |  |  |  |  | $sender->$add_method($listener); | 
| 214 |  |  |  |  |  |  | } | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | # XXX not tested | 
| 217 |  |  |  |  |  |  | sub disconnect { | 
| 218 | 0 |  |  | 0 | 0 |  | my $invocant         = shift;  # not used | 
| 219 | 0 |  |  |  |  |  | my $listener_type    = shift; | 
| 220 | 0 |  |  |  |  |  | my $sender           = shift; | 
| 221 | 0 |  |  |  |  |  | my $callbacks        = shift; | 
| 222 | 0 |  |  |  |  |  | my $listener_package = "Java::Swing::$listener_type"; | 
| 223 |  |  |  |  |  |  |  | 
| 224 | 0 |  |  |  |  |  | $listener_package->disconnect($sender, $callbacks); | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | sub _Listener {  # for the private use of our java class friends. | 
| 228 | 0 |  |  | 0 |  |  | my $sender_name      = shift; | 
| 229 | 0 |  |  |  |  |  | my $callbacks_name   = shift; | 
| 230 | 0 |  |  |  |  |  | my $triggered_method = shift; | 
| 231 | 0 |  |  |  |  |  | my $event            = shift; | 
| 232 |  |  |  |  |  |  |  | 
| 233 | 0 | 0 |  |  |  |  | my $methods = $callbacks{$sender_name}{$callbacks_name} | 
| 234 |  |  |  |  |  |  | or die "No registered callback for $sender_name $callbacks_name\n"; | 
| 235 | 0 |  | 0 | 0 |  |  | my $method = $methods->{$triggered_method} || sub {}; | 
|  | 0 |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  |  | 
| 237 | 8 |  |  | 8 |  | 46 | no strict; | 
|  | 8 |  |  |  |  | 17 |  | 
|  | 8 |  |  |  |  | 1129 |  | 
| 238 | 0 |  |  |  |  |  | $method->($sender_name, $event); | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | 1; | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | =head1 NAME | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | Java::Swing - Perl extension providing direct access to the Java Swing API | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | BEGIN { $ENV{CLASSPATH} .= ':/path/to/Java/Swing/java'; } | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | use Java::Swing; | 
| 252 |  |  |  |  |  |  | my $swinger = Java::Swing->new(); | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | my $frame  = JFrame->new(); | 
| 255 |  |  |  |  |  |  | my $button = JButton->new( { label => 'Press Me' } ); | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | $frame->getContentPane()->add($button); | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | $swinger->connect( | 
| 260 |  |  |  |  |  |  | 'ActionListener', $button, { actionPerformed => \&myListener } | 
| 261 |  |  |  |  |  |  | ); | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | my $timer = Timer->new(10, { actionPerformed => \&timer_catcher }); | 
| 264 |  |  |  |  |  |  | $timer->start(); | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | $swinger->start(); | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | sub mylistener { | 
| 269 |  |  |  |  |  |  | my $generating_object = shift; | 
| 270 |  |  |  |  |  |  | my $event             = shift; | 
| 271 |  |  |  |  |  |  | print "Hello, Rob!\n"; | 
| 272 |  |  |  |  |  |  | } | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | my $count = 1; | 
| 275 |  |  |  |  |  |  | sub timer_catcher { | 
| 276 |  |  |  |  |  |  | print "Timer went off " . $count++; | 
| 277 |  |  |  |  |  |  | } | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | =head1 ABSTRACT | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | Provides direct access to the Java Swing toolkit from Perl. | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | Though you can write a Java program which is driven by Perl, some people | 
| 286 |  |  |  |  |  |  | may prefer to keep their Perl pure.  This package lets you do that in manner | 
| 287 |  |  |  |  |  |  | similar to the way Perl/Tk and Gtk2:: provide access to their underlying | 
| 288 |  |  |  |  |  |  | libraries.  This lets us code in our favorite language, while using the | 
| 289 |  |  |  |  |  |  | graphical interface capabilities of Java Swing. | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | =head1 EXAMPLE | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | In the example directory of the distribution there is an example called | 
| 294 |  |  |  |  |  |  | calc.  Here I will walk through it a bit at a time.  To see it in one | 
| 295 |  |  |  |  |  |  | piece look in the untarred distribution.  To run it after make | 
| 296 |  |  |  |  |  |  | and before installing Java::Swing type: | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | perl -I blib/lib example/calc | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | After installation just use: | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | perl example/calc | 
| 303 |  |  |  |  |  |  |  | 
| 304 |  |  |  |  |  |  | But remember to change the path separators to fit your OS. | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | use strict; use warnings; | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | # This program provides an example of a simple Java::Swing application. | 
| 309 |  |  |  |  |  |  | # Type an expression in the top text box, press evaluate, and see the | 
| 310 |  |  |  |  |  |  | # answer in the other box. | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | As the comment tries to say, this program displays two JTextFields and | 
| 313 |  |  |  |  |  |  | a JButton.  When the button is pressed, the expression in the first field | 
| 314 |  |  |  |  |  |  | is eval'd and the result is placed in the second field. | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | # Note that to make the example work, you must have the directory containing | 
| 317 |  |  |  |  |  |  | # the Java::Swing classes in the classpath.  In the distribution this is | 
| 318 |  |  |  |  |  |  | # called java. | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | BEGIN { $ENV{CLASSPATH} .= ':java' } | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | No changes to the CLASSPATH will be effective if they come after | 
| 323 |  |  |  |  |  |  | use Java::Swing, so put them in a BEGIN block before that statement. | 
| 324 |  |  |  |  |  |  | The classes in the java directory of the distribution provide support | 
| 325 |  |  |  |  |  |  | for event listeners. | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | use Java::Swing; | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | This innocuous looking statement actually sets up the aliases that make it | 
| 330 |  |  |  |  |  |  | easy to refer to Java Swing classes.  In particular, it sets up namespaces | 
| 331 |  |  |  |  |  |  | for each Component so you can refer to them directly as shown immediately | 
| 332 |  |  |  |  |  |  | below.  It does not load the Java classes until you actually use them. | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | my $expression  = JTextField->new(); | 
| 335 |  |  |  |  |  |  | my $answer      = JTextField->new( { columns => 10 } ); | 
| 336 |  |  |  |  |  |  | my $submit      = JButton   ->new("Evaluate"); | 
| 337 |  |  |  |  |  |  | my $frame       = JFrame    ->new(); | 
| 338 |  |  |  |  |  |  | my $root_pane   = $frame    ->getContentPane(); | 
| 339 |  |  |  |  |  |  | my $south_panel = JPanel    ->new(); | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  | Once you use Java::Swing, you can refer to javax.swing classes by their | 
| 342 |  |  |  |  |  |  | class name alone as if it name were a Perl package name.  All class methods, | 
| 343 |  |  |  |  |  |  | including constructors, can be called as normal through this Perl package | 
| 344 |  |  |  |  |  |  | name. | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | But, if you like, you may also use Java::Swing named attribute construction, | 
| 347 |  |  |  |  |  |  | as shown for the second JTextField above.  Simply supply a hash reference | 
| 348 |  |  |  |  |  |  | whose keys are attributes of the class with the proper values.  Your object | 
| 349 |  |  |  |  |  |  | will be constructed by calling the empty argument constructor.  Then the | 
| 350 |  |  |  |  |  |  | attribute values will be supplied by calling set accessors.  So columns => 10 | 
| 351 |  |  |  |  |  |  | will translate into setColumns(10). | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | As of version 0.12, you may add an Object attribute to the constructor | 
| 354 |  |  |  |  |  |  | hash.  Then Java::Swing will call the constructor on the underlying class | 
| 355 |  |  |  |  |  |  | which expects it, and then call set accessors for any additional attributes. | 
| 356 |  |  |  |  |  |  | For example: | 
| 357 |  |  |  |  |  |  |  | 
| 358 |  |  |  |  |  |  | my $label = JLabel->new( | 
| 359 |  |  |  |  |  |  | { Object => $icon, | 
| 360 |  |  |  |  |  |  | text   => 'caption', } | 
| 361 |  |  |  |  |  |  | ); | 
| 362 |  |  |  |  |  |  | Thanks to Andreas Puerzer for suggesting this additional sugar. | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | Continuing with the example: | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | $south_panel->add(JLabel->new("Answer:"), "West"  ); | 
| 367 |  |  |  |  |  |  | $south_panel->add($answer,                "Center"); | 
| 368 |  |  |  |  |  |  | $south_panel->add($submit,                "East"  ); | 
| 369 |  |  |  |  |  |  |  | 
| 370 |  |  |  |  |  |  | $root_pane->add($expression,  "North"); | 
| 371 |  |  |  |  |  |  | $root_pane->add($south_panel, "South"); | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | $frame->setSize(300, 100); | 
| 374 |  |  |  |  |  |  | $frame->show(); | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | These lines perform Component layout.  If you are not familiar with layouts in | 
| 377 |  |  |  |  |  |  | Java Swing (which has the same scheme as awt), consult a book (O'Reilly | 
| 378 |  |  |  |  |  |  | has more than one that will do, try the Java Foundation Classes in a Nutshell | 
| 379 |  |  |  |  |  |  | or Java Swing). | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | my $swinger = Java::Swing->new(); | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | At some point, you must obtain a Java::Swing object.  Through it, you | 
| 384 |  |  |  |  |  |  | stop and start event listening.  It also allows you to connect listeners | 
| 385 |  |  |  |  |  |  | directly to Perl code. | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | $swinger->connect( | 
| 388 |  |  |  |  |  |  | "ActionListener", $submit, { actionPerformed => \&evaluate } | 
| 389 |  |  |  |  |  |  | ); | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | $swinger->connect( | 
| 392 |  |  |  |  |  |  | "WindowListener", $frame, { windowClosing => \&ending } | 
| 393 |  |  |  |  |  |  | ); | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | Call connect through your Java::Swing object passing it the listener type, | 
| 396 |  |  |  |  |  |  | the object to listen to, and a hash reference whose keys are all the events | 
| 397 |  |  |  |  |  |  | you care about.  The values in the hash must be code references.  These | 
| 398 |  |  |  |  |  |  | will be called when the event is triggered.  They will receive the stringified | 
| 399 |  |  |  |  |  |  | name of the sending object (the originator of the event) and the event object. | 
| 400 |  |  |  |  |  |  | If you need the actual sending object, ask the event for it (try getSource). | 
| 401 |  |  |  |  |  |  | You only need to supply the events you care about.  Leave others out, | 
| 402 |  |  |  |  |  |  | default no-ops will be called for them. | 
| 403 |  |  |  |  |  |  |  | 
| 404 |  |  |  |  |  |  | If you plan to disconnect, you need to store the hash reference in a | 
| 405 |  |  |  |  |  |  | variable, so that you can pass EXACTLY the same arguments to the disconnect | 
| 406 |  |  |  |  |  |  | method.  It is not enough to have the same data in the hash reference, it | 
| 407 |  |  |  |  |  |  | must be the same reference. | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | If you want multiple routines called for the same event, call connect | 
| 410 |  |  |  |  |  |  | repeatedly.  Give it a different hash reference each time, even if that | 
| 411 |  |  |  |  |  |  | reference refers to the same name/code ref pairs. | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | $swinger->start(); | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | After everything is ready, call start on your Java::Swing object.  Once you | 
| 416 |  |  |  |  |  |  | do this Java takes over control with its event loop.  This probably makes | 
| 417 |  |  |  |  |  |  | Java::Swing incompatible with other packages that want to manage the main | 
| 418 |  |  |  |  |  |  | loop, like POE (if you can make such packages cooperate, please advise me | 
| 419 |  |  |  |  |  |  | on how it is done). | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | sub evaluate { | 
| 422 |  |  |  |  |  |  | my $sender_name = shift; | 
| 423 |  |  |  |  |  |  | my $event       = shift; | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | $answer->setText(eval $expression->getText()); | 
| 426 |  |  |  |  |  |  | } | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | Here, evaluate pays no attention to the arguments it receives, but they | 
| 429 |  |  |  |  |  |  | are included so you can see how they come in.  Instead, the text in the | 
| 430 |  |  |  |  |  |  | expression box is passed directly to eval (yes, security is important here, | 
| 431 |  |  |  |  |  |  | don't set uid).  That answer is directly placed in the answer field via | 
| 432 |  |  |  |  |  |  | setText. | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | sub ending { | 
| 435 |  |  |  |  |  |  | $swinger->stop(); | 
| 436 |  |  |  |  |  |  | } | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | When you receive control via an event, you can stop the Java event loop by | 
| 439 |  |  |  |  |  |  | calling stop on your Java::Swing object.  Here that happens when the user | 
| 440 |  |  |  |  |  |  | closes the window.  When you stop the event loop, your program terminates. | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | =head1 EXTENDING | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | While I have tried to provide all the Swing you will ever need, there | 
| 445 |  |  |  |  |  |  | are inevitably some things I have not gotten to.  Further, you may have | 
| 446 |  |  |  |  |  |  | in house code which you would like to incoroprate into this scheme. | 
| 447 |  |  |  |  |  |  | This section explains the pieces needed to use other code as part of | 
| 448 |  |  |  |  |  |  | Java::Swing. | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | =head2 Adding Components or Families of Them | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | While most of the Components from Swing are implemented, AWT, SWT and other | 
| 453 |  |  |  |  |  |  | kits are not implemented (though parts of AWT are).  Most Components have | 
| 454 |  |  |  |  |  |  | the same basic plan.  They are part of Swing.  They have a no argument | 
| 455 |  |  |  |  |  |  | constructor and accessors for all of their attributes.  If that describes | 
| 456 |  |  |  |  |  |  | your widget, there is only one thing to do: | 
| 457 |  |  |  |  |  |  |  | 
| 458 |  |  |  |  |  |  | Add the base name of your class to the @packages list in | 
| 459 |  |  |  |  |  |  | Swing::PerlFakeLocalPackages | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  | If your widget is not at the top level of its package include the | 
| 462 |  |  |  |  |  |  | subdirectories leading from the package to it like so: | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | text::html::AccessibleHTML | 
| 465 |  |  |  |  |  |  |  | 
| 466 |  |  |  |  |  |  | If the widget has all of the above traits, but is in a different package, | 
| 467 |  |  |  |  |  |  | you should still add it to C<@packages> in C. | 
| 468 |  |  |  |  |  |  | But, you also need to add an entry for it in C<%names> like so: | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | YourWidget => 'com.yourcompany.package', | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | If you are adding lots of widgets, you'll want to automate this, but | 
| 473 |  |  |  |  |  |  | I don't have a lot of advice on how to do so.  I did it once and forgot | 
| 474 |  |  |  |  |  |  | the scripts.  I don't think they were complex. | 
| 475 |  |  |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | If the Java class is standard (for some sense of standard), send the module | 
| 477 |  |  |  |  |  |  | to me so I can add it to future distributions. | 
| 478 |  |  |  |  |  |  |  | 
| 479 |  |  |  |  |  |  | =head2 Adding Listeners | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | In Java::Swing, events are handled through callbacks to Perl code.  To make | 
| 482 |  |  |  |  |  |  | the callbacks happen, you need a Java class which implements the | 
| 483 |  |  |  |  |  |  | listener interface.  As of version 0.10, you no longer need a corresponding | 
| 484 |  |  |  |  |  |  | Perl module, the code from those modules is now implemented once in | 
| 485 |  |  |  |  |  |  | Swing.pm.  It is possible to hand code the Java listener implementation, | 
| 486 |  |  |  |  |  |  | but it is a pain. | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | There are two real possibilities.  Either you have a single listener or | 
| 489 |  |  |  |  |  |  | you have several.  In the first case, you can use listener_generator. | 
| 490 |  |  |  |  |  |  | This script is not installed, but can be found in the Swing/Generate | 
| 491 |  |  |  |  |  |  | subdirectory of the distribution.  Details follow. | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | =head3 One Listener at a Time | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | Create a file describing the listener with a valid Perl hash reference in it | 
| 496 |  |  |  |  |  |  | like this: | 
| 497 |  |  |  |  |  |  |  | 
| 498 |  |  |  |  |  |  | { | 
| 499 |  |  |  |  |  |  | 'listener' => 'TreeWillExpandListener', | 
| 500 |  |  |  |  |  |  | 'methods' => [ | 
| 501 |  |  |  |  |  |  | { | 
| 502 |  |  |  |  |  |  | 'name' => 'treeWillExpand', | 
| 503 |  |  |  |  |  |  | 'type' => 'javax.swing.event.TreeExpansionEvent', | 
| 504 |  |  |  |  |  |  | 'throws' => 'javax.swing.tree.ExpandVetoException' | 
| 505 |  |  |  |  |  |  | }, | 
| 506 |  |  |  |  |  |  | { | 
| 507 |  |  |  |  |  |  | 'name' => 'treeWillCollapse', | 
| 508 |  |  |  |  |  |  | 'type' => 'javax.swing.event.TreeExpansionEvent', | 
| 509 |  |  |  |  |  |  | 'throws' => 'javax.swing.tree.ExpandVetoException' | 
| 510 |  |  |  |  |  |  | } | 
| 511 |  |  |  |  |  |  | ], | 
| 512 |  |  |  |  |  |  | 'full_name' => 'javax.swing.event.TreeWillExpandListener' | 
| 513 |  |  |  |  |  |  | } | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | Always use all three keys: | 
| 516 |  |  |  |  |  |  |  | 
| 517 |  |  |  |  |  |  | =over 4 | 
| 518 |  |  |  |  |  |  |  | 
| 519 |  |  |  |  |  |  | =item full_name | 
| 520 |  |  |  |  |  |  |  | 
| 521 |  |  |  |  |  |  | The fully qualified name of the listener interface. | 
| 522 |  |  |  |  |  |  |  | 
| 523 |  |  |  |  |  |  | =item listener | 
| 524 |  |  |  |  |  |  |  | 
| 525 |  |  |  |  |  |  | The interface name (short form, not qualified). | 
| 526 |  |  |  |  |  |  |  | 
| 527 |  |  |  |  |  |  | =item methods | 
| 528 |  |  |  |  |  |  |  | 
| 529 |  |  |  |  |  |  | An anonymous list of hash references.  Each hash needs two keys: | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  | =over 4 | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | =item name (required) | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | The name of the method. | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | =item type (required) | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | The type of event the method receives. | 
| 540 |  |  |  |  |  |  |  | 
| 541 |  |  |  |  |  |  | =item throws (optional) | 
| 542 |  |  |  |  |  |  |  | 
| 543 |  |  |  |  |  |  | The type of event the method throws. | 
| 544 |  |  |  |  |  |  |  | 
| 545 |  |  |  |  |  |  | =back | 
| 546 |  |  |  |  |  |  |  | 
| 547 |  |  |  |  |  |  | =back | 
| 548 |  |  |  |  |  |  |  | 
| 549 |  |  |  |  |  |  | Once you have a file describing your listener, run C | 
| 550 |  |  |  |  |  |  | with that file as the only command line argument.  This writes to standard | 
| 551 |  |  |  |  |  |  | out.  Save the resulting file as C.  When you | 
| 552 |  |  |  |  |  |  | compile the java file, include the Inline classes in the CLASSPATH, these | 
| 553 |  |  |  |  |  |  | will be in a directory like | 
| 554 |  |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | /cpan/modules/Inline-Java-0.47/Java/classes | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | (During run time, Inline::Java makes sure these are in the CLASSPATH.) | 
| 558 |  |  |  |  |  |  |  | 
| 559 |  |  |  |  |  |  | =head3 Families of Listeners | 
| 560 |  |  |  |  |  |  |  | 
| 561 |  |  |  |  |  |  | When adding a whole new toolkit (like swt) you need to add all the listeners. | 
| 562 |  |  |  |  |  |  | To do this use the following steps (all scripts mentioned are in the | 
| 563 |  |  |  |  |  |  | Swing/Generate directory of the distribution): | 
| 564 |  |  |  |  |  |  |  | 
| 565 |  |  |  |  |  |  | =over 4 | 
| 566 |  |  |  |  |  |  |  | 
| 567 |  |  |  |  |  |  | =item 1 | 
| 568 |  |  |  |  |  |  |  | 
| 569 |  |  |  |  |  |  | Use C, giving it the package name and the directory where | 
| 570 |  |  |  |  |  |  | source and class files live.  (Note that it relies on a hard coded path | 
| 571 |  |  |  |  |  |  | to your rt.jar, change that to the correct location.) | 
| 572 |  |  |  |  |  |  | (If you don't have sources, you'll have to change the script substantially.) | 
| 573 |  |  |  |  |  |  | The output comes to standard out, store it in a file. | 
| 574 |  |  |  |  |  |  |  | 
| 575 |  |  |  |  |  |  | =item 2 | 
| 576 |  |  |  |  |  |  |  | 
| 577 |  |  |  |  |  |  | Use C, giving it the name of the file from step one along with | 
| 578 |  |  |  |  |  |  | directory for the java pieces. | 
| 579 |  |  |  |  |  |  |  | 
| 580 |  |  |  |  |  |  | =item 3 | 
| 581 |  |  |  |  |  |  |  | 
| 582 |  |  |  |  |  |  | Compile the java programs.  Remember to include the Inline::Java classes | 
| 583 |  |  |  |  |  |  | in the CLASSPATH for compilation (they are supplied for you at run time). | 
| 584 |  |  |  |  |  |  | On my machine these are in /usr/src/Inline-Java-0.47/Java/classes. | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | =item 4 | 
| 587 |  |  |  |  |  |  |  | 
| 588 |  |  |  |  |  |  | Make sure that the classes from step 3 are in the CLASSPATH for all | 
| 589 |  |  |  |  |  |  | scripts that need them. | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | =back | 
| 592 |  |  |  |  |  |  |  | 
| 593 |  |  |  |  |  |  | =head2 Adding Constant Interfaces | 
| 594 |  |  |  |  |  |  |  | 
| 595 |  |  |  |  |  |  | Since C is based on C it is inherently a remote | 
| 596 |  |  |  |  |  |  | procedure call system.  Among other things this means that only methods | 
| 597 |  |  |  |  |  |  | can be called from one language to another.  Constants cannot be seen. | 
| 598 |  |  |  |  |  |  | Therefore, if you have constants in an interface, or even in a class, | 
| 599 |  |  |  |  |  |  | you must provide methods for these, typically in both java and perl. | 
| 600 |  |  |  |  |  |  |  | 
| 601 |  |  |  |  |  |  | To see how to do this, consult C in the java | 
| 602 |  |  |  |  |  |  | directory of the distribution and its pair C in | 
| 603 |  |  |  |  |  |  | the Swing directory. | 
| 604 |  |  |  |  |  |  |  | 
| 605 |  |  |  |  |  |  | There is not currently an automated way to build these wrappers. | 
| 606 |  |  |  |  |  |  |  | 
| 607 |  |  |  |  |  |  | =head2 EXPORT | 
| 608 |  |  |  |  |  |  |  | 
| 609 |  |  |  |  |  |  | None. | 
| 610 |  |  |  |  |  |  |  | 
| 611 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 612 |  |  |  |  |  |  |  | 
| 613 |  |  |  |  |  |  | The documentation above is, of course, incomplete.  It gives you the | 
| 614 |  |  |  |  |  |  | spirit of using the kit.  The real documentation is the official | 
| 615 |  |  |  |  |  |  | Java documentation for the version of the jdk you have installed. | 
| 616 |  |  |  |  |  |  |  | 
| 617 |  |  |  |  |  |  | Particular Java::Swing:: modules may have additional Perl specific | 
| 618 |  |  |  |  |  |  | documentation.  See the Swing directory for these modules. | 
| 619 |  |  |  |  |  |  |  | 
| 620 |  |  |  |  |  |  | Questions about this module may be addressed to the author or posted to | 
| 621 |  |  |  |  |  |  | the Inline mailing list to which the author and other interested parties | 
| 622 |  |  |  |  |  |  | subscribe. | 
| 623 |  |  |  |  |  |  |  | 
| 624 |  |  |  |  |  |  | =head1 AUTHOR | 
| 625 |  |  |  |  |  |  |  | 
| 626 |  |  |  |  |  |  | Phil Crow, Ephilcrow2000@yahoo.comE | 
| 627 |  |  |  |  |  |  |  | 
| 628 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 629 |  |  |  |  |  |  |  | 
| 630 |  |  |  |  |  |  | Copyright 2004 by Philip Crow | 
| 631 |  |  |  |  |  |  |  | 
| 632 |  |  |  |  |  |  | This library is free software; you can redistribute it and/or modify | 
| 633 |  |  |  |  |  |  | it under the same terms as Perl 5.8.0 itself. | 
| 634 |  |  |  |  |  |  |  | 
| 635 |  |  |  |  |  |  | =cut | 
| 636 |  |  |  |  |  |  |  | 
| 637 |  |  |  |  |  |  | __DATA__ |