Clover icon

Coverage Report

  1. Project Clover database Wed Dec 3 2025 17:03:17 GMT
  2. Package javajs.async

File SwingJSUtils.java

 

Coverage histogram

../../img/srcFileCovDistChart2.png
54% of files have more coverage

Code metrics

76
137
40
4
651
284
87
0.64
3.42
10
2.17

Classes

Class Line # Actions
SwingJSUtils 31 25 14
0.00%
SwingJSUtils.StateMachine 207 0 0
-1.0 -
SwingJSUtils.StateHelper 219 76 58
0.2838709728.4%
SwingJSUtils.Performance 565 36 15
0.00%
 

Contributing tests

This file is covered by 1 test. .

Source view

1    package javajs.async;
2   
3    import java.awt.Component;
4    import java.awt.Dimension;
5    import java.awt.Graphics;
6    import java.awt.Image;
7    import java.awt.event.ActionEvent;
8    import java.awt.event.ActionListener;
9    import java.io.BufferedInputStream;
10    import java.io.BufferedReader;
11    import java.io.ByteArrayInputStream;
12    import java.io.IOException;
13    import java.io.InputStream;
14    import java.io.InputStreamReader;
15    import java.util.stream.Collectors;
16   
17    import javax.imageio.ImageIO;
18    import javax.swing.Timer;
19   
20    /**
21    * A set of generally useful SwingJS-related methods. Includes:
22    *
23    * alternatives to using getCodeBase() for loading resources, due to issues in
24    * Eclipse setting that incorrectly (but no problem in JavaScript)
25    *
26    *
27    *
28    * @author hansonr
29    *
30    */
 
31    public class SwingJSUtils {
32    /**
33    * Set the dimension for the applet prior to j2sApplet's call to
34    * run the applet. Must be used to create a static field:
35    *
36    * <code>
37    * private static Dimension dim =
38    * </code>
39    *
40    *
41    * Then, if it is desired also to have Java also set this, add
42    *
43    * if (dim != null) setSize(dim);
44    *
45    * to the applet's init() method.
46    *
47    * @param w
48    * @param h
49    * @return the Dimension
50    *
51    * @author hansonr
52    */
 
53  0 toggle public static Dimension setDim(int w, int h) {
54  0 String baseURI = (/** @j2sNative document.body.baseURI || */
55    null);
56  0 boolean isTest = (baseURI == null || baseURI.indexOf("_applet.html") >= 0);
57  0 if (!isTest)
58  0 return null;
59    /**
60    * @j2sNative
61    *
62    * J2S.thisApplet.__Info.width = w; J2S.thisApplet.__Info.height = h;
63    */
64  0 return new Dimension(w, h);
65    }
66   
67    /**
68    * Reliably load a resource of a specific type from the code directory
69    *
70    * adaptable - here we are returning an image or a string
71    *
72    * @param cl the classname of the object to return (Image.class,
73    * String.class) null for InputStream
74    * @param filename
75    * @return
76    *
77    * @author hansonr
78    */
 
79  0 toggle public static Object getResource(Class<?> baseClass, String filename, Class<?> cl) {
80  0 System.out.println("mpUtils.SwingJSUtils.getResource " + baseClass.getCanonicalName() + " " + filename);
81  0 InputStream is = baseClass.getResourceAsStream(filename);
82  0 if (cl == Image.class) {
83  0 try {
84  0 return ImageIO.read(is);
85    } catch (IOException e) {
86  0 e.printStackTrace();
87    }
88  0 } else if (cl == String.class) {
89  0 return new BufferedReader(new InputStreamReader(is)).lines().collect(Collectors.joining("\n"));
90    }
91  0 return is;
92    }
93   
94    /**
95    * Pre-fetch images during the static entry of the class. This should provide
96    * plenty of clock ticks, since the file transfer is synchronous, and all we are
97    * waiting for is the DOM image object to initialize.
98    *
99    * @param cl
100    * @param images
101    * @param root
102    * @param nImages
103    * @param ext
104    */
 
105  0 toggle public static void loadImagesStatic(Class<?> cl, Image[] images, String root, String ext, int nImages) {
106  0 for (int i = nImages; --i >= 0;) {
107   
108    // Bild laden und beim MediaTracker registrieren
109    // MediaTracker ladekontrolle = new MediaTracker(this);
110   
111    // BH SwingJS -- adding generally useful method for loading data
112    // avoiding the use of getCodeBase(), which for some reason does not work in
113    // Eclipse.
114   
115  0 images[i] = (Image) getResource(cl, root + i + "." + ext, Image.class);
116    // /**
117    // * @j2sNative
118    // * $("body").append(images[i]._imgNode);
119    // *
120    // */
121    // ladekontrolle.addImage(scharf[i],i);
122    // Warten , bis Bild ganz geladen ist
123   
124    // try {ladekontrolle.waitForID(i);}
125    // catch (InterruptedException e)
126    // {}
127    }
128    }
129   
130    /**
131    * Fill an array with images based on a String[] listing
132    * @param cl reference class
133    * @param root optional root path, ending in "/"
134    * @param names source file names
135    * @param images array to fill
136    */
 
137  0 toggle public static void loadImagesStatic(Class<?> cl, String root, String[] names, Image[] images) {
138  0 for (int i = names.length; --i >= 0;) {
139  0 images[i] = (Image) getResource(cl, root + names[i], Image.class);
140    }
141    }
142   
143    /**
144    * Eclipse-friendly image getting
145    *
146    * @param c
147    * @param fileName
148    * @return
149    */
 
150  0 toggle public static Image getImage(Component c, String fileName) {
151  0 return getImage(c.getClass(), fileName);
152    }
153   
154    /**
155    * Eclipse-friendly image getting
156    *
157    * @param c
158    * @param fileName
159    * @return
160    */
 
161  0 toggle public static Image getImage(Class<?> c, String fileName) {
162  0 return (Image) getResource(c, fileName, Image.class);
163    }
164   
165    /**
166    * Clear the component graphic. BH added this for JavaScript because changing
167    * the browser zoom can change the size of the canvas for unknown reasons.
168    *
169    * @param c
170    */
 
171  0 toggle public static void clearComponent(Component c) {
172  0 Graphics gc = c.getGraphics();
173  0 gc.clearRect(0, 0, c.getWidth(), c.getHeight());
174  0 gc.dispose();
175    }
176   
177   
178    /**
179    * A simple interface to the machine loop, generally of the form
180    * <code>
181    * public boolean stateLoop() {
182    * while (stateHepler.isAlive()) {
183    * switch (stateHelper.getState()) {
184    * case STATE_XXX:
185    * ...
186    * return stateHelper.delayState(100,STATE_YYY);
187    * case STATE_YYY:
188    * ...
189    * stateHelper.setState(STATE_ZZZ);
190    * continue;
191    * case STATE_ZZZ:
192    * ...
193    * return stateHelper.delayAction(100, MY_ID, "myCommand", myListener, STATE_XXX); *
194    * case STATE_DONE:
195    * ...
196    * stateHelper.interrupt();
197    * return false;
198    * }
199    * return true;
200    * }
201    * return false;
202    * }
203    * </code>
204    * @author hansonr
205    *
206    */
 
207    public interface StateMachine {
208   
209    public boolean stateLoop();
210   
211    }
212    /**
213    * StateHelper is a class that facilitates moving from an asychronous multithreaded model to a state-oriented model of programming
214    * for SwingJS animations and other asynchronous business.
215    *
216    * @author hansonr
217    *
218    */
 
219    public static class StateHelper {
220   
221    public static final int UNCHANGED = Integer.MIN_VALUE;
222   
223    private StateMachine machine;
224    private int state;
225    private int level;
226   
227    private boolean interrupted;
228   
229   
 
230  11 toggle public StateHelper(StateMachine machine) {
231  11 this.machine = machine;
232    }
233   
 
234  0 toggle public void interrupt() {
235  0 interrupted = true;
236    }
237   
 
238  0 toggle public boolean isInterrupted() {
239  0 return interrupted;
240    }
241   
 
242  0 toggle public boolean isAlive() {
243  0 return !interrupted;
244    }
245   
 
246  0 toggle public void restart() {
247  0 interrupted = false;
248    }
249   
 
250  11 toggle public void setState(int state) {
251  11 this.state = this.stateNext = state;
252    }
253   
 
254  27 toggle public int getState() {
255  27 return state;
256    }
257   
 
258  0 toggle public void setLevel(int level) {
259  0 this.level = this.levelNext = level;
260    }
261   
 
262  0 toggle public int getLevel() {
263  0 return level;
264    }
265   
 
266  0 toggle public void setNextState(int next) {
267  0 stateNext = next;
268    }
269   
 
270  0 toggle public int getNextState() {
271  0 return stateNext;
272    }
273   
 
274  0 toggle public int getNextLevel() {
275  0 return levelNext;
276    }
277   
 
278  0 toggle public void setNextLevel(int next) {
279  0 levelNext = next;
280    }
281   
282    /**
283    *
284    * NOTE: this method must remain private; it is accessed via p$1
285    *
286    * @return
287    */
 
288  5 toggle private boolean nextState() {
289  5 return next(stateNext, levelNext);
290    }
291    /**
292    * Set the state and run machine.stateLoop().
293    *
294    * @param state something meaningful to the machine
295    *
296    * @return not interrupted
297    *
298    * @author Bob Hanson hansonr@stolaf.edu
299    */
 
300  11 toggle public boolean next(int state) {
301  11 return next(state, 0);
302    }
303   
304    /**
305    * Set the state and level, and then run machine.stateLoop(). Driven directly or via delayedState or delayedAction
306    *
307    * @param state something meaningful to the machine
308    * @param level something meaningful to the machine
309    *
310    * @return not interrupted
311    *
312    * @author Bob Hanson hansonr@stolaf.edu
313    */
 
314  16 toggle public boolean next(int state, int level) {
315  16 return nextStatePriv(this, state, level);
316    }
317   
 
318  16 toggle private static boolean nextStatePriv(Object oThis, int state, int level) {
319  16 StateHelper me = (StateHelper) oThis;
320  16 if (me.interrupted)
321  0 return false;
322  16 if (level != UNCHANGED)
323  16 me.level = level;
324  16 if (state != UNCHANGED)
325  16 me.state = state;
326  16 return me.machine.stateLoop();
327    }
328   
329    /**
330    * After the given number of milliseseconds, set the new state and run the machines stateLoop with unchanged level
331    *
332    * @param ms the number of milliseconds to delay; 0 to execute synchronously *
333    * @param stateNext the next state to run
334    * @return not interrupted
335    *
336    * @author Bob Hanson hansonr@stolaf.edu
337    */
 
338  11 toggle public boolean delayedState(int ms, int stateNext) {
339  11 return delayedState(ms, stateNext, level);
340    }
341   
342    private Timer stateTimer;
343   
344    private int stateNext;
345    private int levelNext;
346   
347    /**
348    * After the given number of milliseseconds, set the new state and level, and
349    * run the machines stateLoop
350    *
351    * @param ms the number of milliseconds to delay; 0 to execute
352    * synchronously *
353    * @param stateNext the next state
354    * @param levelNext the next level
355    * @return not interrupted
356    *
357    * @author Bob Hanson hansonr@stolaf.edu
358    */
359   
 
360  11 toggle public boolean delayedState(int ms, int stateNext, int levelNext) {
361  11 if (interrupted)
362  0 return false;
363  11 if (ms == 0)
364  0 return next(stateNext, levelNext);
365  11 if (stateNext != UNCHANGED)
366  11 this.stateNext = stateNext;
367  11 if (levelNext != UNCHANGED)
368  11 this.levelNext = levelNext;
369   
370    /**
371    * @j2sNative
372    * var me = this;
373    * setTimeout(function(){
374    * p$1.nextState.apply(me, []);
375    * },ms);
376    */
377    {
378    // Java only
379   
380  11 if (stateTimer == null) {
381  11 stateTimer = new Timer(ms, new ActionListener() {
 
382  5 toggle @Override
383    public void actionPerformed(ActionEvent e) {
384  5 nextState();
385    }
386   
387    });
388  11 stateTimer.setRepeats(false);
389  11 stateTimer.start();
390    } else {
391  0 stateTimer.restart();
392    }
393    }
394  11 return true;
395    }
396   
397    /**
398    * Fire an actionPerformed event after a given number of milliseconds
399    *
400    * @param ms delay milliseconds. if 0, then this action will be called
401    * synchronously
402    * @param id id for this event, possibly ACTION_PERFORMED (1001), but not
403    * necessarily
404    * @param command key for ActionEvent.getCommand()
405    * @param listener ActionListener to be called.
406    * @return not interrupted
407    *
408    * @author Bob Hanson hansonr@stolaf.edu
409    */
 
410  0 toggle public boolean delayedAction(int ms, int id, String command, ActionListener listener) {
411  0 return delayedAction(ms, id, command, listener, UNCHANGED, UNCHANGED);
412    }
413   
414    /**
415    * Fire an actionPerformed event after a given number of milliseconds
416    *
417    * @param ms delay milliseconds. if 0, then this action will be called
418    * synchronously
419    * @param id id for this event, possibly ACTION_PERFORMED (1001), but not
420    * necessarily
421    * @param command key for ActionEvent.getCommand()
422    * @param listener ActionListener to be called.
423    *
424    * @param state the next state to go to after this listener is called; UNCHANGED to let the listener take care of this.
425    *
426    * @return not interrupted
427    *
428    * @author Bob Hanson hansonr@stolaf.edu
429    */
 
430  0 toggle public boolean delayedAction(int ms, int id, String command, ActionListener listener, int state) {
431  0 return delayedAction(ms, id, command, listener, state, UNCHANGED);
432    }
433   
434    /**
435    * Fire an actionPerformed event after a given number of milliseconds. Setting BOTH stateNext and levelNext to UNCHANGED (Integer.MIN_VALUE)
436    * allows the listener to handle continuing the loop.
437    *
438    * @param ms delay milliseconds. if 0, then this action will be called
439    * synchronously
440    * @param id id for this event, possibly ACTION_PERFORMED (1001), but not
441    * necessarily
442    * @param command key for ActionEvent.getCommand()
443    * @param listener ActionListener to be called.
444    * @param stateNext state to run after the event is processed by the listener, or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle this.
445    * @param levelNext level to run after the event is processed by the listener, or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle this.
446    * @return not interrupted
447    *
448    * @author Bob Hanson hansonr@stolaf.edu
449    */
 
450  0 toggle public boolean delayedAction(int ms, int id, String command, ActionListener listener, int stateNext, int levelNext) {
451  0 if (interrupted)
452  0 return false;
453  0 ActionEvent event = new ActionEvent(this, id, command);
454  0 if (ms == 0) {
455  0 listener.actionPerformed(event);
456  0 return (stateNext == UNCHANGED && levelNext == UNCHANGED || nextStatePriv(this, stateNext == UNCHANGED ? state : stateNext, levelNext == UNCHANGED ? level : levelNext));
457    }
458   
459  0 StateHelper me = this;
460   
461  0 Timer timer = new Timer(ms, id == ActionEvent.ACTION_PERFORMED ? listener : new ActionListener() {
 
462  0 toggle @Override
463    public void actionPerformed(ActionEvent e) {
464  0 if (!interrupted)
465  0 listener.actionPerformed(event);
466  0 if (!interrupted && (stateNext != UNCHANGED || levelNext != UNCHANGED))
467  0 nextStatePriv(me, stateNext == UNCHANGED ? state : stateNext, levelNext == UNCHANGED ? level : levelNext);
468    }
469   
470    });
471  0 timer.setRepeats(false);
472  0 timer.start();
473  0 return true;
474    }
475   
 
476  0 toggle public static void delayedRun(int ms, Runnable runnable) {
477  0 new StateHelper(null).delayedRun(ms, runnable, UNCHANGED, UNCHANGED);
478    }
479   
480   
481    /**
482    * Fire an actionPerformed event after a given number of milliseconds. Setting
483    * BOTH stateNext and levelNext to UNCHANGED (Integer.MIN_VALUE) allows the
484    * listener to handle continuing the loop.
485    *
486    * @param ms delay milliseconds. if 0, then this action will be called
487    * synchronously
488    * @param id id for this event, possibly ACTION_PERFORMED (1001), but not
489    * necessarily
490    * @param command key for ActionEvent.getCommand()
491    * @param listener ActionListener to be called.
492    * @param stateNext state to run after the event is processed by the listener,
493    * or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle
494    * this.
495    * @param levelNext level to run after the event is processed by the listener,
496    * or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle
497    * this.
498    * @return not interrupted
499    *
500    * @author Bob Hanson hansonr@stolaf.edu
501    */
 
502  0 toggle public boolean delayedRun(int ms, Runnable runnable, int stateNext, int levelNext) {
503  0 if (interrupted)
504  0 return false;
505  0 if (ms == 0) {
506  0 return (stateNext == UNCHANGED && levelNext == UNCHANGED || nextStateIfUnchanged(this, runnable,
507  0 stateNext == UNCHANGED ? state : stateNext, levelNext == UNCHANGED ? level : levelNext));
508    }
509  0 StateHelper me = this;
510    /**
511    * @j2sNative
512    *
513    * setTimeout(function() {
514    *
515    * me.nextStateIfUnchanged$O$O$I$I.apply(me, [me, runnable, stateNext, levelNext]);
516    *
517    * },ms);
518    */
519    {
520  0 Timer timer = new Timer(ms, new ActionListener() {
 
521  0 toggle @Override
522    public void actionPerformed(ActionEvent e) {
523  0 nextStateIfUnchanged(me, runnable, stateNext, levelNext);
524    }
525   
526    });
527  0 timer.setRepeats(false);
528  0 timer.start();
529    }
530  0 return true;
531    }
532   
 
533  0 toggle protected boolean nextStateIfUnchanged(Object oThis, Object runnable, int stateNext, int levelNext) {
534  0 StateHelper me = (StateHelper)(oThis);
535  0 if (!me.interrupted)
536  0 ((Runnable) runnable).run();
537  0 if (!me.interrupted && (stateNext != UNCHANGED || levelNext != UNCHANGED))
538  0 nextStatePriv(oThis, stateNext == UNCHANGED ? me.state : stateNext,
539  0 levelNext == UNCHANGED ? me.level : levelNext);
540  0 return true;
541    }
542   
543    /**
544    * sleep and then execute the next state
545    * @param ms
546    */
 
547  0 toggle public void sleep(int ms) {
548  0 int next = stateNext;
549  0 delayedState(ms, next);
550    }
551    }
552   
553    /**
554    * open a "url-like" input stream
555    * @param base
556    * @param fileName
557    * @return
558    */
 
559  0 toggle public static BufferedInputStream openStream(Class<?> base, String fileName) {
560  0 String s = (String) getResource(base, fileName, String.class);
561  0 return new BufferedInputStream(new ByteArrayInputStream(s.getBytes()));
562    }
563   
564   
 
565    public static class Performance {
566   
567    public final static int TIME_RESET = 0;
568   
569    public final static int TIME_MARK = 1;
570   
571    public static final int TIME_SET = 2;
572   
573    public static final int TIME_GET = 3;
574   
575    public static long time, mark, set, duration;
576   
577    /**
578    * typical usage:
579    *
580    * Performance.timeCheck(null, Platform.TIME_MARK);
581    *
582    * ...
583    *
584    * Performance.timeCheck("some message", Platform.TIME_MARK);
585    *
586    * reset...[set/mark]n...get (total time) (time spent between set and mark)
587    *
588    * set...get (total time) (time spent between set and get)
589    *
590    * long t0 = now(0); ........ ; dt = now(t0); (time since t0)e
591    *
592    * @param msg
593    * @param mode
594    */
 
595  0 toggle public static void timeCheck(String msg, int mode) {
596  0 msg = timeCheckStr(msg, mode);
597  0 if (msg != null)
598  0 System.err.println(msg);
599    }
600   
 
601  0 toggle public static long now(long t) {
602  0 return System.currentTimeMillis() - t;
603    }
604   
 
605  0 toggle public static String timeCheckStr(String msg, int mode) {
606  0 long t = System.currentTimeMillis();
607  0 switch (mode) {
608  0 case TIME_RESET:
609  0 time = mark = t;
610  0 duration = set = 0;
611  0 if (msg != null) {
612  0 return ("Platform: timer reset\t\t\t" + msg);
613    }
614  0 break;
615  0 case TIME_SET:
616  0 if (time == 0)
617  0 time = t;
618  0 set = t;
619  0 break;
620  0 case TIME_MARK:
621  0 if (set > 0) {
622    // total time between set/mark points
623  0 duration += (t - set);
624    } else {
625  0 if (time == 0) {
626  0 time = mark = t;
627    }
628  0 if (msg != null) {
629  0 long m0 = mark;
630  0 mark = t;
631  0 return ("Platform: timer mark\t" + ((t - time) / 1000f) + "\t" + ((t - m0) / 1000f) + "\t"
632    + msg);
633    }
634  0 mark = t;
635    }
636  0 break;
637  0 case TIME_GET:
638  0 if (msg != null) {
639  0 if (mark < set)
640  0 duration = t - set;
641  0 return ("Platform: timer get\t" + ((t - time) / 1000f) + "\t" + ((duration) / 1000f) + "\t" + msg);
642    }
643  0 set = 0;
644  0 break;
645    }
646  0 return null;
647    }
648   
649    }
650   
651    }