1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 (function () {
  27 
  28 // Check if java.desktop module is available and we're running in non-headless mode.
  29 // We access AWT via script to avoid direct dependency on java.desktop module.
  30 function isHeadless() {
  31     var GraphicsEnvironment = java.awt.GraphicsEnvironment;
  32     return Java.isType(GraphicsEnvironment)? GraphicsEnvironment.isHeadless() : true;
  33 }
  34 
  35 
  36 // Function that shows a JFileChooser dialog and returns the file name chosen (if chosen).
  37 // We access swing from script to avoid direct dependency on java.desktop module.
  38 function chooseFile() {
  39     var JFileChooser = javax.swing.JFileChooser;
  40     if (!Java.isType(JFileChooser)) {
  41         return null;
  42     }
  43 
  44     var ExtensionFilter = javax.swing.filechooser.FileNameExtensionFilter;
  45     function run() {
  46         var chooser = new JFileChooser();
  47         chooser.fileFilter = new ExtensionFilter('JavaScript Files', 'js');
  48         var retVal = chooser.showOpenDialog(null);
  49         return retVal == JFileChooser.APPROVE_OPTION ?
  50             chooser.selectedFile.absolutePath : null;
  51     }
  52 
  53     var FutureTask = java.util.concurrent.FutureTask;
  54     var fileChooserTask = new FutureTask(run);
  55     javax.swing.SwingUtilities.invokeLater(fileChooserTask);
  56 
  57     return fileChooserTask.get();
  58 }
  59 
  60 // Function that opens up the desktop browser application with the given URI.
  61 // We access AWT from script to avoid direct dependency on java.desktop module.
  62 function browse(uri) {
  63     var Desktop = java.awt.Desktop;
  64     if (Java.isType(Desktop)) {
  65         Desktop.desktop.browse(uri);
  66     }
  67 }
  68 
  69 function printDoc(list) {
  70     list.forEach(function(doc) {
  71         print();
  72         print(doc.signature());
  73         print();
  74         print(doc.javadoc());
  75     });
  76 }
  77 
  78 var JShell = null;
  79 var jshell = null;
  80 
  81 function javadoc(obj) {
  82     var str = String(obj);
  83     if (!JShell) {
  84         // first time - resolve JShell class
  85         JShell = Packages.jdk.jshell.JShell;
  86         // if JShell class is available, create an instance
  87         jshell = Java.isType(JShell)? JShell.create() : null;
  88     }
  89 
  90     if (!jshell) {
  91         // we don't have jshell. Just print the default!
  92         return print(str);
  93     }
  94 
  95     /*
  96      * A java method object's String representation looks something like this:
  97      *
  98      * For an overloaded method:
  99      *
 100      *   [jdk.dynalink.beans.OverloadedDynamicMethod
 101      *      String java.lang.System.getProperty(String,String)
 102      *      String java.lang.System.getProperty(String)
 103      *    ]
 104      *
 105      * For a non-overloaded method:
 106      *
 107      *  [jdk.dynalink.beans.SimpleDynamicMethod void java.lang.System.exit(int)]
 108      *
 109      * jshell expects "java.lang.System.getProperty(" or "java.lang.System.exit("
 110      * to retrieve the javadoc comment(s) for the method.
 111      */
 112     var javaCode = str.split(" ")[2]; // stuff after second whitespace char
 113     javaCode = javaCode.substring(0, javaCode.indexOf('(') + 1); // strip argument types
 114 
 115     try {
 116         var analysis = jshell.sourceCodeAnalysis();
 117         var docList = analysis.documentation(javaCode, javaCode.length, true);
 118         if (!docList.isEmpty()) {
 119             return printDoc(docList);
 120         }
 121 
 122         /*
 123          * May be the method is a Java instance method. In such a case, jshell expects
 124          * a valid starting portion of an instance method call expression. We cast null
 125          * to Java object and call method on it. i.e., We pass something like this:
 126          *
 127          *  "((java.io.PrintStream)null).println("
 128          */
 129         var javaType = javaCode.substring(0, javaCode.lastIndexOf('.'));
 130         javaCode = "((" + javaType + ")null)" + javaCode.substring(javaCode.lastIndexOf('.'));
 131         docList = analysis.documentation(javaCode, javaCode.length, true);
 132         if (!docList.isEmpty()) {
 133             return printDoc(docList);
 134         }
 135     } catch (e) {
 136     }
 137     print(str);
 138 }
 139 
 140 return {
 141     isHeadless: isHeadless,
 142     chooseFile: chooseFile,
 143     browse: browse,
 144     javadoc: javadoc
 145 };
 146 
 147 })();