File Coverage

blib/lib/Java/Swing.pm
Criterion Covered Total %
statement 45 136 33.0
branch 0 8 0.0
condition 2 4 50.0
subroutine 14 28 50.0
pod 0 11 0.0
total 61 187 32.6


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__