#210 Fixes Ensembl fetch problem (really)
[jalview.git] / src / jalview / util / Platform.java
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
23 import jalview.javascript.json.JSON;
24
25 import java.awt.Toolkit;
26 import java.awt.event.MouseEvent;
27 import java.io.BufferedReader;
28 import java.io.File;
29 import java.io.FileReader;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.InputStreamReader;
33 import java.io.Reader;
34 import java.net.URL;
35 import java.util.Properties;
36
37 import javax.swing.SwingUtilities;
38
39 import org.json.simple.parser.JSONParser;
40 import org.json.simple.parser.ParseException;
41
42 /**
43  * System platform information used by Applet and Application
44  * 
45  * @author Jim Procter
46  */
47 public class Platform
48 {
49
50   private static boolean isJS = /** @j2sNative true || */
51           false;
52
53   private static Boolean isNoJSMac = null, isNoJSWin = null, isMac = null,
54           isWin = null;
55
56   private static Boolean isHeadless = null;
57
58   /**
59    * added to group mouse events into Windows and nonWindows (mac, unix, linux)
60    * 
61    * @return
62    */
63   public static boolean isMac()
64   {
65     return (isMac == null
66             ? (isMac = (System.getProperty("os.name").indexOf("Mac") >= 0))
67             : isMac);
68   }
69
70   /**
71    * added to group mouse events into Windows and nonWindows (mac, unix, linux)
72    * 
73    * @return
74    */
75   public static boolean isWin()
76   {
77     return (isWin == null
78             ? (isWin = (System.getProperty("os.name").indexOf("Win") >= 0))
79             : isWin);
80   }
81
82   /**
83    * 
84    * @return true if HTML5 JavaScript
85    */
86   public static boolean isJS()
87   {
88     return isJS;
89   }
90
91   /**
92    * sorry folks - Macs really are different
93    * 
94    * BH: disabled for SwingJS -- will need to check key-press issues
95    * 
96    * @return true if we do things in a special way.
97    */
98   public static boolean isAMacAndNotJS()
99   {
100     return (isNoJSMac == null ? (isNoJSMac = !isJS && isMac()) : isNoJSMac);
101   }
102
103   /**
104    * Check if we are on a Microsoft plaform...
105    * 
106    * @return true if we have to cope with another platform variation
107    */
108   public static boolean isWindowsAndNotJS()
109   {
110     return (isNoJSWin == null ? (isNoJSWin = !isJS && isWin()) : isNoJSWin);
111   }
112
113   /**
114    * 
115    * @return true if we are running in non-interactive no UI mode
116    */
117   public static boolean isHeadless()
118   {
119     if (isHeadless == null)
120     {
121       isHeadless = "true".equals(System.getProperty("java.awt.headless"));
122     }
123     return isHeadless;
124   }
125
126   /**
127    * 
128    * @return nominal maximum command line length for this platform
129    */
130   public static int getMaxCommandLineLength()
131   {
132     // TODO: determine nominal limits for most platforms.
133     return 2046; // this is the max length for a windows NT system.
134   }
135
136   /**
137    * escape a string according to the local platform's escape character
138    * 
139    * @param file
140    * @return escaped file
141    */
142   public static String escapeString(String file)
143   {
144     StringBuffer f = new StringBuffer();
145     int p = 0, lastp = 0;
146     while ((p = file.indexOf('\\', lastp)) > -1)
147     {
148       f.append(file.subSequence(lastp, p));
149       f.append("\\\\");
150       lastp = p + 1;
151     }
152     f.append(file.substring(lastp));
153     return f.toString();
154   }
155
156   /**
157    * Answers true if the mouse event has Meta-down (Command key on Mac) or
158    * Ctrl-down (on other o/s). Note this answers _false_ if the Ctrl key is
159    * pressed instead of the Meta/Cmd key on Mac. To test for Ctrl-pressed on
160    * Mac, you can use e.isPopupTrigger().
161    * 
162    * @param e
163    * @return
164    */
165   public static boolean isControlDown(MouseEvent e)
166   {
167     return isControlDown(e, isMac());
168   }
169
170   /**
171    * Overloaded version of method (to allow unit testing)
172    * 
173    * @param e
174    * @param aMac
175    * @return
176    */
177   protected static boolean isControlDown(MouseEvent e, boolean aMac)
178   {
179     if (!aMac)
180     {
181       return e.isControlDown();
182     }
183     // answer false for right mouse button
184     // shortcut key will be META for a Mac
185     return !e.isPopupTrigger()
186             && (Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
187                     & e.getModifiers()) != 0;
188     // could we use e.isMetaDown() here?
189   }
190
191   // BH: I don't know about that previous method. Here is what SwingJS uses.
192   // Notice the distinction in mouse events. (BUTTON3_MASK == META)
193   //
194   // private static boolean isPopupTrigger(int id, int mods, boolean isWin) {
195   // boolean rt = ((mods & InputEvent.BUTTON3_MASK) != 0);
196   // if (isWin) {
197   // if (id != MouseEvent.MOUSE_RELEASED)
198   // return false;
199   ////
200   //// // Oddly, Windows returns InputEvent.META_DOWN_MASK on release, though
201   //// // BUTTON3_DOWN_MASK for pressed. So here we just accept both.
202   ////
203   //// actually, we can use XXX_MASK, not XXX_DOWN_MASK and avoid this issue,
204   // because
205   //// J2S adds the appropriate extended (0x3FC0) and simple (0x3F) modifiers.
206   ////
207   // return rt;
208   // } else {
209   // // mac, linux, unix
210   // if (id != MouseEvent.MOUSE_PRESSED)
211   // return false;
212   // boolean lt = ((mods & InputEvent.BUTTON1_MASK) != 0);
213   // boolean ctrl = ((mods & InputEvent.CTRL_MASK) != 0);
214   // return rt || (ctrl && lt);
215   // }
216   // }
217   //
218
219   /**
220    * Windows (not Mac, Linux, or Unix) and right button to test for the
221    * right-mouse pressed event in Windows that would have opened a menu or a
222    * Mac.
223    * 
224    * @param e
225    * @return
226    */
227   public static boolean isWinRightButton(MouseEvent e)
228   {
229     // was !isAMac(), but that is true also for Linux and Unix and JS,
230
231     return isWin() && SwingUtilities.isRightMouseButton(e);
232   }
233
234   /**
235    * Windows (not Mac, Linux, or Unix) and middle button -- for mouse wheeling
236    * without pressing the button.
237    * 
238    * @param e
239    * @return
240    */
241   public static boolean isWinMiddleButton(MouseEvent e)
242   {
243     // was !isAMac(), but that is true also for Linux and Unix and JS
244     return isWin() && SwingUtilities.isMiddleMouseButton(e);
245   }
246
247   public static boolean allowMnemonics()
248   {
249     return !isMac();
250   }
251
252   public final static int TIME_RESET = 0;
253
254   public final static int TIME_MARK = 1;
255
256   public static final int TIME_SET = 2;
257
258   public static final int TIME_GET = 3;
259
260   public static long time, mark, set, duration;
261
262   public static void timeCheck(String msg, int mode)
263   {
264     long t = System.currentTimeMillis();
265     switch (mode)
266     {
267     case TIME_RESET:
268       time = mark = t;
269       if (msg != null)
270       {
271         System.err.println("Platform: timer reset\t\t\t" + msg);
272       }
273       break;
274     case TIME_MARK:
275       if (set > 0)
276       {
277         duration += (t - set);
278       }
279       else
280       {
281         if (time == 0)
282         {
283           time = mark = t;
284         }
285         if (msg != null)
286         {
287           System.err.println("Platform: timer mark\t" + ((t - time) / 1000f)
288                   + "\t" + ((t - mark) / 1000f) + "\t" + msg);
289         }
290         mark = t;
291       }
292       break;
293     case TIME_SET:
294       set = t;
295       break;
296     case TIME_GET:
297       if (msg != null)
298       {
299         System.err.println("Platform: timer dur\t" + ((t - time) / 1000f)
300                 + "\t" + ((duration) / 1000f) + "\t" + msg);
301       }
302       set = 0;
303       break;
304     }
305   }
306
307   public static void cacheFileData(String path, byte[] data)
308   {
309     if (!isJS())
310     {
311       return;
312     }
313     /**
314      * @j2sNative
315      * 
316      *            swingjs.JSUtil.cacheFileData$S$O(path, data);
317      * 
318      */
319   }
320
321   public static byte[] getFileBytes(File f)
322   {
323     return /** @j2sNative f && f._bytes || */
324     null;
325   }
326
327   public static byte[] getFileAsBytes(String fileStr)
328   {
329     // BH 2018 hack for no support for access-origin
330     return /** @j2sNative swingjs.JSUtil.getFileAsBytes$O(fileStr) || */
331     null;
332   }
333
334   public static String getFileAsString(String data)
335   {
336     return /** @j2sNative swingjs.JSUtil.getFileAsString$S(data) || */
337     null;
338   }
339
340   public static boolean setFileBytes(File f, String urlstring)
341   {
342     if (!isJS())
343     {
344       return false;
345     }
346     @SuppressWarnings("unused")
347     byte[] bytes = getFileAsBytes(urlstring);
348     /**
349      * @j2sNative f._bytes = bytes;
350      */
351     return true;
352   }
353
354   public static void addJ2SBinaryType(String ext)
355   {
356     /**
357      * @j2sNative
358      * 
359      *            J2S._binaryTypes.push("." + ext + "?");
360      * 
361      */
362   }
363
364   /**
365    * Encode the URI using JavaScript encodeURIComponent
366    * 
367    * @param value
368    * @return encoded value
369    */
370   public static String encodeURI(String value)
371   {
372     /**
373      * @j2sNative value = encodeURIComponent(value);
374      */
375     return value;
376   }
377
378   /**
379    * Open the URL using a simple window call if this is JavaScript
380    * 
381    * @param url
382    * @return true if window has been opened
383    */
384   public static boolean openURL(String url)
385   {
386     if (!isJS())
387     {
388       return false;
389     }
390     /**
391      * @j2sNative
392      * 
393      * 
394      *            window.open(url);
395      */
396     return true;
397   }
398
399   public static String getUniqueAppletID()
400   {
401     @SuppressWarnings("unused")
402     ThreadGroup g = Thread.currentThread().getThreadGroup();
403     /**
404      * @j2sNative return g.html5Applet._uniqueId;
405      *
406      */
407     return null;
408
409   }
410
411   /**
412    * Read the Info block for this applet.
413    * 
414    * @param prefix
415    *          "jalview_"
416    * @param p
417    * @return unique id for this applet
418    */
419   public static void readInfoProperties(String prefix, Properties p)
420   {
421     if (!isJS())
422     {
423       return;
424     }
425     @SuppressWarnings("unused")
426     ThreadGroup g = Thread.currentThread().getThreadGroup();
427     String id = getUniqueAppletID();
428     String key = "", value = "";
429     /**
430      * @j2sNative var info = g.html5Applet.__Info || {}; for (var key in info) {
431      *            if (key.indexOf(prefix) == 0) { value = "" + info[key];
432      */
433
434     System.out.println(
435             "Platform id=" + id + " reading Info." + key + " = " + value);
436     p.put(id + "_" + key, value);
437
438     /**
439      * @j2sNative
440      * 
441      * 
442      *            } }
443      */
444   }
445
446   public static void setAjaxJSON(URL url)
447   {
448     if (isJS())
449     {
450       JSON.setAjax(url);
451     }
452   }
453
454   public static Object parseJSON(InputStream response)
455           throws IOException, ParseException
456   {
457     if (isJS())
458     {
459       return JSON.parse(response);
460     }
461
462     BufferedReader br = null;
463     try
464     {
465       br = new BufferedReader(new InputStreamReader(response, "UTF-8"));
466       return parseJSON(br);
467     } finally
468     {
469       if (br != null)
470       {
471         try
472         {
473           br.close();
474         } catch (IOException e)
475         {
476           // ignore
477         }
478       }
479     }
480   }
481
482   public static Object parseJSON(String json) throws ParseException
483   {
484     return (isJS() ? JSON.parse(json)
485             : new JSONParser().parse(json));
486   }
487
488   public static Object parseJSON(Reader r)
489           throws IOException, ParseException
490   {
491     if (r == null)
492     {
493       return null;
494     }
495
496     if (!isJS())
497     {
498       return new JSONParser().parse(r);
499     }
500     // Using a file reader is not currently supported in SwingJS JavaScript
501
502     if (r instanceof FileReader)
503     {
504       throw new IOException(
505               "StringJS does not support FileReader parsing for JSON -- but it could...");
506     }
507     return JSON.parse(r);
508
509   }
510
511 }