content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  package org.apache.chemistry.opencmis.workbench;
  
  import java.awt.Component;
  import java.awt.Cursor;
  import java.awt.Desktop;
  import java.awt.Desktop.Action;
  import java.awt.Toolkit;
  import java.awt.datatransfer.Clipboard;
  import java.awt.datatransfer.DataFlavor;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.event.KeyEvent;
  import java.awt.event.MouseAdapter;
  import java.awt.event.MouseEvent;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.io.PrintWriter;
  import java.io.Writer;
  import java.net.URI;
  import java.util.Collections;
  import java.util.List;
  
  import javax.script.ScriptEngine;
  import javax.script.ScriptEngineManager;
  import javax.swing.JFrame;
  import javax.swing.JMenu;
  import javax.swing.JMenuBar;
  import javax.swing.JMenuItem;
  import javax.swing.JPopupMenu;
  import javax.swing.text.AttributeSet;
  import javax.swing.text.BadLocationException;
  import javax.swing.text.DefaultEditorKit;
  
  import org.apache.chemistry.opencmis.client.api.Session;
  import org.apache.chemistry.opencmis.commons.SessionParameter;
  import org.apache.chemistry.opencmis.commons.impl.IOUtils;
  import org.apache.chemistry.opencmis.workbench.ClientHelper.FileEntry;
  import org.apache.chemistry.opencmis.workbench.model.ClientModel;
  import org.apache.chemistry.opencmis.workbench.model.ClientSession;
  
  import groovy.lang.Binding;
  import groovy.ui.Console;
  import groovy.util.GroovyScriptEngine;
  
  public class ConsoleHelper {
  
      private static final String SYSPROP_SCRIPTS = ClientSession.WORKBENCH_PREFIX + "scripts";
      private static final String SYSPROP_SNIPPETS = ClientSession.WORKBENCH_PREFIX + "snippets";
  
      private static final String GROOVY_SCRIPT_FOLDER = "/scripts/";
      private static final String GROOVY_SCRIPT_LIBRARY = "script-library.properties";
      private static final String GROOVY_SNIPPET_FOLDER = "/snippets/";
      private static final String GROOVY_SNIPPET_LIBRARY = "snippet-library.properties";
  
      /**
       * Open a Groovy console and load the script from the provided file.
       */
      public static Console openConsole(final Component parent, final ClientModel model, final URI file) {
          return openConsole(parent, model, file, null);
      }
  
      /**
       * Open a Groovy console and use the provided.
       */
      public static Console openConsole(final Component parent, final ClientModel model, final String soureCode) {
          return openConsole(parent, model, null, soureCode);
      }
  
      private static Console openConsole(final Component parent, final ClientModel model, final URI file,
              final String soureCode) {
          try {
              parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
  
              final Session groovySession = model.getClientSession().getSession();
              final String user = model.getClientSession().getSessionParameters().get(SessionParameter.USER);
              final String title = "GroovyConsole - Repsository: " + groovySession.getRepositoryInfo().getId();
  
              final Console console = new Console(parent.getClass().getClassLoader()) {
                  @Override
                  public void updateTitle() {
                      JFrame frame = (JFrame) getFrame();
  
                      if (getScriptFile() != null) {
                          frame.setTitle(((File) getScriptFile()).getName() + (getDirty() ? " * " : "") + " - " + title);
                      } else {
                          frame.setTitle(title);
                      }
                  }
              };
  
              console.setVariable("session", groovySession);
              console.setVariable("binding", groovySession.getBinding());
  
              console.run();
  
              // add menu
              JMenuBar consoleMenuBar = console.getFrame().getRootPane().getJMenuBar();
  
              JMenu cmisMenu = new JMenu("CMIS");
              cmisMenu.setMnemonic(KeyEvent.VK_M);
              consoleMenuBar.add(cmisMenu);
  
              addConsoleMenu(cmisMenu, "CMIS 1.0 Specification",
                      new URI("https://docs.oasis-open.org/cmis/CMIS/v1.0/os/cmis-spec-v1.0.html"));
              addConsoleMenu(cmisMenu, "CMIS 1.1 Specification",
                      new URI("https://docs.oasis-open.org/cmis/CMIS/v1.1/CMIS-v1.1.html"));
              cmisMenu.addSeparator();
              addConsoleMenu(cmisMenu, "OpenCMIS Documentation",
                      new URI("https://chemistry.apache.org/java/opencmis.html"));
              addConsoleMenu(cmisMenu, "OpenCMIS Code Samples",
                      new URI("https://chemistry.apache.org/docs/cmis-samples/"));
              addConsoleMenu(cmisMenu, "OpenCMIS Client API JavaDoc",
                      new URI("https://chemistry.apache.org/java/javadoc/"));
              cmisMenu.addSeparator();
              JMenuItem menuItem = new JMenuItem("CMIS Session Details");
              menuItem.addActionListener(new ActionListener() {
                  @Override
                  public void actionPerformed(ActionEvent e) {
                      AttributeSet style = console.getOutputStyle();
                      console.clearOutput();
                      console.appendOutputNl("Session ID:      " + groovySession.getBinding().getSessionId(), style);
                      console.appendOutputNl("Repository ID:   " + groovySession.getRepositoryInfo().getId(), style);
                      console.appendOutputNl("Repository name: " + groovySession.getRepositoryInfo().getName(), style);
                      console.appendOutputNl("Binding:         " + groovySession.getBinding().getBindingType(), style);
                      console.appendOutputNl("User:            " + user, style);
                  }
              });
              cmisMenu.add(menuItem);
  
              JMenu snippetsMenu = new JMenu("Snippets");
              snippetsMenu.setMnemonic(KeyEvent.VK_N);
              consoleMenuBar.add(snippetsMenu);
  
              for (FileEntry entry : readSnippetLibrary()) {
                  String snippet = ClientHelper.readFileAndRemoveHeader(entry.getFile());
  
                  JMenuItem snippetItem = new JMenuItem(entry.getName());
                  snippetItem.addActionListener(new ConsoleInsertActionListener(console, snippet));
                  snippetsMenu.add(snippetItem);
              }
  
              // add popup menu
  
              final JPopupMenu popup = new JPopupMenu();
  
              final JMenuItem cutItem = new JMenuItem(new DefaultEditorKit.CutAction());
              cutItem.setText("Cut");
              popup.add(cutItem);
  
              final JMenuItem copyItem = new JMenuItem(new DefaultEditorKit.CopyAction());
              copyItem.setText("Copy");
              popup.add(copyItem);
  
              final JMenuItem pasteItem = new JMenuItem(new DefaultEditorKit.PasteAction());
              pasteItem.setText("Paste");
              popup.add(pasteItem);
  
              console.getInputArea().addMouseListener(new MouseAdapter() {
                  @Override
                  public void mouseClicked(MouseEvent e) {
                  }
  
                  @Override
                  public void mousePressed(MouseEvent e) {
                      maybeShowPopup(e);
                  }
  
                  @Override
                  public void mouseReleased(MouseEvent e) {
                      maybeShowPopup(e);
                  }
  
                  private void maybeShowPopup(MouseEvent e) {
                      if (e.isPopupTrigger()) {
                          if (console.getInputArea().getSelectedText() != null) {
                              cutItem.setEnabled(true);
                              copyItem.setEnabled(true);
  
                          } else {
                              cutItem.setEnabled(false);
                              copyItem.setEnabled(false);
                          }
  
                          Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
                          pasteItem.setEnabled(clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor));
  
                          popup.show(e.getComponent(), e.getX(), e.getY());
                      }
                  }
              });
  
              // read source code
              if (file != null) {
                  console.getInputArea().setText(ClientHelper.readFileAndRemoveHeader(file));
              } else if (soureCode != null) {
                  console.getInputArea().setText(soureCode);
              }
  
              return console;
          } catch (Exception ex) {
              ClientHelper.showError(null, ex);
              return null;
          } finally {
              parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
          }
      }
  
      private static void addConsoleMenu(JMenu menu, String title, final URI url) {
          if (!Desktop.isDesktopSupported() || !Desktop.getDesktop().isSupported(Action.BROWSE)) {
              return;
          }
  
          JMenuItem menuItem = new JMenuItem(title);
          menuItem.addActionListener(new ActionListener() {
              @Override
              public void actionPerformed(ActionEvent e) {
                  try {
                      Desktop.getDesktop().browse(url);
                  } catch (IOException ex) {
                      ClientHelper.showError(null, ex);
                  }
              }
          });
  
          menu.add(menuItem);
      }
  
      public static List<FileEntry> readScriptLibrary() {
          URI propFile = null;
  
          String externalScripts = System.getProperty(SYSPROP_SCRIPTS);
          if (externalScripts == null) {
              propFile = ClientHelper.getClasspathURI(GROOVY_SCRIPT_FOLDER + GROOVY_SCRIPT_LIBRARY);
          } else {
              propFile = (new File(externalScripts)).toURI();
          }
  
          List<FileEntry> result = ClientHelper.readFileProperties(propFile);
  
          if (result == null || result.isEmpty()) {
              result = Collections.singletonList(new FileEntry("Groovy Console", null));
          }
  
          return result;
      }
  
      public static List<FileEntry> readSnippetLibrary() {
          URI propFile = null;
  
          String externalSnippets = System.getProperty(SYSPROP_SNIPPETS);
          if (externalSnippets == null) {
              propFile = ClientHelper.getClasspathURI(GROOVY_SNIPPET_FOLDER + GROOVY_SNIPPET_LIBRARY);
          } else {
              propFile = (new File(externalSnippets)).toURI();
          }
  
          List<FileEntry> result = ClientHelper.readFileProperties(propFile);
  
          if (result == null) {
              result = Collections.emptyList();
          }
  
          return result;
      }
  
      public static void runGroovyScript(final Component parent, final ClientModel model, final File file,
              final Writer out) {
          try {
              parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
  
              String[] roots = new String[] { file.getParentFile().getAbsolutePath() };
              GroovyScriptEngine gse = new GroovyScriptEngine(roots, parent.getClass().getClassLoader());
              Binding binding = new Binding();
              binding.setVariable("session", model.getClientSession().getSession());
              binding.setVariable("binding", model.getClientSession().getSession().getBinding());
              binding.setVariable("out", out);
              gse.run(file.getName(), binding);
          } catch (Exception ex) {
              ClientHelper.showError(null, ex);
          } finally {
              parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
          }
      }
  
      public static void runJSR223Script(final Component parent, final ClientModel model, final File file,
              final String ext, final Writer out) {
          InputStreamReader reader = null;
          try {
              parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
  
              reader = new InputStreamReader(new FileInputStream(file), IOUtils.UTF8);
  
              ScriptEngineManager mgr = new ScriptEngineManager();
              ScriptEngine engine = mgr.getEngineByExtension(ext);
              engine.getContext().setWriter(out);
              engine.getContext().setErrorWriter(out);
              engine.put("session", model.getClientSession().getSession());
              engine.put("binding", model.getClientSession().getSession().getBinding());
              engine.put("out", new PrintWriter(out));
              engine.eval(reader);
          } catch (Exception ex) {
              ClientHelper.showError(null, ex);
          } finally {
              IOUtils.closeQuietly(reader);
              parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
          }
      }
  
      // ---
  
      private static class ConsoleInsertActionListener implements ActionListener {
          private final Console console;
          private final String snippet;
  
          public ConsoleInsertActionListener(Console console, String snippet) {
              this.console = console;
              this.snippet = snippet;
          }
  
          @Override
          public void actionPerformed(ActionEvent e) {
              try {
                  int start = console.getInputArea().getSelectionStart();
                  int end = console.getInputArea().getSelectionEnd();
                  if (end - start > 0) {
                      console.getInputArea().getDocument().remove(start, end - start);
                  }
                  console.getInputArea().getDocument().insertString(console.getInputArea().getCaretPosition(), snippet,
                          null);
              } catch (BadLocationException ex) {
                  ClientHelper.showError(console.getFrame().getRootPane(), ex);
              }
          }
      }
  }
  