Clover icon

Coverage Report

  1. Project Clover database Wed Dec 3 2025 17:03:17 GMT
  2. Package jalview.util

File Platform.java

 

Coverage histogram

../../img/srcFileCovDistChart5.png
43% of files have more coverage

Code metrics

140
249
64
2
1,228
707
171
0.69
3.89
32
2.67

Classes

Class Line # Actions
Platform 72 221 154
0.4534313745.3%
Platform.PlatformDependentValue 275 28 17
0.4222222342.2%
 

Contributing tests

This file is covered by 829 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.util;
22    import java.awt.Component;
23    import java.awt.Dimension;
24    import java.awt.GraphicsEnvironment;
25   
26    import java.awt.Toolkit;
27    import java.awt.event.KeyEvent;
28    import java.awt.event.MouseEvent;
29    import java.io.BufferedReader;
30    import java.io.File;
31    import java.io.FileOutputStream;
32    import java.io.FileReader;
33    import java.io.IOException;
34    import java.io.InputStream;
35    import java.io.InputStreamReader;
36    import java.io.Reader;
37    import java.lang.reflect.Method;
38    import java.net.URL;
39    import java.nio.channels.Channels;
40    import java.nio.channels.ReadableByteChannel;
41    import java.nio.file.Files;
42    import java.nio.file.Path;
43    import java.nio.file.Paths;
44    import java.nio.file.StandardCopyOption;
45    import java.nio.file.attribute.BasicFileAttributes;
46    import java.util.Date;
47    import java.util.Locale;
48    import java.util.Map;
49    import java.util.Objects;
50    import java.util.Properties;
51    import java.util.logging.ConsoleHandler;
52    import java.util.logging.Level;
53    import java.util.logging.Logger;
54   
55    import javax.swing.SwingUtilities;
56   
57    import org.json.simple.parser.JSONParser;
58    import org.json.simple.parser.ParseException;
59   
60   
61    import com.stevesoft.pat.Regex;
62   
63    import jalview.bin.Jalview;
64    import jalview.javascript.json.JSON;
65    import swingjs.api.JSUtilI;
66   
67    /**
68    * System platform information used by Applet and Application
69    *
70    * @author Jim Procter
71    */
 
72    public class Platform
73    {
74   
75    private static boolean isJS = /** @j2sNative true || */
76    false;
77   
78    private static Boolean isNoJSMac = null, isNoJSWin = null, isMac = null,
79    isWin = null, isLinux = null;
80   
81    private static Boolean isHeadless = null;
82   
83    // If launched from CLI with launcher script then -DCOLUMNWIDTH is set
84    private static final int CONSOLEWIDTH;
85   
86    private static final String CONSOLEWIDTHPROPERTY = "CONSOLEWIDTH";
87   
88    private static swingjs.api.JSUtilI jsutil;
89   
90    /**
91    * added to group mouse events into Windows and nonWindows (mac, unix, linux)
92    *
93    * @return
94    */
 
95  853 toggle public static boolean isMac()
96    {
97  853 return (isMac == null
98    ? (isMac = (System.getProperty("os.name").indexOf("Mac") >= 0))
99    : isMac);
100    }
101   
102    public static int SHORTCUT_KEY_MASK = (Platform.isMac()
103    ? KeyEvent.META_DOWN_MASK
104    : KeyEvent.CTRL_DOWN_MASK);
105   
106   
 
107  55 toggle static
108    {
109  55 int cw = 80;
110  55 if (System.getProperty(CONSOLEWIDTHPROPERTY) != null
111    && System.getProperty(CONSOLEWIDTHPROPERTY).length() > 0)
112    {
113  0 try
114    {
115  0 cw = Integer.parseInt(System.getProperty(CONSOLEWIDTHPROPERTY));
116    } catch (NumberFormatException e)
117    {
118    }
119    }
120  55 CONSOLEWIDTH = cw;
121  55 if (isJS)
122    {
123  0 try
124    {
125    // this is ok - it's a highly embedded method in Java; the deprecation
126    // is
127    // really a recommended best practice.
128  0 jsutil = ((JSUtilI) Class.forName("swingjs.JSUtil").newInstance());
129    } catch (InstantiationException | IllegalAccessException
130    | ClassNotFoundException e)
131    {
132  0 e.printStackTrace();
133    }
134    }
135  55 if (!GraphicsEnvironment.isHeadless())
136    {
137    // Using non-deprecated Extended key mask modifiers, but Java 8 has no
138    // getMenuShortcutKeyMaskEx method
139  19 Toolkit tk = Toolkit.getDefaultToolkit();
140  19 Method method = null;
141  19 try
142    {
143  19 method = tk.getClass().getMethod("getMenuShortcutKeyMaskEx");
144    } catch (Exception e)
145    {
146  0 System.err.println(
147    "Could not find Toolkit method getMenuShortcutKeyMaskEx. Trying getMenuShortcutKeyMask.");
148    }
149  19 if (method == null)
150    {
151  0 try
152    {
153  0 method = tk.getClass().getMethod("getMenuShortcutKeyMask");
154    } catch (Exception e)
155    {
156  0 System.err.println(
157    "Could not find Toolkit method getMenuShortcutKeyMaskEx or getMenuShortcutKeyMask.");
158  0 e.printStackTrace();
159    }
160    }
161  19 if (method != null)
162    {
163  19 try
164    {
165  19 method.setAccessible(true);
166  19 SHORTCUT_KEY_MASK = ((int) method.invoke(tk, new Object[0]));
167    } catch (Exception e)
168    {
169  0 e.printStackTrace();
170    }
171    }
172  19 if (SHORTCUT_KEY_MASK <= 0xF)
173    {
174    // shift this into the extended region (was Java 8)
175  0 SHORTCUT_KEY_MASK = SHORTCUT_KEY_MASK << 6;
176    }
177    }
178    }
179    /**
180    * added to group mouse events into Windows and nonWindows (mac, unix, linux)
181    *
182    * @return
183    */
 
184  22 toggle public static boolean isWin()
185    {
186  22 return (isWin == null
187    ? (isWin = (System.getProperty("os.name").indexOf("Win") >= 0))
188    : isWin);
189    }
190   
191    /**
192    * added to check LaF for Linux
193    *
194    * @return
195    */
 
196  249 toggle public static boolean isLinux()
197    {
198  249 return (isLinux == null
199    ? (isLinux = (System.getProperty("os.name")
200    .indexOf("Linux") >= 0))
201    : isLinux);
202    }
203   
204    /**
205    *
206    * @return true if HTML5 JavaScript
207    */
 
208  2670697 toggle public static boolean isJS()
209    {
210  2670707 return isJS;
211    }
212   
213    /**
214    * sorry folks - Macs really are different
215    *
216    * BH: disabled for SwingJS -- will need to check key-press issues
217    *
218    * @return true if we do things in a special way.
219    */
 
220  4949 toggle public static boolean isAMacAndNotJS()
221    {
222  4949 return (isNoJSMac == null ? (isNoJSMac = !isJS && isMac()) : isNoJSMac);
223    }
224   
225    /**
226    * Check if we are on a Microsoft platform...
227    *
228    * @return true if we have to cope with another platform variation
229    */
 
230  11 toggle public static boolean isWindowsAndNotJS()
231    {
232  11 return (isNoJSWin == null ? (isNoJSWin = !isJS && isWin()) : isNoJSWin);
233    }
234   
235    /**
236    *
237    * @return true if we are running in non-interactive no UI mode
238    */
 
239  1008 toggle public static boolean isHeadless()
240    {
241  1008 if (isHeadless == null)
242    {
243  21 isHeadless = "true".equals(System.getProperty("java.awt.headless"));
244    }
245  1008 return isHeadless;
246    }
247   
248    /**
249    * Construct the value that depends on the system architecture. The methods
250    * setting the value for subsequent platforms are chained after this call and
251    * finalized with a {@link PlatformDependentValue#value() value()} call.
252    *
253    * Example: {@code
254    * Platform.forArch(120).forMac(114).forWin(112).forLinux(115).value();
255    * }
256    *
257    * @param <T>
258    * type of the value
259    * @param defaultValue
260    * default value used if platform not determined
261    * @return platform dependent value wrapper object
262    */
 
263  1 toggle public static <T> PlatformDependentValue<T> forArch(T defaultValue)
264    {
265  1 return new PlatformDependentValue<T>(defaultValue);
266    }
267   
268    /**
269    *
270    * @author mmwarowny
271    *
272    * @param <T>
273    * type of the value
274    */
 
275    public static class PlatformDependentValue<T>
276    {
277    private T defaultValue = null;
278   
279    private T macValue = null;
280   
281    private T winValue = null;
282   
283    private T linuxValue = null;
284   
285    private T jsValue = null;
286   
287    private T headlessValue = null;
288   
 
289  1 toggle private PlatformDependentValue(T value)
290    {
291  1 Objects.requireNonNull(value);
292  1 defaultValue = value;
293    }
294   
295    /**
296    * Set the value used on Mac platform.
297    *
298    * @param value
299    * parameter value
300    * @return
301    */
 
302  0 toggle public PlatformDependentValue<T> forMac(T value)
303    {
304  0 Objects.requireNonNull(value);
305  0 macValue = value;
306  0 return this;
307    }
308   
309    /**
310    * Set the value used on Windows platform.
311    *
312    * @param value
313    * parameter value
314    * @return
315    */
 
316  1 toggle public PlatformDependentValue<T> forWin(T value)
317    {
318  1 Objects.requireNonNull(value);
319  1 winValue = value;
320  1 return this;
321    }
322   
323    /**
324    * Set the value used on Linux platform.
325    *
326    * @param value
327    * parameter value
328    * @return
329    */
 
330  0 toggle public PlatformDependentValue<T> forLinux(T value)
331    {
332  0 Objects.requireNonNull(value);
333  0 linuxValue = value;
334  0 return this;
335    }
336   
337    /**
338    * Set the value used on JS platform.
339    *
340    * @param value
341    * parameter value
342    * @return
343    */
 
344  0 toggle public PlatformDependentValue<T> forJS(T value)
345    {
346  0 Objects.requireNonNull(value);
347  0 jsValue = value;
348  0 return this;
349    }
350   
351    /**
352    * Set the value used on headless platform. The headless value takes
353    * precedence over other platforms if set.
354    *
355    * @param value
356    * parameter value
357    * @return
358    */
 
359  0 toggle public PlatformDependentValue<T> forHeadless(T value)
360    {
361  0 Objects.requireNonNull(value);
362  0 headlessValue = value;
363  0 return this;
364    }
365   
366    /**
367    * Get the value of the parameter respecting the platform. The headless
368    * platform takes precedence over any other platform if it has the value
369    * set.
370    *
371    * @return parameter value depending on the platform
372    */
 
373  1 toggle public T value()
374    {
375  1 if (headlessValue != null && isHeadless())
376  0 return headlessValue;
377  1 if (macValue != null && isMac())
378  0 return macValue;
379  1 if (winValue != null && isWin())
380  0 return winValue;
381  1 if (linuxValue != null && isLinux())
382  0 return linuxValue;
383  1 if (jsValue != null && isJS())
384  0 return jsValue;
385  1 return defaultValue;
386    }
387    }
388   
389    /**
390    *
391    * @return nominal maximum command line length for this platform
392    */
 
393  0 toggle public static int getMaxCommandLineLength()
394    {
395    // TODO: determine nominal limits for most platforms.
396  0 return 2046; // this is the max length for a windows NT system.
397    }
398   
399    /**
400    * Answers the input with every backslash replaced with a double backslash (an
401    * 'escaped' single backslash)
402    *
403    * @param s
404    * @return
405    */
 
406  95 toggle public static String escapeBackslashes(String s)
407    {
408  95 return s == null ? null : s.replace("\\", "\\\\");
409    }
410   
411    /**
412    * Answers true if the mouse event has Meta-down (Command key on Mac) or
413    * Ctrl-down (on other o/s). Note this answers _false_ if the Ctrl key is
414    * pressed instead of the Meta/Cmd key on Mac. To test for Ctrl-pressed on
415    * Mac, you can use e.isPopupTrigger().
416    *
417    * @param e
418    * @return
419    */
 
420  3 toggle public static boolean isControlDown(MouseEvent e)
421    {
422  3 return isControlDown(e, isMac());
423    }
424   
425    /**
426    * Overloaded version of method (to allow unit testing)
427    *
428    * @param e
429    * @param aMac
430    * @return
431    */
 
432  6 toggle protected static boolean isControlDown(MouseEvent e, boolean aMac)
433    {
434    //
435    // System.out.println(e.isPopupTrigger()
436    // + " " + ((SHORTCUT_KEY_MASK & e.getModifiersEx()) != 0)
437    // + " " + e.isControlDown());
438  6 return (aMac
439    ? !e.isPopupTrigger()
440    && (SHORTCUT_KEY_MASK & e.getModifiersEx()) != 0
441    : e.isControlDown());
442    }
443   
444    // BH: I don't know about that previous method. Here is what SwingJS uses.
445    // Notice the distinction in mouse events. (BUTTON3_MASK == META)
446    //
447    // private static boolean isPopupTrigger(int id, int mods, boolean isWin) {
448    // boolean rt = ((mods & InputEvent.BUTTON3_MASK) != 0);
449    // if (isWin) {
450    // if (id != MouseEvent.MOUSE_RELEASED)
451    // return false;
452    ////
453    //// // Oddly, Windows returns InputEvent.META_DOWN_MASK on release, though
454    //// // BUTTON3_DOWN_MASK for pressed. So here we just accept both.
455    ////
456    //// actually, we can use XXX_MASK, not XXX_DOWN_MASK and avoid this issue,
457    // because
458    //// J2S adds the appropriate extended (0x3FC0) and simple (0x3F) modifiers.
459    ////
460    // return rt;
461    // } else {
462    // // mac, linux, unix
463    // if (id != MouseEvent.MOUSE_PRESSED)
464    // return false;
465    // boolean lt = ((mods & InputEvent.BUTTON1_MASK) != 0);
466    // boolean ctrl = ((mods & InputEvent.CTRL_MASK) != 0);
467    // return rt || (ctrl && lt);
468    // }
469    // }
470    //
471   
472    /**
473    * Windows (not Mac, Linux, or Unix) and right button to test for the
474    * right-mouse pressed event in Windows that would have opened a menu or a
475    * Mac.
476    *
477    * @param e
478    * @return
479    */
 
480  3 toggle public static boolean isWinRightButton(MouseEvent e)
481    {
482    // was !isAMac(), but that is true also for Linux and Unix and JS,
483   
484  3 return isWin() && SwingUtilities.isRightMouseButton(e);
485    }
486   
487    /**
488    * Windows (not Mac, Linux, or Unix) and middle button -- for mouse wheeling
489    * without pressing the button.
490    *
491    * @param e
492    * @return
493    */
 
494  0 toggle public static boolean isWinMiddleButton(MouseEvent e)
495    {
496    // was !isAMac(), but that is true also for Linux and Unix and JS
497  0 return isWin() && SwingUtilities.isMiddleMouseButton(e);
498    }
499   
 
500  498 toggle public static boolean allowMnemonics()
501    {
502  498 return !isMac();
503    }
504   
505    public final static int TIME_RESET = 0;
506   
507    public final static int TIME_MARK = 1;
508   
509    public static final int TIME_SET = 2;
510   
511    public static final int TIME_GET = 3;
512   
513    public static long time, mark, set, duration;
514   
515    /**
516    * typical usage:
517    *
518    * Platform.timeCheck(null, Platform.TIME_MARK);
519    *
520    * ...
521    *
522    * Platform.timeCheck("some message", Platform.TIME_MARK);
523    *
524    * reset...[set/mark]n...get
525    *
526    * @param msg
527    * @param mode
528    */
 
529  0 toggle public static void timeCheck(String msg, int mode)
530    {
531  0 long t = System.currentTimeMillis();
532  0 switch (mode)
533    {
534  0 case TIME_RESET:
535  0 time = mark = t;
536  0 duration = 0;
537  0 if (msg != null)
538    {
539  0 jalview.bin.Console.errPrintln("Platform: timer reset\t\t\t" + msg);
540    }
541  0 break;
542  0 case TIME_MARK:
543  0 if (set > 0)
544    {
545    // total time between set/mark points
546  0 duration += (t - set);
547    }
548    else
549    {
550  0 if (time == 0)
551    {
552  0 time = mark = t;
553    }
554  0 if (msg != null)
555    {
556  0 jalview.bin.Console.errPrintln(
557    "Platform: timer mark\t" + ((t - time) / 1000f) + "\t"
558    + ((t - mark) / 1000f) + "\t" + msg);
559    }
560  0 mark = t;
561    }
562  0 break;
563  0 case TIME_SET:
564  0 set = t;
565  0 break;
566  0 case TIME_GET:
567  0 if (msg != null)
568    {
569  0 jalview.bin.Console
570    .errPrintln("Platform: timer dur\t" + ((t - time) / 1000f)
571    + "\t" + ((duration) / 1000f) + "\t" + msg);
572    }
573  0 set = 0;
574  0 break;
575    }
576    }
577   
 
578  0 toggle public static void cacheFileData(String path, Object data)
579    {
580  0 if (isJS && data != null)
581    {
582  0 jsutil.cachePathData(path, data);
583    }
584    }
585   
 
586  0 toggle public static void cacheFileData(File file)
587    {
588  0 if (isJS)
589    {
590  0 byte[] data = Platform.getFileBytes(file);
591    {
592  0 if (data != null)
593    {
594  0 cacheFileData(file.toString(), data);
595    }
596    }
597    }
598    }
599   
 
600  1567 toggle public static byte[] getFileBytes(File f)
601    {
602  1567 return (isJS && f != null ? jsutil.getBytes(f) : null);
603    }
604   
 
605  0 toggle public static byte[] getFileAsBytes(String fileStr)
606    {
607  0 if (isJS && fileStr != null)
608    {
609  0 byte[] bytes = (byte[]) jsutil.getFile(fileStr, false);
610  0 cacheFileData(fileStr, bytes);
611  0 return bytes;
612    }
613  0 return null;
614    }
615   
 
616  0 toggle public static String getFileAsString(String url)
617    {
618  0 if (isJS && url != null)
619    {
620  0 String ret = (String) jsutil.getFile(url, true);
621  0 cacheFileData(url, ret);
622  0 return ret;
623    }
624  0 return null;
625    }
626   
 
627  3 toggle public static boolean setFileBytes(File f, String urlstring)
628    {
629  3 if (isJS && f != null && urlstring != null)
630    {
631  0 @SuppressWarnings("unused")
632    byte[] bytes = getFileAsBytes(urlstring);
633  0 jsutil.setFileBytes(f, bytes);
634  0 return true;
635    }
636  3 return false;
637    }
638   
 
639  4 toggle public static void addJ2SBinaryType(String ext)
640    {
641  4 if (isJS)
642    {
643  0 jsutil.addBinaryFileType(ext);
644    }
645    }
646   
647    /**
648    * Encode the URI using JavaScript encodeURIComponent
649    *
650    * @param value
651    * @return encoded value
652    */
 
653  0 toggle public static String encodeURI(String value)
654    {
655    /**
656    * @j2sNative value = encodeURIComponent(value);
657    */
658  0 return value;
659    }
660   
661    /**
662    * Open the URL using a simple window call if this is JavaScript
663    *
664    * @param url
665    * @return true if window has been opened
666    */
 
667  0 toggle public static boolean openURL(String url) throws IOException
668    {
669  0 if (!isJS())
670    {
671  0 return false;
672    }
673    /**
674    * @j2sNative
675    *
676    *
677    * window.open(url);
678    */
679  0 return true;
680    }
681   
 
682  0 toggle public static String getUniqueAppletID()
683    {
684  0 return (isJS ? (String) jsutil.getAppletAttribute("_uniqueId") : null);
685   
686    }
687   
688    /**
689    * Read the Info block for this applet.
690    *
691    * @param prefix
692    * "jalview_"
693    * @param p
694    * @return unique id for this applet
695    */
 
696  49 toggle public static void readInfoProperties(String prefix, Properties p)
697    {
698    // TODO - BAD MERGE ?
699  49 if (isJS)
700    {
701  0 String id = getUniqueAppletID();
702   
703  0 String key = "";
704  0 String value = "";
705  0 @SuppressWarnings("unused")
706    Object info = jsutil.getAppletAttribute("__Info");
707    /**
708    * @j2sNative for (key in info) { value = info[key];
709    */
710   
711  0 if (key.indexOf(prefix) == 0)
712    {
713  0 jalview.bin.Console.outPrintln("Platform id=" + id + " reading Info." + key
714    + " = " + value);
715  0 p.put(key, value);
716    }
717    }
718   
719    /**
720    * @j2sNative
721    *
722    *
723    * } }
724    */
725    }
726   
 
727  0 toggle public static void setAjaxJSON(URL url)
728    {
729  0 if (isJS())
730    {
731  0 JSON.setAjax(url);
732    }
733    }
734   
 
735  89 toggle public static Object parseJSON(InputStream response)
736    throws IOException, ParseException
737    {
738  89 if (isJS())
739    {
740  0 return JSON.parse(response);
741    }
742   
743  89 BufferedReader br = null;
744  89 try
745    {
746  89 br = new BufferedReader(new InputStreamReader(response, "UTF-8"));
747  89 return new JSONParser().parse(br);
748    } finally
749    {
750  89 if (br != null)
751    {
752  89 try
753    {
754  89 br.close();
755    } catch (IOException e)
756    {
757    // ignore
758    }
759    }
760    }
761    }
762   
 
763  140 toggle public static Object parseJSON(String json) throws ParseException
764    {
765  140 return (isJS() ? JSON.parse(json) : new JSONParser().parse(json));
766    }
767   
 
768  54 toggle public static Object parseJSON(Reader r)
769    throws IOException, ParseException
770    {
771  54 if (r == null)
772    {
773  0 return null;
774    }
775   
776  54 if (!isJS())
777    {
778  54 return new JSONParser().parse(r);
779    }
780    // Using a file reader is not currently supported in SwingJS JavaScript
781   
782  0 if (r instanceof FileReader)
783    {
784  0 throw new IOException(
785    "StringJS does not support FileReader parsing for JSON -- but it could...");
786    }
787  0 return JSON.parse(r);
788   
789    }
790   
791    /**
792    * Dump the input stream to an output file.
793    *
794    * @param is
795    * @param outFile
796    * @throws IOException
797    * if the file cannot be created or there is a problem reading the
798    * input stream.
799    */
 
800  0 toggle public static void streamToFile(InputStream is, File outFile)
801    throws IOException
802    {
803  0 if (isJS)
804    {
805  0 jsutil.setFileBytes(outFile, is);
806  0 return;
807    }
808  0 FileOutputStream fio = new FileOutputStream(outFile);
809  0 try
810    {
811  0 byte[] bb = new byte[32 * 1024];
812  0 int l;
813  0 while ((l = is.read(bb)) > 0)
814    {
815  0 fio.write(bb, 0, l);
816    }
817    } finally
818    {
819  0 fio.close();
820    }
821    }
822   
823    /**
824    * Add a known domain that implements access-control-allow-origin:*
825    *
826    * These should be reviewed periodically.
827    *
828    * @param domain
829    * for a service that is not allowing ajax
830    *
831    * @author hansonr@stolaf.edu
832    *
833    */
 
834  249 toggle public static void addJ2SDirectDatabaseCall(String domain)
835    {
836   
837  249 if (isJS)
838    {
839  0 jsutil.addDirectDatabaseCall(domain);
840  0 jalview.bin.Console.outPrintln(
841    "Platform adding known access-control-allow-origin * for domain "
842    + domain);
843    /**
844    * @j2sNative
845    *
846    * J2S.addDirectDatabaseCall(domain);
847    */
848    }
849   
850    }
851   
852    /**
853    * Allow for URL-line command arguments. Untested.
854    *
855    */
 
856  55 toggle public static void getURLCommandArguments()
857    {
858  55 try {
859    /**
860    * Retrieve the first query field as command arguments to Jalview. Include
861    * only if prior to "?j2s" or "&j2s" or "#". Assign the applet's
862    * __Info.args element to this value.
863    *
864    * @j2sNative var a =
865    * decodeURI((document.location.href.replace("&","?").split("?j2s")[0]
866    * + "?").split("?")[1].split("#")[0]); a && (jalview.bin.Console.outPrintln("URL arguments detected were "+a)) && (J2S.thisApplet.__Info.urlargs = a.split(" "));
867    * (!J2S.thisApplet.__Info.args || J2S.thisApplet.__Info.args == "" || J2S.thisApplet.__Info.args == "??") && (J2S.thisApplet.__Info.args = a) && (jalview.bin.Console.outPrintln("URL arguments were passed to J2S main."));
868    */
869    } catch (Throwable t)
870    {
871    }
872    }
873   
874    /**
875    * A (case sensitive) file path comparator that ignores the difference between
876    * / and \
877    *
878    * @param path1
879    * @param path2
880    * @return
881    */
 
882  19 toggle public static boolean pathEquals(String path1, String path2)
883    {
884  19 if (path1 == null)
885    {
886  2 return path2 == null;
887    }
888  17 if (path2 == null)
889    {
890  1 return false;
891    }
892  16 String p1 = path1.replace('\\', '/');
893  16 String p2 = path2.replace('\\', '/');
894  16 return p1.equals(p2);
895    }
896   
897    /**
898    * If started on command line using launch script, return the console width
899    */
 
900  0 toggle public static int consoleWidth()
901    {
902  0 return CONSOLEWIDTH;
903    }
904    ///////////// JAL-3253 Applet additions //////////////
905   
906    /**
907    * Retrieve the object's embedded size from a div's style on a page if
908    * embedded in SwingJS.
909    *
910    * @param frame
911    * JFrame or JInternalFrame
912    * @param defaultWidth
913    * use -1 to return null (no default size)
914    * @param defaultHeight
915    * @return the embedded dimensions or null (no default size or not embedded)
916    */
 
917  928 toggle public static Dimension getDimIfEmbedded(Component frame,
918    int defaultWidth, int defaultHeight)
919    {
920  928 Dimension d = null;
921  928 if (isJS)
922    {
923  0 d = (Dimension) getEmbeddedAttribute(frame, "dim");
924    }
925  928 return (d == null && defaultWidth >= 0
926    ? new Dimension(defaultWidth, defaultHeight)
927    : d);
928   
929    }
930   
 
931  85 toggle public static Regex newRegex(String regex)
932    {
933  85 return newRegex(regex, null);
934    }
935   
 
936  1691 toggle public static Regex newRegex(String searchString, String replaceString)
937    {
938  1691 ensureRegex();
939  1691 return (replaceString == null ? new Regex(searchString)
940    : new Regex(searchString, replaceString));
941    }
942   
 
943  378 toggle public static Regex newRegexPerl(String code)
944    {
945  378 ensureRegex();
946  378 return Regex.perlCode(code);
947    }
948   
949    /**
950    * Initialize Java debug logging. A representative sample -- adapt as desired.
951    */
 
952  0 toggle public static void startJavaLogging()
953    {
954    /**
955    * @j2sIgnore
956    */
957    {
958  0 logClass("java.awt.EventDispatchThread", "java.awt.EventQueue",
959    "java.awt.Component", "java.awt.focus.Component",
960    "java.awt.event.Component",
961    "java.awt.focus.DefaultKeyboardFocusManager");
962    }
963    }
964   
965    /**
966    * Initiate Java logging for a given class. Only for Java, not JavaScript;
967    * Allows debugging of complex event processing.
968    *
969    * @param className
970    */
 
971  0 toggle public static void logClass(String... classNames)
972    {
973    /**
974    * @j2sIgnore
975    *
976    *
977    */
978    {
979  0 Logger rootLogger = Logger.getLogger("");
980  0 rootLogger.setLevel(Level.ALL);
981  0 ConsoleHandler consoleHandler = new ConsoleHandler();
982  0 consoleHandler.setLevel(Level.ALL);
983  0 for (int i = classNames.length; --i >= 0;)
984    {
985  0 Logger logger = Logger.getLogger(classNames[i]);
986  0 logger.setLevel(Level.ALL);
987  0 logger.addHandler(consoleHandler);
988    }
989    }
990    }
991   
992    /**
993    * load a resource -- probably a core file -- if and only if a particular
994    * class has not been instantialized. We use a String here because if we used
995    * a .class object, that reference itself would simply load the class, and we
996    * want the core package to include that as well.
997    *
998    * @param resourcePath
999    * @param className
1000    */
 
1001  5 toggle public static void loadStaticResource(String resourcePath,
1002    String className)
1003    {
1004  5 if (isJS)
1005    {
1006  0 jsutil.loadResourceIfClassUnknown(resourcePath, className);
1007    }
1008    }
1009   
 
1010  2092 toggle public static void ensureRegex()
1011    {
1012  2092 if (isJS)
1013    {
1014  0 loadStaticResource("core/core_stevesoft.z.js",
1015    "com.stevesoft.pat.Regex");
1016    }
1017    }
1018   
1019    /**
1020    * Set the "app" property of the HTML5 applet object, for example,
1021    * "testApplet.app", to point to the Jalview instance. This will be the object
1022    * that page developers use that is similar to the original Java applet object
1023    * that was accessed via LiveConnect.
1024    *
1025    * @param j
1026    */
 
1027  0 toggle public static void setAppClass(Object j)
1028    {
1029  0 if (isJS)
1030    {
1031  0 jsutil.setAppClass(j);
1032    }
1033    }
1034   
1035    /**
1036    *
1037    * If this frame is embedded in a web page, return a known type.
1038    *
1039    * @param frame
1040    * a JFrame or JInternalFrame
1041    * @param type
1042    * "name", "node", "init", "dim", or any DOM attribute, such as "id"
1043    * @return null if frame is not embedded.
1044    */
 
1045  570 toggle public static Object getEmbeddedAttribute(Component frame, String type)
1046    {
1047  570 return (isJS ? jsutil.getEmbeddedAttribute(frame, type) : null);
1048    }
1049   
 
1050  0 toggle public static void stackTrace()
1051    {
1052  0 try
1053    {
1054  0 throw new NullPointerException();
1055    } catch (Exception e)
1056    {
1057  0 e.printStackTrace();
1058    }
1059   
1060    }
1061   
 
1062  0 toggle public static URL getDocumentBase()
1063    {
1064  0 return (isJS ? jsutil.getDocumentBase() : null);
1065    }
1066   
 
1067  0 toggle public static URL getCodeBase()
1068    {
1069  0 return (isJS ? jsutil.getCodeBase() : null);
1070    }
1071   
 
1072  204 toggle public static String getUserPath(String subpath)
1073    {
1074  204 char sep = File.separatorChar;
1075  204 return System.getProperty("user.home") + sep
1076    + subpath.replace('/', sep);
1077    }
1078   
1079    /**
1080    * This method enables checking if a cached file has exceeded a certain
1081    * threshold(in days)
1082    *
1083    * @param file
1084    * the cached file
1085    * @param noOfDays
1086    * the threshold in days
1087    * @return
1088    */
 
1089  0 toggle public static boolean isFileOlderThanThreshold(File file, int noOfDays)
1090    {
1091  0 if (isJS())
1092    {
1093    // not meaningful in SwingJS -- this is a session-specific temp file. It
1094    // doesn't have a timestamp.
1095  0 return false;
1096    }
1097  0 Path filePath = file.toPath();
1098  0 BasicFileAttributes attr;
1099  0 int diffInDays = 0;
1100  0 try
1101    {
1102  0 attr = Files.readAttributes(filePath, BasicFileAttributes.class);
1103  0 diffInDays = (int) ((new Date().getTime()
1104    - attr.lastModifiedTime().toMillis())
1105    / (1000 * 60 * 60 * 24));
1106    // System.out.println("Diff in days : " + diffInDays);
1107    } catch (IOException e)
1108    {
1109  0 e.printStackTrace();
1110    }
1111  0 return noOfDays <= diffInDays;
1112    }
1113   
1114    /**
1115    * Get the leading integer part of a string that begins with an integer.
1116    *
1117    * @param input
1118    * - the string input to process
1119    * @param failValue
1120    * - value returned if unsuccessful
1121    * @return
1122    */
 
1123  6 toggle public static int getLeadingIntegerValue(String input, int failValue)
1124    {
1125  6 if (input == null)
1126    {
1127  0 return failValue;
1128    }
1129  6 if (isJS)
1130    {
1131  0 int val = /** @j2sNative 1 ? parseInt(input) : */
1132    0;
1133  0 return (val == val + 0 ? val : failValue);
1134    }
1135    // JavaScript does not support Regex ? lookahead
1136  6 String[] parts = input.split("(?=\\D)(?<=\\d)");
1137  6 if (parts != null && parts.length > 0 && parts[0].matches("[0-9]+"))
1138    {
1139  2 return Integer.valueOf(parts[0]);
1140    }
1141  4 return failValue;
1142    }
1143   
 
1144  0 toggle public static Map<String, Object> getAppletInfoAsMap()
1145    {
1146  0 return (isJS ? jsutil.getAppletInfoAsMap() : null);
1147    }
1148   
1149    /**
1150    * Get the SwingJS applet ID and combine that with the frameType
1151    *
1152    * @param frameType
1153    * "alignment", "desktop", etc., or null
1154    * @return
1155    */
 
1156  615 toggle public static String getAppID(String frameType)
1157    {
1158   
1159  615 String id = Jalview.getInstance().j2sAppletID;
1160  615 if (id == null)
1161    {
1162  54 Jalview.getInstance().j2sAppletID = id = (isJS
1163    ? (String) jsutil.getAppletAttribute("_id")
1164    : "jalview");
1165    }
1166  615 return id + (frameType == null ? "" : "-" + frameType);
1167    }
1168   
1169    /**
1170    * Option to avoid unnecessary seeking of nonexistent resources in JavaScript.
1171    * Works in Java as well.
1172    *
1173    * @param loc
1174    * @return
1175    */
 
1176  54 toggle public static Locale getLocaleOrNone(Locale loc)
1177    {
1178  54 return (isJS && loc.getLanguage() == "en" ? new Locale("") : loc);
1179    }
1180   
1181    /**
1182    * From UrlDownloadClient; trivial in JavaScript; painful in Java.
1183    *
1184    * @param urlstring
1185    * @param outfile
1186    * @throws IOException
1187    */
 
1188  14 toggle public static void download(String urlstring, String outfile)
1189    throws IOException
1190    {
1191  14 Path temp = null;
1192  14 try (InputStream is = new URL(urlstring).openStream())
1193    {
1194  10 if (isJS)
1195    { // so much easier!
1196  0 streamToFile(is, new File(outfile));
1197  0 return;
1198    }
1199  10 temp = Files.createTempFile(".jalview_", ".tmp");
1200  10 try (FileOutputStream fos = new FileOutputStream(temp.toString());
1201  10 ReadableByteChannel rbc = Channels.newChannel(is))
1202    {
1203  10 fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
1204    // copy tempfile to outfile once our download completes
1205    // incase something goes wrong
1206  10 Files.copy(temp, Paths.get(outfile),
1207    StandardCopyOption.REPLACE_EXISTING);
1208    }
1209    } catch (IOException e)
1210    {
1211  4 throw e;
1212    } finally
1213    {
1214  14 try
1215    {
1216  14 if (temp != null)
1217    {
1218  10 Files.deleteIfExists(temp);
1219    }
1220    } catch (IOException e)
1221    {
1222  0 System.out.println("Exception while deleting download temp file: "
1223    + e.getMessage());
1224    }
1225    }
1226    }
1227    }
1228